创建了几乎可以工作的排序算法,但我不明白第二个 for 循环

Created almost working sort algorithm but I don't understand the second for-loop

这是我自己编写的代码,我理解这里所做的一切:

import java.util.Arrays;
public class Decision{
    public static void main (String[]args){
        int[] myArray = {1,8,3,0,2};


        for(int i=0; i<myArray.length-1; i++){
        if(myArray[i]<myArray[i+1]){

        }
        else{
            int temp=myArray[i];
            myArray[i]=myArray[i+1];
            myArray[i+1]=temp;
        }

        System.out.println(Arrays.toString(myArray));
        }   
    }
}

Output: [1, 3, 0, 2, 8]

所以我们首先检查数组中的第一个值是否小于下一个值。如果是,那么不要做任何事情。如果不是,则交换两者。

这种排序算法的问题在于它不会将第一个值与第三个值交换,或者第二个值与第五个值交换,等等。

我意识到如果我们添加第二个 for 循环来重复我的代码中的 for 循环,这种排序就可以完美地工作:

for(int n=myArray.length; n>1; n=n-1){
...
}

我也意识到这种排序叫做冒泡排序,但是请解释一下这个for循环的重要作用,我很遗憾我完全不明白它的作用? : /

假设这个算法的最坏情况,一个数组倒序排列,看看一个循环会发生什么

4 3 2 1 0    original array
3 4 2 1 0    after one swap
3 2 4 1 0    after two swaps
3 2 1 4 0    after three swaps
3 2 1 0 4    after the complete loop

你看到现在只有最大的元素在正确的位置,其余的仍然是倒序的。第二次通过后,第二大元素也会位于正确的位置,依此类推。

请注意,第一次之后的遍历不必遍历整个数组(但这不会造成伤害),因为越来越多的元素已经到达了它们的最终位置。

老实说,我对你的问题有点困惑。通常我建议添加诊断打印输出,但您已经有一个。如您所见,只进行一次传递最多可以将一个项目与其旁边的一个交换。您无法以这种方式对数组进行完全排序,因为一旦您处理了 [i] 处的一个元素,您的算法当然不能将该元素移动到其他任何地方。因此,如果它在一次通过后不在最终的正确位置,它就永远不会到达那里。 特别是,给定的元素每次只能移动到左边最多1个位置。最坏的情况是如果输入是在进入之前按递减顺序排序。

当您添加一个执行多次传递的外部循环时,它每次都会对数组进行更多排序,直到最终排序。执行 myArray.length - 1 遍保证即使是最坏情况的元素也有机会 "moved" 通过整个数组到达正确的位置,因为在最坏的情况下输入它保证你能够移动一个元素 left 穿过整个数组(在长度为 n 的数组中需要 n-1 移动将最后一个位置的元素放到第一个位置——如果没有点击,将其画在纸上并数数)。

我的意思是,您的工具集中缺少一项您真正应该学习的重要技能,那就是您需要习惯于可视化代码的作用。有几种方法可以做到这一点:

  1. Research the algorithm you are implementing. The linked wiki article on bubble sort pretty clearly explains the algorithm in the step-by-step section.
  2. 有了一些经验,您的头脑就会明白。
  3. 在纸上解决问题。
  4. 在调试器中逐行跟踪。
  5. 为您的程序添加临时跟踪打印输出。
  6. 当然,有时会通过破坏算法并观察变化来试验算法。

你选择了#5 和#6,你只需要学习如何解读这些信息。 Here is a working version of your sort,例如(忽略其他潜在的代码改进,我确信这里的其他答案会帮助你沿着这些路线)添加一个额外的打印来分隔输出中的通道:

public static void main (String[]args){
    int[] myArray = {1,8,3,0,2};

    for(int n=myArray.length; n>1; n=n-1){
        System.out.println("Outer loop: " + n);
        for(int i=0; i<myArray.length-1; i++){
            if(myArray[i]<myArray[i+1]){
            }
            else{
                int temp=myArray[i];
                myArray[i]=myArray[i+1];
                myArray[i+1]=temp;
            }
            System.out.println(Arrays.toString(myArray));
        }   
    }
}

这输出:

Outer loop: 5
[1, 8, 3, 0, 2]
[1, 3, 8, 0, 2]
[1, 3, 0, 8, 2]
[1, 3, 0, 2, 8]
Outer loop: 4
[1, 3, 0, 2, 8]
[1, 0, 3, 2, 8]
[1, 0, 2, 3, 8]
[1, 0, 2, 3, 8]
Outer loop: 3
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]
Outer loop: 2
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]
[0, 1, 2, 3, 8]

如您所见,在第一次通过后它没有完全排序。第二关更近一些。在这种情况下,第三遍完成了它。在这种情况下,是 0 阻碍了一切:0 必须向左移动 3 个位置,因此您必须至少完成 3 次传递。

要获得更清晰的图像,请尝试输入最坏情况 [8, 3, 2, 1, 0] 的 ideone 示例。

除了添加第二个循环你还可以说

boolean change = true;
while (change) {
   change = false;
   ...
     if (a[i] > a[i + 1]) { // write it like this and you don't need an else
       change = true;
       // swap
     }
   ...
}

两者都有效,因为对数组进行排序所需的最大遍数为 n - 1。在最坏的情况下,数组按降序而不是升序排序,元素将像水泡一样游荡到最后面。在第一次迭代中,第一个元素将在最后一个位置结束,在第二次迭代中,最初的第二个元素(现在是第一个)将移动到倒数第二个位置,依此类推。所以你可以看到你只需要 n-1 次迭代,因为在第 (n-1) 次迭代中,最初的倒数第二个元素(现在是第一个)将在第二个位置结束,而最初的最后一个元素(现在是第一个)是已经正确排序。

您还可以注意到,在每次迭代中(至少)数组末尾的另一个元素最终位于其正确位置,因此您可以在每次迭代中减少内部循环的最大值。