您能否建议一种能够在链接列表中找到最多10个节点的所有节点对的算法。 我想出了以下内容。
算法:比较每个节点,从第二个节点开始,每个节点从头节点开始直到前一节点(比较当前节点之前)并报告所有这些对。
我认为这个算法应该可行,但它当然不是具有O(n2)复杂度的最有效算法。
任何人都可以暗示更有效的解决方案(可能需要线性时间)。此类解决方案可以使用其他或临时节点。
答案 0 :(得分:6)
如果他们的范围有限(例如介于-100和100之间),那很容易。
创建一个数组quant[-100..100]
,然后循环浏览链表,执行:
quant[value] = quant[value] + 1
接下来的循环就可以了。
for i = -100 to 100:
j = 10 - i
for k = 1 to quant[i] * quant[j]
output i, " ", j
即使他们的范围不受限制,您也可以通过先对值进行排序,然后只保留计数而不是单个值(与上述解决方案相同),提供比您提议的方法更有效的方法。
这是通过运行两个指针来实现的,一个在列表的开头,另一个在结尾。当这些指针的数字加起来为10时,输出它们并向下移动结束指针并启动指针。
当它们大于10时,将结束指针向下移动。当它们减少时,将启动指针向上移动。
这取决于排序的性质。少于10表示您需要使总和更高(向上移动开始指针)。大于10表示您需要减少总和(结束指针向下)。由于它们在列表中没有重复(因为计数),等于10意味着你移动两个指针。
当指针相互通过时停止。
还有一个棘手的位,那就是当指针相等且值总和为10时(这只有在值为5时才会发生,显然)。
您不会根据产品输出对数,而是基于值减1的乘积。这是因为计数为1的值5实际上并不是10(因为只有一个5)。
所以,列表:
2 3 1 3 5 7 10 -1 11
你得到:
Index a b c d e f g h
Value -1 1 2 3 5 7 10 11
Count 1 1 1 2 1 1 1 1
p1
开始指针a
,在p2
开始h
。从-1 + 11 = 10
开始,您输出这两个数字(如上所述,您执行N
次N
是计数的乘积。这是(-1,11)
的一份副本。然后,您将p1
移至b
,将p2
移至g
。1 + 10 > 10
请将p1
留在b
,将p2
移至f
。1 + 7 < 10
请将p1
移至c
,将p2
移至f
。2 + 7 < 10
请将p1
移至d
,将p2
移至f
。3 + 7 = 10
,输出(3,7)
的两份副本,因为d
的计数为2,将p1
移至e
,将p2
移至{ {1}}。e
但是 5 + 5 = 10
所以产品是0或0的0。没有输出,将p1 = p2
移到p1
,{{1转到f
。p2
开始结束。因此整体产出是:
d
这是正确的。
这是一些测试代码。您会注意到我已将7(中点)强制为特定值进行测试。显然,你不会这样做。
p1 > p2
你会看到上面的代码都是O(n),因为我没有在这个版本中进行排序,只是智能地将值用作索引。
如果最小值低于零(或非常高到浪费太多内存的程度),你可以使用minVal来调整索引(另一个O(n)扫描找到最小值然后只是对数组索引使用(-1,11)
( 3, 7)
( 3, 7)
而不是#include <stdio.h>
#define SZSRC 30
#define SZSORTED 20
#define SUM 14
int main (void) {
int i, s, e, prod;
int srcData[SZSRC];
int sortedVal[SZSORTED];
int sortedCnt[SZSORTED];
// Make some random data.
srand (time (0));
for (i = 0; i < SZSRC; i++) {
srcData[i] = rand() % SZSORTED;
printf ("srcData[%2d] = %5d\n", i, srcData[i]);
}
// Convert to value/size array.
for (i = 0; i < SZSORTED; i++) {
sortedVal[i] = i;
sortedCnt[i] = 0;
}
for (i = 0; i < SZSRC; i++)
sortedCnt[srcData[i]]++;
// Force 7+7 to specific count for testing.
sortedCnt[7] = 2;
for (i = 0; i < SZSORTED; i++)
if (sortedCnt[i] != 0)
printf ("Sorted [%3d], count = %3d\n", i, sortedCnt[i]);
// Start and end pointers.
s = 0;
e = SZSORTED - 1;
// Loop until they overlap.
while (s <= e) {
// Equal to desired value?
if (sortedVal[s] + sortedVal[e] == SUM) {
// Get product (note special case at midpoint).
prod = (s == e)
? (sortedCnt[s] - 1) * (sortedCnt[e] - 1)
: sortedCnt[s] * sortedCnt[e];
// Output the right count.
for (i = 0; i < prod; i++)
printf ("(%3d,%3d)\n", sortedVal[s], sortedVal[e]);
// Move both pointers and continue.
s++;
e--;
continue;
}
// Less than desired, move start pointer.
if (sortedVal[s] + sortedVal[e] < SUM) {
s++;
continue;
}
// Greater than desired, move end pointer.
e--;
}
return 0;
}
。
而且,即使内存从低到高的范围太贵,也可以使用稀疏数组。你必须对它进行排序,O(n log n),并搜索它以更新计数,也搜索O(n log n),但这仍然比原始O(n 2 )更好。二进制搜索为O(n log n)的原因是因为单个搜索将是O(log n),但您必须为每个值执行此操作。
这是测试运行的输出,它显示了计算的各个阶段。
i-minVal
i
答案 1 :(得分:2)
创建一个哈希集(Java中的HashSet)(如果你的数字有限,可以使用稀疏数组,即你知道它们落入+/- 100)
对于每个节点,首先检查10-n是否在集合中。如果是这样,你找到了一对。无论哪种方式,然后将n添加到集合并继续。
所以例如你有 1 - 6 - 3 - 4 - 9
1 - 在集合中是9?都能跟得上
6 - 4?否。
3 - 7?否。
4 - 6?对!打印(6,4)
9 - 1?对!打印(9,1)
答案 2 :(得分:1)
这是一个微小的子集求和问题,它是NP完全的。
答案 3 :(得分:0)
如果你是第一次对集合进行排序,它将消除需要评估的数字对。