【问题标题】:implementing an algorithm that requires minimal memory实现需要最少内存的算法
【发布时间】:2014-01-23 00:18:24
【问题描述】:

我正在尝试实现一种算法来找到需要最少内存的第 N 个最大元素。

示例: 整数列表:1;9;5;7;2;5。数量:2 删除重复项后,列表变为 1;9;5;7;2。 所以,答案是 7,因为 7 是修改后列表中的第二大元素。

在下面的算法中,我使用冒泡排序对列表进行排序,然后在不使用临时变量的情况下删除重复项,这是否使我的程序内存高效?任何想法或建议

type Integer_array is Array (Natural range <>) of Integer;

 procedure FindN-thLargestNumber (A : in out Integer_Array) is
 b : Integer;
 c:Integer;
 begin

//sorting of array
    for I in 0 to length(A) loop
       for J in 1 to length(A) loop
          if A[I] > A[J] then
             A[I] = A[I] + A[J];
             A[J] = A[I] - A[J];
             A[I] = A[I] - A[J];
          end if;
       end loop;
    end loop;

//remove duplicates
    for K in 1 to length(A) loop
       IF A[b] != A[K] then
       b++;
       A[b]=A[K];
     end loop;
    c = ENTER TO FIND N-th Largest number

    PRINT A[b-(c-1)] ;
 end FindN-th Largest Number 

【问题讨论】:

  • 在实践中避免使用 temp 并不能节省任何内存 - 最终都会出现在寄存器中。冒泡排序对于任何目的都是可怕的(O(N**2) 时间)。快速排序需要 O(log N) 额外空间,但速度更快。
  • 为什么这个问题被标记为 Java?

标签: algorithm big-o bubble-sort


【解决方案1】:

要查找第 n 个最大的元素,您根本不需要对主列表进行排序。请注意,如果 N 小于 M,则此算法将表现良好。如果 N 是列表大小的很大一部分,那么您最好只对列表进行排序。

您只需要一个排序列表,其中包含您的最大 N,然后从该列表中选择最小的(这都是未经测试的,因此可能需要进行一些调整):

int[n] found = new int[n];

for (int i = 0;i<found.length;i++) {
   found[i] = Integer.MIN_VALUE;
}


for (int i: list) {
   if (i > found[0]) {
       int insert = 0;

       // Find the point in the array to insert the value
       while (insert < found.length && found[insert] < i) {
           insert++;
       }

       // If not at the end we have found a larger value, so move back one before inserting
       if (found[insert] >= i) {
          insert --;
       }

       // insert the value and shuffle everything BELOW it down.
       for (int j=insert;j<=0;j--) {
            int temp = found[j];
            found[j]=i;
            i=temp;
       }
   }
}

At the end you have the top N values from your list sorted in order. the first entry in the list is Nth value, the last entry the top value.

【讨论】:

  • 堆/优先级队列应该比排序列表执行得更好(取决于 N 有多大)。
  • 对于大 N 是。它必须非常大才能有所作为,因为您在循环中大多数时候只检查数组中的一个条目。我个人会考虑使用TreeSet,但他说内存使用量最少,这确实是您可以获得最少内存的最佳选择。
  • 只是我刚刚看到问题已被取消标记为 Java,所以我没有真正使用任何花哨的功能是一件好事 :)
  • 但是问题关于minimal memory作为第一要务。由于理论上的最小内存接近于零(只有一个临时变量),这离目标很远。
  • 是的。这结合了出色的性能和出色的内存使用。如果这是唯一的标准,那么冒泡排序是您可以获得的最佳内存,但性能会很糟糕。
【解决方案2】:

如果你需要第 N 个最大的元素,那么你不需要对整个数组进行排序。您应该应用选择排序,但仅限于所需的 N 个步骤。

【讨论】:

  • 根据 N 的大小,快速排序然后选择第 N 个元素可能会更快。 IE。如果 N 大于 logL,其中 L 是整个数组的长度。
  • @Groo 总的来说,你是对的,但 OP 的首要目标是最小化内存。因此选择是冒泡排序、选择排序或插入排序。其中,由于可用的短路,显然选择排序是最佳选择。
  • 我很确定,对于足够大的N,对整个数组进行堆排序会击败N 选择。当然,预测N 并不简单。
  • @SteveJessop 这是保证最小内存,因此它最字面上回答了OP的问题。除此之外,最佳方式当然是例如Postgresql 确实执行SELECT ... ORDER BY ... LIMIT n---这不是一个完整的堆排序,而是一个堆支持的优先级队列。
  • @MarkoTopolnik:好吧,如果从字面上理解提问者的“最小记忆”,那么我想你必须计算冒泡排序和选择排序之间的变量,看看哪个更少。然后意识到优化器无论如何都会让你惊讶于任何严肃的编程语言中的精确堆栈使用,所以这个问题毫无意义;-) 如果只需要持续使用内存,那么对于相当适中的值,截断堆排序将击败截断选择排序N,我想。
【解决方案3】:

不要使用冒泡排序,而是使用快速排序的部分排序。

  1. 选择一个键并用作枢轴移动元素(移动所有元素>= 枢轴到数组的左侧)
  2. 计算有多少唯一元素大于等于枢轴。
  3. 如果数字小于 N,则答案在数组的右侧。否则它位于数组的左侧(与枢轴相比,左侧或右侧)
  4. 使用较小的数组和适当的 N 反复重复

复杂度为 O(n),您将需要恒定的额外内存。

【讨论】:

  • 您已经略过了“计算唯一元素”——在没有排序和额外内存的情况下如何做到这一点?至少时间复杂度不再是 O(n)。
【解决方案4】:

HeapSort 使用恒定的附加内存,因此它具有最小的空间复杂性,尽管它不使用最少数量的变量。

它按O(n log n) 时间排序,我认为是这个问题的最佳时间复杂度,因为需要忽略重复。我可能错了。

当然你不需要完成堆排序——只需将数组堆起来,然后弹出前N个不重复的最大值。

如果您确实希望最大限度地减少内存使用量,以至于您以一种或另一种方式关心一个临时变量,那么您可能不得不接受糟糕的性能。然而,这变成了一个纯粹的理论练习——它不会让你的代码在实践中更有效地使用内存,因为在非递归代码中,使用 64 字节的堆栈与使用 128 字节的堆栈之间没有实际区别。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-05
    • 2014-03-02
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多