数据结构与算法之排序算法(二):交换排序
交换排序可以分为:冒泡排序和快速排序。其中快速排序是对冒泡排序的改进
1.冒泡排序
原理:比较临近的两个数字,按照从小到大的顺序进行交换,这样一趟过去后,最大(最小)的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第i位时结束。
代码实现:
for(int i=0;i<a.length-1;i++){ for(int j=i + 1; j<a.length;j++){ if(a[j] < a[j-1]){ a[j] = a[j] ^ a[j-1]; a[j-1] = a[j] ^ a[j-1]; a[j] = a[j] ^ a[j-1]; } } }
分析:稳定,空间复杂度O(1),时间复杂度【最佳、最差、平均都是O(n*n)】。
改进:如果当前顺序已经正确,就没有必要再冒泡了。可以定义一个标志变量来标志是否当前顺序已正确。
代码实现:
for(int i=0;i<a.length-1;i++){ bool swap = false; //定义一个标志变量swap for(int j=1;j<a.length-i;j++){ if(a[j] < a[j-1]){ a[j] = a[j] ^ a[j-1]; a[j-1] = a[j] ^ a[j-1]; a[j] = a[j] ^ a[j-1]; swap = true; } } if(!swap){return;} }
分析:稳定,空间复杂度O(1),时间复杂度[最佳为O(n)、最差、平均都是O(n*n)]。
2.快速排序(冒泡排序的改进)
原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的数据小。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。
实现思路:
- 以第一个关键字k1为控制字(枢轴),将[k1,k2,....kn]分成两个子区。使左区所有关键字都小于等于控制字,右区所有关键字都大于等于控制字,最后控制字居两个子区中间适当的位置。
- 将左区和右区分别进行递归处理。
代码实现:
public static void main(String[] args) { int[] arr = new int[]{50,10,90,20,40,60,80,70}; quickSort(arr,0,arr.length-1); print(arr); }
private static void print(int[] arr) { for (int i : arr){ System.out.println(i + " "); } } private static void quickSort(int[] arr, int left, int right) { //定义关键字pivot int pivot = 0; if (left < right){ //将序列分成两个子区,算出关键字值 pivot = partition(arr,left,right); //递归左子区 quickSort(arr,left,pivot - 1); //递归右子区 quickSort(arr,pivot + 1,right); } } private static int partition(int[] arr, int left, int right) { //将序列第一个元素作为关键字,缓存起来 int pivotKey = arr[left]; //循环条件,两个指针left,right,分别指向序列的两端,直到两个指针重合 while(left < right){ //将序列最右端的值与关键字比较,如果大于,则应放在关键字右边,right减1(向左移动一个),直到不满足条件为止 while(left < right && arr[right] >= pivotKey){ right--; } //当right遇到第一个小于关键字或者两个指针重合时,将该值赋给左指针指向的位置,left向右移动。right停止移动 arr[left++] = arr[right]; //此时移动left while(left < right && arr[left] <= pivotKey){ left++; } //当left遇到第一个大于关键字或者两个指针重合时,将该值赋给右指针指向的位置,lright向左移动。left停止移动 arr[right--] = arr[left]; } arr[left] = pivotKey;//将关键字赋值给left return left;//返回关键字所在位置 } }
分析:算法不稳定,空间代价【最坏O(n)、最好和平均O(logn)】,时间代价【最坏O(n*n)、最好和平均O(nlogn)】.