排序算法------交换排序(冒泡排序和快速排序)
目录
1、冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
1.1 算法思路
(1)第一次比较:首先比较第一和第二个数,将小数放在前面,将大数放在后面。
(2)比较第2和第3个数,将小数 放在前面,大数放在后面。
(3)如此继续,知道比较到最后的两个数,将小数放在前面,大数放在后面,重复步骤,直至全部排序完成
(4)针对所有的元素重复以上的步骤,除了最后一个,依次类推,每一趟比较次数减少依次。
1.2 算法动态图
1.3 算法分析
1.如果我们的数据正序,只需要走一趟即可完成排序。冒泡排序最好的时间复杂度为O(n)。
2.如果很不幸我们的数据是反序的,则需要进行n-1趟排序,这时最坏时间需要O(n2)。
算法 | 最好时间 | 最坏时间 | 平均时间 | 额外空间 | 稳定性 |
---|---|---|---|---|---|
冒泡算法 | O(n) | O(n2) | O(n2) | 1 | 稳定 |
关于稳定性:因为在比较的过程中,当两个相同大小的元素相邻,只比较大或者小,所以相等的时候是不会交换位置的。而当两个相等元素离着比较远的时候,也只是会把他们交换到相邻的位置。他们的位置前后关系不会发生任何变化,所以算法是稳定的。
1.4 算法实现
/**
* 冒泡算法
*/
public class BubbleSort {
public int[] sort(int[] nums){
if(nums == null || nums.length == 0){ //若数组为空,直接返回null
return null;
}
for(int i = 0; i < nums.length; ++i){
for(int j = 0; j < nums.length - i - 1; ++j){
if(nums[j] > nums[j + 1]){ //依次比较两个相邻的数的大小
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
return nums;
}
public static void main(String args[]){
int[] nums = new int[]{5,69,54,21,5,36,7,84};
BubbleSort bubbleSort = new BubbleSort();
nums = bubbleSort.sort(nums);
for(int num : nums) {
System.out.print(num + " ");
}
}
}
运行结果:5 5 7 21 36 54 69 84
1.5 优化
参考链接:https://www.cnblogs.com/jyroy/p/11248691.html
(1)当数组是,5,4,3,1,2 的时候的时候就非常明显了,实际上在第一次循环的时候整个数组就已经完成排序,但是常规版的算法仍然会继续后面的流程,这就是多余的了。
为了解决这个问题,我们可以设置一个标志位,用来表示当前第 i 趟是否有交换,如果有则要进行 i+1 趟,如果没有,则说明当前数组已经完成排序。
(2)除了上面这个问题,在冒泡排序中还有一个问题存在,就是第 i 趟排的第 i 小或者大的元素已经在第 i 位上了,甚至可能第 i-1 位也已经归位了,那么在内层循环的时候,有这种情况出现就会导致多余的比较出现。例如:6,4,7,5,1,3,2,当我们进行第一次排序的时候,结果为6,7,5,4,3,2,1,实际上后面有很多次交换比较都是多余的,因为没有产生交换操作。
针对上述的问题,我们可以想到,利用一个标志位,记录一下当前第 i 趟所交换的最后一个位置的下标,在进行第 i+1 趟的时候,只需要内循环到这个下标的位置就可以了,因为后面位置上的元素在上一趟中没有换位,这一次也不可能会换位置了。
2、快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
2.1 算法思路
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
(1)从数列中挑出一个元素,称为 “基准”(pivot);
(2)重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
(3)递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
2.2 算法动态图
2.3 算法分析
2.4 算法实现
/**
* 快速排序
*/
public class QuickSort {
public void sort(int[] nums , int start , int end){
int temp;
int i = start;
int j = end;
if(i < j){
temp = nums[i];
while (i != j){
while (i < j && temp < nums[j]){ //从数组最右边找到第一个比temp小的
j--;
}
if(i < j){ //交换位置
nums[i] = nums[j];
i++;
}
while (i < j && temp > nums[i]){ //从数组左边找到第一个比temp大的
i++;
}
if(i < j){ //交换位置
nums[j] = nums[i];
j--;
}
}
nums[i] = temp;
sort(nums , start , i - 1); //分别递归排序
sort(nums , i + 1 , end);
}
}
public static void main(String args[]){
int[] nums = new int[]{5,69,54,21,5,36,7,84};
QuickSort quickSort = new QuickSort();
quickSort.sort(nums , 0 , nums.length - 1);
for(int num : nums){
System.out.print(num + " ");
}
}
}
运行结果:
5 5 7 21 36 54 69 84