排序I

冒泡排序

冒泡排序的基本思想是两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

标准代码

package com.weixuan.sort.bubble;

public class BubbleSort {

    public static void bubbleSort(int[] data) {
        for (int i = 0; i < data.length; i++) {
            for (int j = data.length - 2; j >= i; j--) {
                if (data[j] > data[j + 1])
                    swap(data, j, j + 1);
            }
            System.out.println("第 " + (i+1) + " 次");
            printArray(data);
            System.out.println("\n");
        }
    }

    private static void swap(int[] data, int j, int i) {
        if (data == null || data.length <= 0)
            return;
        int temp = data[j];
        data[j] = data[i];
        data[i] = temp;
    }

    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++)
            System.out.print(array[i] + "\t");
    }
}

过程详解 – {9,1,5,8,3,7,4,6,2}

第一趟:i==0,j = 8,j从j = 8 反循环遍历到j = 0,逐个比较,直到找到最小值放在i=0的位置上。

2 < 6 : 9, 1, 5, 8, 3, 7, 4, 2, 6 j = 8
2 < 4 : 9, 1, 5, 8, 3, 7, 2, 4, 6 j = 7
2 < 7 : 9, 1, 5, 8, 3, 2, 7, 4, 6 j = 6
2 < 3 : 9, 1, 5, 8, 2, 3, 7, 4, 6 j = 5
2 < 8 : 9, 1, 5, 2, 8, 3, 7, 4, 6 j = 4
2 < 5 : 9, 1, 2, 5, 8, 3, 7, 4, 6 j = 3
1 < 2<不交换> : 9, 1, 2, 5, 8, 3, 7, 4, 6 j = 2
1 < 9 : 1, 9, 2, 5, 8, 3, 7, 4, 6 j = 1

第一趟的结果就是:

1, 9, 2, 5, 8, 3, 7, 4, 6

第二趟:i==1,j = 8,j从j = 8 反循环遍历到j = 1,逐个比较,直到找到次最小值放在i=1的位置上。

4 < 6<不交换> : 1, 9, 2, 5, 8, 3, 7, 4, 6 j = 8
4 < 7 : 1, 9, 2, 5, 8, 3, 4, 7, 6 j = 7
3 < 4<不交换> : 1, 9, 2, 5, 8, 3, 4, 7, 6 j = 6
3 < 8 : 1, 9, 2, 5, 3, 8, 4, 7, 6 j = 5
3 < 5 : 1, 9, 2, 3, 5, 8, 7, 4, 6 j = 4
2 < 3<不交换> : 1, 9, 2, 3, 5, 8, 7, 4, 6 j = 2
2 < 9 : 1, 2, 9, 3, 5, 8, 7, 4, 6 j = 2
1 < 2<不交换> : 1, 2, 9, 3, 5, 8, 7, 4, 6 j = 1

第二趟的结果就是:

1, 2, 9, 3, 5, 8, 7, 4, 6

后面的同理

第 1 次
1   9   2   5   8   3   7   4   6   

第 2 次
1   2   9   3   5   8   4   7   6   

第 3 次
1   2   3   9   4   5   8   6   7   

第 4 次
1   2   3   4   9   5   6   8   7   

第 5 次
1   2   3   4   5   9   6   7   8   

第 6 次
1   2   3   4   5   6   9   7   8   

第 7 次
1   2   3   4   5   6   7   9   8   

第 8 次
1   2   3   4   5   6   7   8   9   

第 9 次
1   2   3   4   5   6   7   8   9   

可以优化的地方

假设有序列{2,1,3,4,5,6,7,8,9},除了2和1需要交换之外,其他的已经有序了,无需再次循环。

优化之前

输出

第 1 次
1   2   3   4   5   6   7   8   9   

第 2 次
1   2   3   4   5   6   7   8   9   

第 3 次
1   2   3   4   5   6   7   8   9   

第 4 次
1   2   3   4   5   6   7   8   9   

第 5 次
1   2   3   4   5   6   7   8   9   

第 6 次
1   2   3   4   5   6   7   8   9   

第 7 次
1   2   3   4   5   6   7   8   9   

第 8 次
1   2   3   4   5   6   7   8   9   

第 9 次
1   2   3   4   5   6   7   8   9   

可以看到,从第二次开始就是已经有序了,但是这个傻瓜式的程序还循环了7次!!!

优化的代码

    public static void bubbleSortSuper(int[] data) {
        boolean flag = true;
        for (int i = 0; i < data.length && flag; i++) {
            flag = false;/*初始为false*/
            for (int j = data.length - 2; j >= i; j--) {
                if (data[j] > data[j + 1]) {
                    swap(data, j, j + 1);
                    flag = true; // 有数据交换,flag = true
                }
            }
            System.out.println("第 " + (i + 1) + " 次");
            printArray(data);
            System.out.println("\n");
        }
    }

