【问题标题】:A beginner way to understand how time complexity works [closed]了解时间复杂度如何工作的初学者方法[关闭]
【发布时间】:2020-06-13 17:18:16
【问题描述】:

我一直在研究我的数据结构课程的时间复杂度。我的任务是报告 Shell 排序算法并解释其时间复杂度(最佳/最差/平均情况)。我发现这个网站https://stackabuse.com/shell-sort-in-java/ 显示了这个Shell排序算法的时间复杂度:

void shellSort(int array[], int n){ 
    //n = array.length
    for (int gap = n/2; gap > 0; gap /= 2){
      for (int i = gap; i < n; i += 1) {
        int temp = array[i];
        int j;
        for (j = i; j >= gap && array[j - gap] > temp; j -= gap){
          array[j] = array[j - gap];
        }
        array[j] = temp;
      }
    }
  }

O(n log n)。但问题是我仍然对将 logn 设为 logn 或 nlogn 的含义感到困惑。

我也尝试过计步方法,但又一次,我不知道从哪里开始,所以我只是从上面的网站复制并做了这个。

void shellSort(int array[], int n){
    //n = array.length
    for (int gap = n/2; gap > 0; gap /= 2){                       //step 1 = runs logn times
      for (int i = gap; i < n; i += 1) {                          //step 2 = runs n-gap times
        int temp = array[i];                                      //step 3 = 1
        int j;                                                    //step 4 = 1
        for (j = i; j >= gap && array[j - gap] > temp; j -= gap){ //step 5 = i/gap times
          array[j] = array[j - gap];                              //step 6 = 1
        }
        array[j] = temp;                                          //step 7 = 1
      }
    }
  }

但我不知道这是否正确,我只是根据这个网站。 https://stackabuse.com/shell-sort-in-java/.

我还尝试比较了插入排序和壳排序之间的总移动次数,因为壳排序是插入排序和冒泡排序的概括。我会附上下面的图片。我还使用了一个在线数字生成器,它将给我 100 个随机数,将其复制并应用于插入排序和 Shell 排序,并将其分配为要排序的数组。

结果就是这样,

插入排序的总移动次数 = 4731

Shell Sort 的总移动次数 = 1954

Shell Sort implementation that tells me the total number of moves it does

Insertion Sort implementation that tells me the total number of moves it does

我从所有这些中了解到的是,尽管 Shell 排序是插入排序的泛化,但在对大型数组(例如 100 个元素)进行排序时,Shell 排序比插入排序快 2 倍。

但最终的问题是,有没有像这种 Shell Sort 算法一样计算时间复杂度的初学者方法?

【问题讨论】:

  • 您使用的站点中所述的时间复杂度不正确。 Wikipedia 指出,使用代码中显示的间隙序列进行 shell 排序的最坏情况复杂度为 O(n^2)。网站上显示的公式正确计算出复杂度为 O(n^2),但由于某种原因,文本错误地指出它是 O(n log n)。

标签: java algorithm loops sorting time-complexity


【解决方案1】:

您必须查看函数的大 O 或大 Theta 分析。您的外部循环在每次迭代中被除以一半,因此它运行的总时间是 log n。现在,当您查看内部循环时,它最初从 n/2 到 n 一直运行到 1 到 n 或 2 到 n ,具体取决于 n 的初始大小,因此其执行时间将为 n/2 + n/4 + 。 ... n /2^k 它是一个“谐波级数”(您也可以搜索几何级数,如果您考虑 n -> n(1/2 + 1/4 + ... + 1/2^k)等于 nlogn。现在,每个列表都可以在某种程度上排序的最佳情况是 Ω(nlogn),因为在外循环的中间,我们将找到最佳解决方案,所以我们可以说那 nlogn 是它的下限 - 意味着它肯定等于或大于那个 - 因此我们可以说平均情况是 Θ(nlog^2 n) 意味着它在那个严格的范围内 - 请注意对于一般情况,我使用 Big theta。现在,如果我们假设列表完全反转,则外部循环将一直运行到最后,这意味着 log n。内部循环将运行 nlogn,因此总时间将为nlog^2(n) 我们可以说它将是 O(nlog^2(n)) (我们也可以使用 Big O但是theta更好,您可以搜索theta如何提供紧密绑定,而大O仅提供上限)。因此,我们也可以说最坏的情况是 O(n^2),这在某些情况下是相对正确的。

我建议您查看 Big-O 和 Big-Theta 以及 Big-Omega,它们在这种情况下也很有用。

然而,shell 算法最精确的数学表示将是 O(n^3/2)。但是,仍然存在争论和分析。

我希望这会有所帮助。

【讨论】:

  • shell 排序的最坏情况复杂度与显示的间隙序列正好是 O(n^2),而不是 O(n^(3/2)),这对于其他一些间隙序列。
  • 我忘了问这个,但你为什么说内部循环运行 nlogn 次?还是很迷茫
【解决方案2】:

首先,我将证明算法永远不会比O(n^2) 慢,然后我将证明运行时可能是O(n^2)

假设n 是二的幂。我们知道插入排序最坏的情况是O(n^2)。当h 对数组进行排序时,我们正在执行h 插入排序,每个排序都在大小为n / h 的数组上。所以h-sort pass 的复杂度是O(h * (n / h)^2) = O(n^2 / h)。整个算法的复杂度现在是n^2 / h 的总和,其中h 是两个的幂,直到n / 2。这是一个几何级数,具有第一项 n^2、公比 1 / 2log2(n) 项。使用几何级数求和公式得到n^2*((1 / 2)^log2(n) - 1) / (1 / 2 - 1) = n^2*(1 / n - 1) / (-1 / 2) = n^2*(-2 / n + 2) = 2n^2 - 2n = O(n^2)

考虑通过交织两个递增序列创建的数组,其中一个序列中的所有元素都大于另一个序列中的所有元素,例如[1, 5, 2, 6, 3, 7, 4, 8]。由于这个数组是二次排序的,所以除了最后一个之外,所有的 pass 什么都不做。在最后一遍中,必须将索引i 处的元素(其中i 甚至是)移动到索引i / 2,它使用O(i / 2) 操作。所以我们有1 + 2 + 3 + ... + n / 2 = (n / 2) * (n / 2 + 1) / 2 = O(n^2)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 2014-12-20
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    相关资源
    最近更新 更多