Skip to content

Commit bf0aa8c

Browse files
committed
added ShortestRangeInKSortedLists question
1 parent 5956ea5 commit bf0aa8c

File tree

3 files changed

+205
-1
lines changed

3 files changed

+205
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Maven build script is provided.
6161
- RangeMerge (RangeMergeTest) - sorted array of ints. Encode/Archive/Zip numbers into ranges. Optimized solution
6262
- TrappingRainWater (TrappingRainWaterTest) - array of landscape. Calculate how many units of water will remain after the rain inside.
6363
- KClosestPoints (KClosestPointsTest) - find k closest 2d points to particular one
64-
64+
- ShortestRangeInKSortedLists (ShortestRangeInKSortedListsTest) - take one element from each list - make the range of taken numbers the shortest possible
6565
# Yandex tasks
6666
- Bencode (BencodeTest) - implementation of Bencode protocol for encoding/decoding data. Widely used in torrents. More info: https://en.wikipedia.org/wiki/Bencode
6767

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package iurii.job.interview.amazon;
2+
3+
import java.util.*;
4+
5+
/**
6+
* Problem statement in detail:
7+
* Given k sorted lists of integers of size n each,
8+
* find the smallest range that includes at least element from each of the k lists.
9+
* If more than one smallest ranges are found, print any one of them.
10+
* <p>
11+
* Problem is similar to {@link iurii.job.interview.facebook.MinIteratorOfSortedAscendingLists}
12+
* <p>
13+
* https://www.youtube.com/watch?v=zplklOy7ENo
14+
*/
15+
public class ShortestRangeInKSortedLists {
16+
17+
public Range findRange(List<List<Integer>> lists) {
18+
// checks if needed
19+
if (lists == null || lists.isEmpty()) {
20+
return null;
21+
}
22+
if (lists.stream().anyMatch(List::isEmpty)) {
23+
return null;
24+
}
25+
long totalTransitionSize = lists.stream().mapToLong(list -> list.size() - 1).sum();
26+
// pointers
27+
int listsCount = lists.size();
28+
List<Integer> pointers = new ArrayList<>(Collections.nCopies(listsCount, 0));
29+
// initial value - will be changed, because min > max
30+
Range result = new Range();
31+
result.min = Integer.MIN_VALUE;
32+
result.max = Integer.MAX_VALUE;
33+
// find
34+
while (totalTransitionSize-- > 0) { // check all transitions
35+
long min = Integer.MAX_VALUE;
36+
long max = Integer.MIN_VALUE;
37+
int minFromExisting = Integer.MAX_VALUE;
38+
int minPointer = -1;
39+
for (int i = 0; i < listsCount; i++) {
40+
int pointer = pointers.get(i);
41+
max = Math.max(lists.get(i).get(pointer), max);
42+
min = Math.min(lists.get(i).get(pointer), min);
43+
// find min from lists that have one more element
44+
if (lists.get(i).get(pointer) < minFromExisting && pointer + 1 < lists.get(i).size()) {
45+
minFromExisting = Math.min(lists.get(i).get(pointer), minFromExisting);
46+
minPointer = i;
47+
}
48+
}
49+
pointers.set(minPointer, pointers.get(minPointer) + 1);
50+
if (max - min < result.max - result.min) {
51+
result.min = min;
52+
result.max = max;
53+
}
54+
}
55+
return result;
56+
}
57+
58+
// long not to have overflow in case wide range of integers
59+
public static class Range {
60+
long min;
61+
long max;
62+
}
63+
64+
public Range findRangeWithPriorityQueue(List<List<Integer>> lists) {
65+
// checks if needed
66+
if (lists == null || lists.isEmpty()) {
67+
return null;
68+
}
69+
if (lists.stream().anyMatch(List::isEmpty)) {
70+
return null;
71+
}
72+
Range result = new Range();
73+
result.min = Integer.MIN_VALUE;
74+
result.max = Integer.MAX_VALUE;
75+
MixedListsIterator iterator = new MixedListsIterator(lists);
76+
while (iterator.hasNext()) {
77+
Range range = iterator.next();
78+
if (range.max - range.min < result.max - result.min) {
79+
result = range;
80+
}
81+
}
82+
return result;
83+
}
84+
85+
interface MinIteratorOfSortedAscendingLists<E> extends Iterator<E> {
86+
}
87+
88+
private class MixedListsIterator implements MinIteratorOfSortedAscendingLists<Range> {
89+
90+
private final List<List<Integer>> lists;
91+
private final PriorityQueue<ListIterator> minQueue;
92+
private final PriorityQueue<Integer> maxQueue;
93+
private long totalTransitionSize;
94+
95+
public MixedListsIterator(List<List<Integer>> lists) {
96+
this.lists = lists;
97+
minQueue = new PriorityQueue<>(lists.size());
98+
maxQueue = new PriorityQueue<>(lists.size(), Comparator.reverseOrder());
99+
for (List<Integer> list : lists) {
100+
minQueue.add(new ListIterator(list.iterator()));
101+
maxQueue.add(list.get(0));
102+
}
103+
totalTransitionSize = lists.stream().mapToLong(list -> list.size() - 1).sum();
104+
}
105+
106+
@Override
107+
public boolean hasNext() {
108+
return totalTransitionSize != 0;
109+
}
110+
111+
@Override
112+
public Range next() {
113+
if (hasNext()) {
114+
ListIterator listIterator = minQueue.poll();
115+
Range result = new Range();
116+
if (listIterator.hasNext()) {
117+
int min = listIterator.next();
118+
int max = maxQueue.peek();
119+
result.min = min;
120+
result.max = max;
121+
maxQueue.remove(min);
122+
minQueue.add(listIterator);
123+
if (listIterator.hasNext()) {
124+
maxQueue.add(listIterator.value);
125+
}
126+
}
127+
return result;
128+
}
129+
throw new NoSuchElementException();
130+
}
131+
}
132+
133+
public static class ListIterator implements Comparable<ListIterator>, Iterator<Integer> {
134+
private Iterator<Integer> iterator;
135+
private Integer value;
136+
137+
public ListIterator(Iterator<Integer> iterator) {
138+
this.iterator = iterator;
139+
if (iterator.hasNext()) {
140+
value = iterator.next();
141+
}
142+
}
143+
144+
@Override
145+
public int compareTo(ListIterator o) {
146+
if (iterator.hasNext() ^ o.iterator.hasNext()) {
147+
return Integer.compare(value, o.value);
148+
} else {
149+
return iterator.hasNext() ? -1 : 1;
150+
}
151+
}
152+
153+
@Override
154+
public boolean hasNext() {
155+
return value != null;
156+
}
157+
158+
@Override
159+
public Integer next() {
160+
int result = value;
161+
if (iterator.hasNext()) {
162+
value = iterator.next();
163+
} else {
164+
value = null;
165+
}
166+
return result;
167+
}
168+
}
169+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package iurii.job.interview.amazon;
2+
3+
import org.junit.Test;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
public class ShortestRangeInKSortedListsTest {
11+
12+
@Test
13+
public void test() {
14+
List<Integer> list1 = Arrays.asList(1, 4, 7);
15+
List<Integer> list2 = Arrays.asList(2, 5, 8);
16+
List<Integer> list3 = Arrays.asList(3, 4, 6);
17+
List<List<Integer>> lists = Arrays.asList(list1, list2, list3);
18+
ShortestRangeInKSortedLists shortestRangeInKSortedLists = new ShortestRangeInKSortedLists();
19+
ShortestRangeInKSortedLists.Range range = shortestRangeInKSortedLists.findRange(lists);
20+
assertThat(range.min).isEqualTo(4);
21+
assertThat(range.max).isEqualTo(5);
22+
}
23+
24+
@Test
25+
public void testWithPriorityQueue() {
26+
List<Integer> list1 = Arrays.asList(1, 4, 7);
27+
List<Integer> list2 = Arrays.asList(2, 5, 8);
28+
List<Integer> list3 = Arrays.asList(3, 4, 6);
29+
List<List<Integer>> lists = Arrays.asList(list1, list2, list3);
30+
ShortestRangeInKSortedLists shortestRangeInKSortedLists = new ShortestRangeInKSortedLists();
31+
ShortestRangeInKSortedLists.Range range = shortestRangeInKSortedLists.findRangeWithPriorityQueue(lists);
32+
assertThat(range.min).isEqualTo(4);
33+
assertThat(range.max).isEqualTo(5);
34+
}
35+
}

0 commit comments

Comments
 (0)