Algorithm 在N个排序数组中查找公共元素,无需额外空间
给定N个大小为N的数组,并且它们都已排序,如果它不允许您使用额外的空间,如何高效或以更少的时间复杂度查找它们的公共数据 例如:Algorithm 在N个排序数组中查找公共元素,无需额外空间,algorithm,data-structures,Algorithm,Data Structures,给定N个大小为N的数组,并且它们都已排序,如果它不允许您使用额外的空间,如何高效或以更少的时间复杂度查找它们的公共数据 例如: 1. 10 160 200 500 500 2. 4 150 160 170 500 3. 2 160 200 202 203 4. 3 150 155 160 300 5. 3 150 155 160 301 这是一个采访问题,我发现了一些类似的问题,但它们没有包括输入被排序或无法使用额外内存的额外条件 我想不出比O(n^2 lgn)复杂度更低的解决方案。
1. 10 160 200 500 500
2. 4 150 160 170 500
3. 2 160 200 202 203
4. 3 150 155 160 300
5. 3 150 155 160 301
这是一个采访问题,我发现了一些类似的问题,但它们没有包括输入被排序或无法使用额外内存的额外条件
我想不出比O(n^2 lgn)复杂度更低的解决方案。在这种情况下,我更倾向于使用最简单的解决方案,这会给我带来这种复杂性,即:
not_found_flag = false
for each element 'v' in row-1
for each row 'i' in the remaining set
perform binary search for 'v' in 'i'
if 'v' not found in row 'i'
not_found_flag = true
break
if not not_found_flag
print element 'v' as it is one of the common element
我们可以通过比较每行的最小值和最大值来改进这一点,并根据这一点来决定数字“num”是否可能介于该行的“min_num”和“max_num”之间
二进制搜索->O(日志n)
用于搜索n-1行中的1个数字:O(nlogn)
第一行中每个数字的二进制搜索:O(n2logn)
我选择了第一行,我们可以选择任何一行,如果在任何(N-1)行中没有找到所选行的元素,那么我们就没有真正的公共数据。这似乎可以在
O(N^2)
中完成;i、 例如,只需查看每个元素一次。注意,如果一个元素对所有数组都是公共的,那么它必须存在于其中任何一个数组中。为了便于说明(由于您使用了上面的for循环),我假设我们可以为每个数组保留一个索引,但稍后我将讨论如何解决这个问题
让我们通过A_N
调用数组A_1
,并使用从1开始的索引。伪代码:
# Initial index values set to first element of each array
for i = 1 to N:
x_i = 1
for x_1 = 1 to N:
val = A_1[x_1]
print_val = true
for i = 2 to N:
while A_i[x_i] < val:
x_i = x_i + 1
if A_i[x_i] != val:
print_val = false
if print_val:
print val
输出:
160
一个O(n^2)(Python)版本,它不使用额外的存储,但修改原始数组。允许存储公共元素而不打印它们:
data = [
[10, 160, 200, 500, 500],
[4, 150, 160, 170, 500],
[2, 160, 200, 202, 203],
[3, 150, 155, 160, 300],
[3, 150, 155, 160, 301],
]
for k in xrange(len(data)-1):
A, B = data[k], data[k+1]
i, j, x = 0, 0, None
while i<len(A) or j<len(B):
while i<len(A) and (j>=len(B) or A[i] < B[j]):
A[i] = x
i += 1
while j<len(B) and (i>=len(A) or B[j] < A[i]):
B[j] = x
j += 1
if i<len(A) and j<len(B):
x = A[i]
i += 1
j += 1
print data[-1]
数据=[
[10, 160, 200, 500, 500],
[4, 150, 160, 170, 500],
[2, 160, 200, 202, 203],
[3, 150, 155, 160, 300],
[3, 150, 155, 160, 301],
]
对于X范围内的k(透镜(数据)-1):
A、 B=数据[k],数据[k+1]
i、 j,x=0,0,无
虽然i这是pythonO(n^2)
中的一个解决方案,但它不使用额外的空间,而是销毁列表:
def find_common(lists):
num_lists = len(lists)
first_list = lists[0]
for j in first_list[::-1]:
common_found = True
for i in range(1,num_lists):
curr_list = lists[i]
while curr_list[len(curr_list)-1] > j:
curr_list.pop()
if curr_list[len(curr_list)-1] != j:
common_found = False
break
if common_found:
return j
下面是Java实现
public static Integer[] commonElementsInNSortedArrays(int[][] arrays) {
int baseIndex = 0, currentIndex = 0, totalMatchFound= 0;
int[] indices = new int[arrays.length - 1];
boolean smallestArrayTraversed = false;
List<Integer> result = new ArrayList<Integer>();
while (!smallestArrayTraversed && baseIndex < arrays[0].length) {
totalMatchFound = 0;
for (int array = 1; array < arrays.length; array++) {
currentIndex = indices[array - 1];
while (currentIndex < arrays[array].length && arrays[array][currentIndex] < arrays[0][baseIndex]) {
currentIndex ++;
}
if (currentIndex < arrays[array].length) {
if (arrays[array][currentIndex] == arrays[0][baseIndex]) {
totalMatchFound++;
}
} else {
smallestArrayTraversed = true;
}
indices[array - 1] = currentIndex;
}
if (totalMatchFound == arrays.length - 1) {
result.add(arrays[0][baseIndex]);
}
baseIndex++;
}
return result.toArray(new Integer[0]);
}
此Swift解决方案复制了原始解决方案,但可以修改为采用inout
参数,从而不占用额外空间。我把它留作副本,因为我认为最好不要修改原件,因为它会删除元素。通过保留索引可以不删除元素,但该算法删除元素以跟踪其位置。这是一种功能性的方法,可能不是超高效的,但有效。由于它是功能性的,因此需要较少的条件逻辑。我之所以发布它,是因为我认为这可能是一种不同的方法,可能会引起其他人的兴趣,也许其他人可以找到提高效率的方法
func findCommonInSortedArrays(arr: [[Int]]) -> [Int] {
var copy = arr
var result: [Int] = []
while (true) {
// get first elements
let m = copy.indices.compactMap { copy[$0].first }
// find max value of those elements.
let mm = m.reduce (0) { max($0, $1) }
// find the value in other arrays or nil
let ii = copy.indices.map { copy[$0].firstIndex { $0 == mm } }
// if nil present in of one of the arrays found, return result
if (ii.map { $0 }).count != (ii.compactMap { $0 }.count) { return result }
// remove elements that don't match target value.
copy.indices.map { copy[$0].removeFirst( ii[$0] ?? 0 ) }
// add to list of matching values.
result += [mm]
// remove the matched element from all arrays
copy.indices.forEach { copy[$0].removeFirst() }
}
}
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14]]
)
findCommonInSortedArrays(arr: [[],
[],
[]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)
你需要一些额外的空间来存储(可能的)公共元素…@MitchWheat。请看上面的伪代码。如果我们擅长打印公共元素,我们真的需要额外的存储吗?你真的通过二进制搜索保存了什么东西吗。。既然你需要找到所有的公共元素,为什么不扫描排序后的数组,然后在O(n)@smk中完成呢。有“N”个不同的数组,每个数组都是独立排序的。我用例子编辑了这个问题。通过扫描n个数组,我们无法在O(n)中找到公共元素。它更像是一个NXN方形矩阵,其中每一行都单独排序。
@Test
public void commonElementsInNSortedArrayTest() {
int arr[][] = { {1, 5, 10, 20, 40, 80},
{6, 7, 20, 80, 100},
{3, 4, 15, 20, 30, 70, 80, 120}
};
Integer result[] = ArrayUtils.commonElementsInNSortedArrays(arr);
assertThat(result, equalTo(new Integer[]{20, 80}));
arr = new int[][]{
{23, 34, 67, 89, 123, 566, 1000},
{11, 22, 23, 24,33, 37, 185, 566, 987, 1223, 1234},
{23, 43, 67, 98, 566, 678},
{1, 4, 5, 23, 34, 76, 87, 132, 566, 665},
{1, 2, 3, 23, 24, 344, 566}
};
result = ArrayUtils.commonElementsInNSortedArrays(arr);
assertThat(result, equalTo(new Integer[]{23, 566}));
}
func findCommonInSortedArrays(arr: [[Int]]) -> [Int] {
var copy = arr
var result: [Int] = []
while (true) {
// get first elements
let m = copy.indices.compactMap { copy[$0].first }
// find max value of those elements.
let mm = m.reduce (0) { max($0, $1) }
// find the value in other arrays or nil
let ii = copy.indices.map { copy[$0].firstIndex { $0 == mm } }
// if nil present in of one of the arrays found, return result
if (ii.map { $0 }).count != (ii.compactMap { $0 }.count) { return result }
// remove elements that don't match target value.
copy.indices.map { copy[$0].removeFirst( ii[$0] ?? 0 ) }
// add to list of matching values.
result += [mm]
// remove the matched element from all arrays
copy.indices.forEach { copy[$0].removeFirst() }
}
}
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14]]
)
findCommonInSortedArrays(arr: [[],
[],
[]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)