【问题标题】:What should be the time complexity of my sorting algorithm?我的排序算法的时间复杂度应该是多少?
【发布时间】:2015-08-14 11:17:48
【问题描述】:

昨天去面试了,

他让我编写一个程序来按升序对数组进行排序。

我写了一个程序如下,

var mySortOne = function(myArray){
    var num = 0,
        temp = 0, 
        isSwipe = false,
        counter = 0;    

    for(num = 0; num < myArray.length; num++){
        print((++counter)+" => "+myArray);
        if (((num+1) < myArray.length) && (myArray[num] > myArray[num+1])){
            temp = myArray[num+1];
            myArray[num + 1] = myArray[num];
            myArray[num] = temp;
            isSwipe = true; 
        }
        if(isSwipe){
            isSwipe = false;
            num = -1;
        }
    }

    print("\n FINAL "+myArray);
};

mySortOne([45,12,78,22,4]);

面试官很不满意,他说“我不是在求最优解,但你的排序算法最差那n^2。”

于是我写了第二个解决方案,如下,

var mySortTwo = function(myArray){
    var num = 0,
        MAX = myArray.length,
        isSwipe = false,
        temp = 0,
        counter = 0;

    do{
        isSwipe = false;
        for(num = 0; num < MAX; num++){
            print((++counter)+" => "+myArray);
            if(((num + 1) < MAX) && (myArray[num] > myArray[num+1])){
                temp = myArray[num + 1];
                myArray[num + 1] = myArray[num];
                myArray[num] = temp;
                isSwipe = true;              
            }
        }
        MAX--;
    }while(isSwipe);

    print("\n FINAL "+myArray);
};

mySortTwo([45,12,78,22,4]);

但他还是不满意。

我有一些关于这两种算法的打印语句。

第一个算法的输出是

1 => 45,12,78,22,4
2 => 12,45,78,22,4
3 => 12,45,78,22,4
4 => 12,45,78,22,4
5 => 12,45,22,78,4
6 => 12,45,22,78,4
7 => 12,22,45,78,4
8 => 12,22,45,78,4
9 => 12,22,45,78,4
10 => 12,22,45,78,4
11 => 12,22,45,4,78
12 => 12,22,45,4,78
13 => 12,22,45,4,78
14 => 12,22,4,45,78
15 => 12,22,4,45,78
16 => 12,4,22,45,78
17 => 4,12,22,45,78
18 => 4,12,22,45,78
19 => 4,12,22,45,78
20 => 4,12,22,45,78
21 => 4,12,22,45,78

 FINAL 4,12,22,45,78

第二个算法的输出如下,

1 => 45,12,78,22,4
2 => 12,45,78,22,4
3 => 12,45,78,22,4
4 => 12,45,22,78,4
5 => 12,45,22,4,78
6 => 12,45,22,4,78
7 => 12,45,22,4,78
8 => 12,22,45,4,78
9 => 12,22,4,45,78
10 => 12,22,4,45,78
11 => 12,22,4,45,78
12 => 12,4,22,45,78
13 => 12,4,22,45,78
14 => 4,12,22,45,78
15 => 4,12,22,45,78

 FINAL 4,12,22,45,78

他说两者都迭代太多了。 我们可以使用简单的冒泡排序获得更好的解决方案。

但我认为我的第二个解决方案是冒泡排序本身。

你能告诉我这两种算法的复杂度是多少,以及我如何改进第二个冒泡排序

【问题讨论】:

  • 显然,第一个甚至无法正常工作:FINAL 12,4,22,45,78
  • "排序算法比 n^2 最差。" - 他的意思是最坏情况的复杂性是O(n²),还是他的意思是(某些)复杂性是比O(n²)还差?
  • 面试官是这么说的,我的第一个解决方案是最糟糕的,将 n 提高到 2

标签: javascript algorithm sorting


【解决方案1】:

两个您的算法都是Bubble sort,因为您比较和交换相邻元素。我没有检查它们是否真的有效,但至少预期的逻辑看起来是正确的。

第一个,与外表相反, 意图是冒泡排序。 for 循环的数量决定迭代的数量。请参阅here 了解使用单个循环的冒泡排序示例。

但是,您的第一个实现看起来确实是 O(n^3),因为您在每次交换后重置了迭代变量。如果您坚持使用单个循环,我建议您循环直到 n^2 并消除您正在执行的重置,就像您在上一个链接中看到的那样。感谢Tali 指出这一点。

您的第二个已针对冒泡排序进行了很好的优化,但您可以做得更多。请注意,在冒泡排序中,最优元素(最大值或最小值)在内部循环运行后到达其最终位置。因此,对于下一次迭代,您只需要向上迭代直到上一次迭代中最后一次交换的位置。