输出,只循环了两次

第 1 次
1   2   3   4   5   6   7   8   9   

第 2 次
1   2   3   4   5   6   7   8   9

性能分析 序列长度为N

最好的情况:序列本身有序,那么就是 N1 次比较,没有交换,时间复杂度是 O(N)

最差的情况:序列逆序,需要比较

i=0n1(i1)=1+2+3+...+(n1)=n(n1)2

基本也是这个数量级的交换,所以时间复杂度是 O(N2)

快速排序

基本思想:每一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录关键字小,然后分别对这两部分进行排序,打到整个序列有序的目的。

package com.weixuan.sort.quicksort;

public class QuickSort {

    private static void swap(int[] data, int j, int i) {
        if (data == null || data.length <= 0)
            return;
        int temp = data[j];
        data[j] = data[i];
        data[i] = temp;
    }

    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++)
            System.out.print(array[i] + "\t");
    }

    private static int partition(int[] data, int low, int high) {
        int pivot = data[low];// 轴值
        while (low < high) {
            while (low < high && data[high] >= pivot)
                high--;
            // 交换
            swap(data, low, high);
            while (low < high && data[low] <= pivot)
                low++;
            // 交换
            swap(data, low, high);
        }
        System.out.println("------");
        printArray(data);
        System.out.println("------");
        return low;
    }

    private static void quickSort(int[] data, int low, int high) {
        if (low < high) {
            int k = partition(data, low, high);
            quickSort(data, low, k - 1);
            quickSort(data, k + 1, high);
        }
    }

    public static void quickSort(int[] data) {
        quickSort(data, 0, data.length - 1);
    }
}

排序过程 – {50,10,90,30,70,40,80,60,20}

第一趟:
初始pivot = 50,low = 0,high = 8,data[low] = 50,data[high] = 20

data[high] = 20 < pivot ,交换,但是low和high没有变,直接跳出循环。
20,10,90,30,70,40,80,60,50 [此时 low = 0,high = 8]

data[low] = 20 < pivot,low++,只到low = 2,data[low] = 90 >pivot,退出循环,交换
20,10,50,30,70,40,80,60,90 [此时 low = 2,high = 8]

low = 2 < high = 8 ,继续大循环
初始pivot = 50,low = 2,high = 8

data[high]=90 > pivot,high–,只到high = 5,data[high] = 40 < pivot,跳出循环,交换
20,10,40,30,70,50,80,60,90 [此时 low = 2,high = 5]

data[low] = 40 < pivot,low++,只到low = 4,data[low] = 70 > pivot,跳出循环,交换
20,10,40,30,50,70,80,60,90 [此时 low = 4,high = 5]

low = 4 < high = 5 ,继续大循环

low = high = 4,退出循环,

返回low = 4

第一趟的结果就是

[20,10,40,30,50,70,80,60,90]

然后就是递归调用

对[20,10,40,30] 和 [70,80,60,90]分别执行第一趟的操作。

性能分析

快速排序的事件性能取决于快速排序递归的深度,上述的递归过程第一个关键字是50,正好是待排序序列的中间值,因此整个树是平衡的。此时性能最好。

在最优情况下,长度为n的序列递归的深度是 O(log2N)+1 , O(log2N) 向下取整。即仅需递归 O(log2N) 次,需要时间为 T(n) 的话,第一次partition对整个数组扫描一遍,做n次比较,然后一分为二,各自需要 T(n/2) 的时间。

也就是说,在最优情况下,时间复杂度为 O(nlogn) ,空间复杂度是 O(logN)

最差情况下,序列为有序或者逆序,此时需要n-1次递归调用,第i次划分需要n-i次比较才能找到第i个记录,因此比较的次数是

i=0n1(Ni)=1+2+3+...+(n1)=n(n1)2

时间复杂度是 O(N2) ,空间复杂度是 O(N)

由于快速排序的关键字比较是跳跃进行的,所以是不稳定的排序方法。

快速排序优化

快排最好情况是第一个轴值就是整个序列的中间值,也就是说跨拍的性能瓶颈在与pivot在序列中的位置。

那么可以在low与hight之间随机选择一个数,随机选取轴值法。这样有些碰运气的感觉。

