【问题标题】:Time complexity for a sorting algorithm排序算法的时间复杂度
【发布时间】:2015-01-05 03:45:12
【问题描述】:

我在做一些练习时遇到了以下问题:

排序算法从列表的开头开始,扫描直到找到两个顺序错误的后续项目。交换这些项目并回到开始。到达列表末尾时算法结束。

对于大小为 n 的列表,最坏情况下的运行时间是多少?

我觉得它与冒泡排序类似,但可能更糟糕的是因为它没有完成扫描列表的整个过程。但我不知道如何计算它的时间复杂度。我不确定我在下面为这个算法提出的代码是否正确。非常感谢您的帮助!

for (int i=0, i<n , i++){//n is the size of the array
     if (array[i]>array[i+1]){
          swap (array[i], array[i+1]);
          i=0;
     }
}

【问题讨论】:

  • 这对我来说像是插入排序,查一下
  • 我很确定这不是插入排序。插入排序是指您拿起一个项目并将其插入到正确的位置。
  • 那将是选择排序
  • 这只是时间复杂度的问题/练习
  • i

标签: java algorithm sorting loops time-complexity


【解决方案1】:

现在比较您的代码和冒泡排序,因为它似乎是..

您的代码 它会给 Max O(n^3)

冒泡排序

void bubbleSort(int arr[])
{
    int n = arr.length;
    for (int i = 0; i < n-1; i++)
        for (int j = 0; j < n-i-1; j++)
            if (arr[j] > arr[j+1])
            {
                // swap temp and arr[i]
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
}

它会在最坏的情况下给出 O(n^2) 并且即使数组已排序。

【讨论】:

    【解决方案2】:

    一个数组可能包含 O(n^2) 次反转。每次运行仅纠正一次反转并执行 O(n) 步骤(比较)。所以总体时间复杂度为 O(n^3)。
    最坏的情况 - 具有 N*(N-1)/2 反转的后向排序数组,Raul Guiu 已经完成了精确的运行时分析。

    【讨论】:

      【解决方案3】:

      最坏情况的比较次数(我不会证明最坏情况是 n,n-1,... 1,但我假设是)是 tetrahedral number

      为什么?

      想象一个序列

      n, n-1, .... 1
      

      所以对于 1,将在之前的所有内容都已就位时进行交换。因此,在最后一个位置的比较次数将是 n-1,然后交换到倒数第二个位置。从倒数第二个位置开始,它必须进行 n - 2 次比较。遵循这个逻辑,并扩展到我们对不同 n 的先前数字:

      换句话说,对于位置 ni 需要从位置 n - 1 向下移动一个数字 (1),我需要从 n-2 (1, 2 & 3) 移动 2, (1 & 2) .假设 1,2,3... n 是有效顺序,它们以 n,...3, 2,1 开头。

      n = 3 -> 2 + 1 * 2 = 4 
      n = 4 -> 3 + 2 * 2 + 3= 10 
      n = 5 -> 4 + 3 * 2 + 2 * 3 + 4 = 20 
      n = 6 -> 5 + 4 * 2 + 3 * 3 * 2 * 4 + 5 = 35
      n = 7 -> 6 + 5 * 2 + 4 * 3 + 3 * 4 + 2 * 5 + 6 = 56 
      n = 8 -> 7 + 6 * 2 + 5 * 3 + 4 * 4 + 3 * 5 + 2 * 6 + 7= 84
      

      而这个数列就是四面体数。

      作为二项式系数:

      所以,看来是O(n^3)

      【讨论】:

      • 谢谢!你能解释一下n = 4 -&gt; 3 + 2 * 2 + 3= 10 代表什么吗? 3 是 n(当 n = 4 时)需要移动到正确位置的步数?但为什么是 2*2 +3?
      • 位置 p1、p2、p3 和 p4,起始位置 4、3、2、1 的元素。当它之前的所有东西都被排序时,1 将从位置 p4 交换到位置 p3。所以这会导致 3 次比较,2 和 1 将在位置 p3 的一个点上(2 开始,1 交换后),这意味着将 2 和 1 从位置 p3 移动到位置 p2 我们有 2 * 2 比较。 3、2 和 1 将从 p2 移动到 p1,每个比较 3 * 1。所以 3 + 2 * 2 + 3 * 1
      【解决方案4】:

      首先,这个算法看起来像冒泡排序,但比冒泡排序慢。

      其次,这个算法在做一些无意义的操作,即Swap those items and go back to the beginning.例如你的初始数组如下,

      10 11 12 13 14 15 16 17 18 19 9
      

      您将迭代到最后,当您看到 9 时,将其与 18 交换,然后转到数组的开头并开始比较 if(arr[0] &lt; arr[1]) 但您已经知道它更小。

      这个最坏情况下的运行时间是 O(n^3)。伪代码将是这样的:

      for i = 0:n-1
          if(arr[i] > arr[i+1]){
              swap(arr[i], arr[i+1])
              i = 0;
          }
      

      【讨论】:

      • 谢谢。我理解算法如何工作的逻辑,但我仍然不明白为什么复杂度是 O(n^3)……你能再解释一下吗?
      • 正如您在我的代码中看到的,为了将最后一个元素放在首位,我迭代了 n^2 次。假设这个数组是逆序的,对于每个元素它都会进行 O(n^2) 比较,所以在最坏的情况下它是 O(n^3)。操作数应该是n^3/4或n^3/6,我没有深思
      【解决方案5】:

      这是一种糟糕的插入排序形式。在插入排序中,插入的复杂度与数组的长度成线性关系,这里是二次的。因此,该算法的最坏情况时间复杂度为 IMO O(n^3)。

      如果初始数组是4、3、2、1,那么算法的步骤是:

      4 3 2 1
      3 4 2 1
      3 2 4 1
      2 3 4 1
      2 3 1 4
      2 1 3 4
      1 2 3 4
      

      请注意,此序列中的每个步骤都具有线性时间复杂度,因为要获得最早“反转”的位置,您需要从头开始扫描整个数组。

      【讨论】:

      • 你能详细说明为什么它是二次的吗?
      • @stillAFanOfTheSimpsons 如果您查看序列,要在正确位置插入 1,我们需要 3 个步骤(看似线性)。然而,每一步都需要我们从头开始扫描整个数组以首先找到交换位置。因此,插入的总复杂度在这里是二次的。
      【解决方案6】:

      你描述的是冒泡排序:http://en.wikipedia.org/wiki/Bubble_sort 冒泡排序具有最坏情况和平均复杂度О(n2)

      【讨论】:

      • 我不这么认为。 Bubble 是在一个 pass 中比较每两个连续的项目,但是这个一旦完成一个交换就会回到开始。
      • 是的,但是给定的代码不正确:它错过了外部迭代,因此 О(n2)
      • 这不是冒泡排序。