do{
    isSwipe = false;
    newMax = 0;
    for(num = 0; num < MAX; num++){
        print((++counter)+" => "+myArray);
        if(((num + 1) < MAX) && (myArray[num] > myArray[num+1])){
            temp = myArray[num + 1];
            myArray[num + 1] = myArray[num];
            myArray[num] = temp;
            isSwipe = true;              
            newMax = i + 1; ///////////////////// here //////////////////// 
         }
    }
    MAX = newMax;
    // no need for this anymore MAX--;
}while(isSwipe);

但它仍然是二次的。但是,它您可以完成的优化。

更高级的优化是Cocktail sort,但在最坏的情况下这也是二次的。

【讨论】:

  • 我认为第一个实际上是O(n^3),因为您线性搜索数组以找到一个交换。通过O(n^2) 交换,您可以得到O(n^3) 比较。
  • @Tali 我认为你是对的。我已经更正了我的答案。
【解决方案2】:

您尝试实现冒泡排序,而您的第二个排序是正确的冒泡排序,第一个看起来像冒泡排序但效率极低。我将对您的第一个算法提出几点意见。

  1. 当有人说算法是 O(n^2) 时,他们的意思是时间 它与n^2成正比,所以当你加倍n时,时间会 变成 4 倍(大约)。这是因为循环数 增加。即对于 O(n^2) 有 n 个循环,每个循环 迭代 n 次或更少。在冒泡排序中,在每个循环中多次 发生互换。在您的情况下,每次交换都会启动一个新循环。所以 复杂性增加了一个数量级。所以你的算法是 O(n^3)。

  2. 您使用的 isSwipe 是关于冒泡排序的最佳功能之一。它使算法能够检测数组是否已排序并停止进一步处理。所以最好的情况复杂度变成o(n),但在选择排序中它仍然是o(n ^ 2)。在您的情况下, isSwipe 已变得无用。由于在比较内部设置为 true,所以当比较返回 true 时,它​​变为 true,并且由于如果 isSwipe 为 true,则将 n 设置为 -1,如果比较返回 true,则将 n 设置为 -1。所以我可以把你的算法改成这样,它会给出相同的结果:

    var mySortOne = function(myArray){
        var num = 0,
        temp = 0, 
        isSwipe = false,
        counter = 0;    
    
        for(num = 0; num < myArray.length; num++){
            print((++counter)+" => "+myArray);
            if (((num+1) < myArray.length) && (myArray[num] > myArray[num+1])){
                temp = myArray[num+1];
                myArray[num + 1] = myArray[num];
                myArray[num] = temp;
                num = -1;
            }
        }
        print("\n FINAL "+myArray);
    };
    

    或者简而言之,您根本没有使用 isSwipe。

关于你的第二个算法,你的面试官是对的。这不是简单的冒泡排序,而是优化的冒泡排序。这意味着您的算法比简单的冒泡排序提供更好的性能,后者会通过没有 MAX-- 步骤来不断比较排序的元素。所以他认为这不是简单的冒泡排序是对的,但认为简单的冒泡排序会更好是错误的。所以你只是一个提升他。恭喜,不要让任何人告诉你第二个算法不是冒泡排序。

【讨论】:

    【解决方案3】:

    第二种算法是冒泡排序。我觉得面试官错了。然而,第一个解决方案是有缺陷的,并且不是正确的排序实现。你至少还需要一个周期。

    【讨论】:

    • 第一个也是冒泡排序。
    • @IVlad 是的,但不是典型的实现我在我的回答中也注意到这两种解决方案是渐近等效的
    • @IVlad:除了冒泡排序实际上对数组进行排序这一事实之外:-)
    • @IVlad 实际上我收回了这一点。注意 isSwipe 在第一个解决方案中的变化。第一个解决方案根本不是二次的。它错过了一个周期
    • 好吧,除非出现实施错误。我没有运行代码。 isSwipe 的变化是什么意思?它不会错过一个循环,因为迭代变量被重置 (num)。
    【解决方案4】:
    1. 是的,您的两种算法都迭代得太多了。它们的复杂度为O(n*n)。如果n 很少 - 你的算法很好。但是,this JSFiddle shows 对于 n = 100000,MergeSort 对数组进行排序需要 263 毫秒,而您的排序需要 100 倍以上 - 23901 毫秒(24 秒!)才能完成。

    2. 这两种算法都是 BubbleSort 的一种。但是,您的代码过于复杂,但具有相同的复杂性。正如我猜想的那样,“我们可以使用简单的冒泡排序获得更好的解决方案”意味着“我们可以编写一个简单的冒泡排序并获得相同的结果”。

    3. 改进慢速排序算法可能不是最佳选择。你可以选择另一个。有很多算法适用于O(n*log(n))。至于我,我更喜欢 MergeSort 和 HeapSort,因为我忘记了 QuickSort 的实际工作原理。此外,MergeSort 是一种稳定的排序算法。

    【讨论】:

    • 1.第一个解决方案是错误的,它不是正确的排序算法。 2. 可以改进慢速排序算法——查找鸡尾酒排序。
    猜你喜欢
    • 2018-07-18
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 2020-01-26
    • 2015-04-20
    • 2012-07-10
    相关资源
    最近更新 更多