元素4和5比较,发现4小于5,所以位置不变。
元素5和6比较,发现5小于6,所以位置不变。
元素6和7比较,发现6小于7,所以位置不变。
元素7和8比较,发现7小于8,所以位置不变。
第1轮结束,数列有序区包含1个元素。
第2轮
元素3和2比较,发现3大于2,所以3和2交换。
元素3和4比较,发现3小于4,所以位置不变。
元素4和5比较,发现4小于5,所以位置不变。
元素5和6比较,发现5小于6,所位位置不变。
元素6和7比较,发现6小于7,所以位置不变。
元素7和8比较,发现7小于8,所以位置不变。
第2轮结束,数列有序区包含2个元素。
你发现其中的问题了吗? 其实右面的许多元素已经是有序的了,可是每一轮还是白白地比较了许多次。没错,这正是冒泡排序中另一个需要优化的点。
这个问题的关键点在于对数列有序区的界定。按照现有的逻辑,有序区的长度和排序的轮数是相等的。例如第1轮排序过后的有序区长度是1,第2轮排序过后的有序区长度是2 ……实际上,数列真正的有序区可能会大于这个长度,如上述例子中在第2轮排序时,后面的5个元素实际上都已经属于有序区了。因此后面的多次元素比较是没有意义的。那么,该如何避免这种情况呢?我们可以在每一轮排序后,记录下来最
后一次元素交换的位置,该位置即为无序数列的边界,再往后就是有序区了。
冒泡排序第3版 代码示例如下:
public static void sort(int []nums){
for(int i = 0; i < nums.length - 1; i ){
boolean mark = true;
int lastPost = 0;
int sortPost = nums.length - 1;
for(int j = 0; j < sortPost; j ){
int temp ;
if(nums[j] > nums[j 1]){
temp = nums[j 1];
nums[j 1] = nums[j];
nums[j] = temp;
mark = false;
lastPost = j;
}
}
sortPost = lastPost;
if(mark)break;
}
}
在第3版代码中,sortBorder就是无序数列的边界。在每一轮排序过程中,处于sortBorder之后的元素就不需要再进行比较了,肯定是有序的。
快速排序同冒泡排序一样,快速排序也属于交换排序 ,通过元素之间的比较和交换位置来达到排序的目的。
不同的是,冒泡排序在每一轮中只把1个元素冒泡到数列的一端,而快速排序则在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。
这种思路就叫作分治法 。
每次把数列分成两部分,究竟有什么好处呢?
假如给出一个8个元素的数列,一般情况下,使用冒泡排序需要比较7 轮,每一轮把1个元素移动到数列的一端,时间复杂度是O(n2 )。
而快速排序的流程是什么样子呢?