链接列表算法查找最多可添加10的对

时间:2009-11-03 05:24:59

标签: data-structures linked-list puzzle

您能否建议一种能够在链接列表中找到最多10个节点的所有节点对的算法。 我想出了以下内容。

  

算法:比较每个节点,从第二个节点开始,每个节点从头节点开始直到前一节点(比较当前节点之前)并报告所有这些对。

我认为这个算法应该可行,但它当然不是具有O(n2)复杂度的最有效算法。

任何人都可以暗示更有效的解决方案(可能需要线性时间)。此类解决方案可以使用其他或临时节点。

4 个答案:

答案 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开始,您输出这两个数字(如上所述,您执行NN是计数的乘积。这是(-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)

如果你是第一次对集合进行排序,它将消除需要评估的数字对。

相关问题