Searching two arrays for matches, no extra memory
前几天我接受了亚马逊的采访,他们问我的一个问题与以下问题有关。
给定2个整数数组,包含任意数量的元素(正负),查找出现在两个数组中的数字。
我可以很容易地用
面试官在我解释完
注:我已经在CareerCup上发布了这个问题,但是那里的每个人似乎都不知道我需要它来不使用额外的空间,而且它必须是
这是我在面试中使用的代码。它可以工作,但对于空间来说不是O(1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import java.util.*; public class ArrayFun { public static void main(String[] args) { int[] a = {1,2,3,4}; int[] b = {2,5,6,7,3,2,2,2,2,1,2,2,2,2}; ArrayList<Integer> matches = ArrayFun.findMatches(a,b); for (int i = 0;i<matches.size();++i) { System.out.println(matches.get(i)); } } public static ArrayList<Integer> findMatches(int[] a, int[] b) { HashMap<Integer,Integer> map = new HashMap<Integer,Integer>(); ArrayList<Integer> matches = new ArrayList<Integer>(); for (int i = 0;i<a.length;++i) { map.put(a[i],0); } for (int i = 0;i<b.length;++i) { if (map.get(b[i]) != null && map.get(b[i]) == 0) { map.put(b[i],1); matches.add(b[i]); } } return matches; } } |
此代码将返回
埃多克斯1〔6〕
编辑:同样,当我说没有额外的空间,和O(1),我有点互换使用它们。没有额外的空间,我的意思是小的占位符变量是可以的,但分配新的数组却不行。
在O(n)时间中,没有O(1)空间法来求两个未排序集的交集。
对于范围不受限制的数据类型,最小排序价格为o(n ln n)。
对于范围有限的数据类型,基数排序提供了在O(n ln n'n")时间内执行就地基数排序的能力,其中n是数据的大小,n'是可以表示的值的数目,n"与检查两个值是否在同一基数组中的开销有关。N"时间价格可以降低,作为O(ln n)空间价格的回报。
在32位整数的特殊情况下,n'是2^32,n"是1,因此这将折叠为o(n),并为数十亿记录集提供一个获胜的解决方案。
对于无限大的整数,n'和n"通过基数排除O(n)时间解。
关键是对两个数组进行适当的排序。我搜索了"就地基数排序",找到了就地基数排序。我相信这个问题是可以解决的,至少对于Java INT[]来说,通过应用这些思想来对每一个数组进行排序,一点一点,然后做明显的扫描。
顺便说一下,我认为问题代码中问题的正确输出是1、2、3。
下面是我的实现,基于对引用问题的回答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | public class ArrayMatch { public static void main(String[] args) { int[] a = { 4, 1, 2, 3, 4 }; int[] b = { 2, 5, 6, 7, 3, 2, 2, 2, 2, 1, 2, 2, 2, 2 }; System.out.print("Original problem"); printMatches(a, b); System.out.println(); int[] a1 = { 4, 1, -1234, 2, 3, 4, Integer.MIN_VALUE }; int[] b1 = { -1234, 2, 5, 6, 7, 3, 2, 2, 2, 2, 1, 2, 2, 2, 2 , Integer.MIN_VALUE, Integer.MAX_VALUE}; System.out.print("With negatives"); printMatches(a1, b1); System.out.println(); } // Print all matching elements between the two arrays. private static void printMatches(int[] a, int[] b) { if (a.length == 0 || b.length == 0) { return; } sort(a); sort(b); int i = 0; int j = 0; while (true) { while (a[i] < b[j]) { i++; if (i == a.length) { return; } } while (a[i] > b[j]) { j++; if (j == b.length) { return; } } if (a[i] == b[j]) { System.out.print("" + a[i]); do { i++; } while (i < a.length && a[i - 1] == a[i]); do { j++; } while (j < b.length && b[j - 1] == b[j]); } if (i == a.length || j == b.length) { return; } } } // In place radix sort. private static void sort(int[] in) { // Flip the sign bit to regularize the sort order flipBit(in, 31); sort(in, 0, in.length, 31); // Flip back the sign bit back to restore 2's complement flipBit(in, 31); } /** * Sort a subarray, elements start through end-1 of in, according to the * values in firstBit through 0. * * @param in * @param start * @param end * @param firstBit */ private static void sort(int[] in, int start, int end, int firstBit) { if (start == end) { return; } int mask = 1 << firstBit; int zeroCount = 0; for (int i = start; i < end; i++) { if ((in[i] & mask) == 0) { zeroCount++; } } int elements = end - start; int nextZeroIndex = start; int nextOneIndex = start + zeroCount; int split = nextOneIndex; if (zeroCount > 0 && zeroCount < elements) { while (nextZeroIndex < split) { if ((in[nextZeroIndex] & mask) != 0) { // Found a one bit in the zero area, look for its partner in the one // area while ((in[nextOneIndex] & mask) != 0) { nextOneIndex++; } int temp = in[nextZeroIndex]; in[nextZeroIndex] = in[nextOneIndex]; in[nextOneIndex] = temp; nextOneIndex++; } nextZeroIndex++; } } if (firstBit > 0) { sort(in, start, split, firstBit - 1); sort(in, split, end, firstBit - 1); } } private static void flipBit(int[] in, int bitNo) { int mask = 1 << bitNo; for (int i = 0; i < in.length; i++) { in[i] ^= mask; } } } |
一个可能的答案类似于
基本上,如果整数被保证在一个固定大小的窗口内(即所有的整数都是1-1000),那么你可以在固定的空间中通过增加每个index=的单元格来实现它,不管你的数字是什么。这与
我相信这是有可能的地方做与
基本思想是就地散列。通过使用
对于这个特定的问题,您需要执行3次就地散列。首先在每个单独的数组上删除重复项。然后,在表示组合数组的包装器上(如果索引小于数组1的长度,则索引到数组1,否则索引到数组2)报告重复项。