更优化的是三数取中法,即取三个关键字先进行排序,将中间数作为轴值,一般是取左端,右端和中间三个数。

  1. 选择合适的轴值
    private static int partition(int[] data, int low, int high) {
        //int pivot = data[low];// 轴值
        int pivot = medianOfThree(data,low,high);
        while (low < high) {
            while (low < high && data[high] >= pivot)
                high--;
            // 交换
            swap(data, low, high);
            while (low < high && data[low] <= pivot)
                low++;
            // 交换
            swap(data, low, high);
        }
        System.out.println("------");
        printArray(data);
        System.out.println("------");
        return low;
    }

    private static void quickSort(int[] data, int low, int high) {
        if (low < high) {
            int k = partition(data, low, high);
            quickSort(data, low, k - 1);
            quickSort(data, k + 1, high);
        }
    }

    public static void quickSort(int[] data) {
        quickSort(data, 0, data.length - 1);
    }

    private static int medianOfThree(int[] data, int low, int high) {
        int pivot;
        int media = low + (high - low) / 2;
        // 保证左端较小
        if (data[low] > data[high])
            swap(data, low, high);
        // 保证中间较小
        if (data[media] > data[high])
            swap(data, high, media);
        // 保证左端较小
        if (data[media] > data[low])
            swap(data, media, low);
        pivot = data[low];
        return pivot;
    }
  1. 避免不必要的交换
private static void quickSort(int[] data, int low, int high) {
        if (low < high) {
            // int k = partition(data, low, high);
            int k = partitionSuper(data, low, high);
            quickSort(data, low, k - 1);
            quickSort(data, k + 1, high);
        }
    }

    public static void quickSort(int[] data) {
        quickSort(data, 0, data.length - 1);
    }

    private static int medianOfThree(int[] data, int low, int high) {
        int pivot;
        int media = low + (high - low) / 2;
        // 保证左端较小
        if (data[low] > data[high])
            swap(data, low, high);
        // 保证中间较小
        if (data[media] > data[high])
            swap(data, high, media);
        // 保证左端较小
        if (data[media] > data[low])
            swap(data, media, low);
        pivot = data[low];
        return pivot;
    }

    private static int partitionSuper(int[] data, int low, int high) {
        int pivot = medianOfThree(data, low, high);
        while (low < high) {
            while (low < high && data[high] >= pivot)
                high--;
            // 将交换改为替换
            // swap(data, low, high);
            data[low] = data[high];
            while (low < high && data[low] <= pivot)
                low++;
            // 将交换改为替换
            // swap(data, low, high);
            data[high] = data[low];
        }
        return low;
    }

选择排序

简单选择排序的基本思想是每一趟在 Ni+1 个记录中选取最小的记录作为有序序列的第i个记录。

package com.weixuan.sort.select;

public class SelectSort {
    private static void swap(int[] data, int j, int i) {
        if (data == null || data.length <= 0)
            return;
        int temp = data[j];
        data[j] = data[i];
        data[i] = temp;
    }

    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++)
            System.out.print(array[i] + "\t");
    }

    public static void selectSort(int[] data) {
        int minIndex;
        for (int i = 0; i < data.length; i++) {
            minIndex = i;
            for (int j = i + 1; j < data.length; j++) {
                if (data[minIndex] > data[j])
                    minIndex = j;
            }
            if (i != minIndex) //找到最小值
                swap(data, i, minIndex);
        }
    }
}

过程说明 – {9,1,5,8,3,7,4,6,2}

i = 0,minIndex = 0,j = 1
data[minIndex] > data[1],minIndex = 1, (比较8次)
minIndex = 1,j = 2…一直循环到j = 8
1,9,5,8,3,7,4,6,2

i = 1,minIndex = 1,j = 2
在剩余的部分找最小值,交换 (比较7次)
1,2,5,8,3,7,4,6,9

i = 2,minIndex = 1,j = 3
在剩余的部分找最小值,交换
1,2,3,8,5,7,4,6,9

i = 3,minIndex = 1,j = 4
在剩余的部分找最小值,交换
1,2,3,4,5,7,8,6,9

i = 4,minIndex = 1,j = 5
在剩余的部分找最小值,交换
1,2,3,4,5,6,8,7,9

i = 5,minIndex = 1,j = 6
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9

i = 6,minIndex = 1,j = 7
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9

i = 7,minIndex = 1,j = 1
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9

i = 8,minIndex = 1,j = 1
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9

性能分析

  1. 无论最好最差情况,比较次数是一样多。第i趟需要进行n-i次关键字比较,总的比较次数
    i=0n1(Ni)=1+2+3+...+(n1)=n(n1)2
  2. 交换次数,最好时,本身有序,交换0次,最差的时候,逆序,交换n-i次
  3. 时间复杂度是 O(N2)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值