数据结构与算法之排序算法(二):交换排序

交换排序可以分为:冒泡排序和快速排序。其中快速排序是对冒泡排序的改进
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.快速排序(冒泡排序的改进)

原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的数据小。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。

实现思路:

  1. 以第一个关键字k1为控制字(枢轴),将[k1,k2,....kn]分成两个子区。使左区所有关键字都小于等于控制字,右区所有关键字都大于等于控制字,最后控制字居两个子区中间适当的位置。
  2. 将左区和右区分别进行递归处理。

代码实现:

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)】.
posted @ 2017-12-14 17:57  small-boy  阅读(743)  评论(0编辑  收藏  举报