9种常见排序算法总结

目录
1. 交换排序: 冒泡排序,快速排序.
2. 选择排序: 简单选择排序,堆排序.
3. 插入排序: 直接插入排序,二分法插入排序,希尔排序.
4. 归并排序
5. 基数排序

import java.util.ArrayList;
import java.util.List;

public class Sort {

    /**
     * 1. 交换排序:冒泡排序,快速排序.
     */

    /**
     * 1.1 冒泡排序: 在要排序的一组数中,对当前还未排好序的范围内的全部数,
     * 自上而下对相邻的两个数依次进行比较和调整,较小的往上冒,较大的下沉.
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                 1,8,4,2,23,10
     *                 1,4,8,2,23,10
     *                 1,4,2,8,23,10
     *                 1,4,2,8,23,10
     *                 1,4,2,8,10,23
     *                 
     *                 1,4,2,8,10,23
     *                 1,2,4,8,10,23
     *                 
     * 冒泡排序是一种稳定的排序方法.
     * 若文件初状为正序,则一趟起泡就可完成排序,排序码的比较次数为n-1,且没有记录移动,时间复杂度是O(n);
     * 若文件初态为逆序,需要n-1趟起泡,每趟进行n-i次排序码的比较,且每次比较都移动三次,比较和移动次数均达到最大值∶O(n2);
     * 起泡排序平均时间复杂度为O(n2).
     */

    public static void bubbleSort(int[] nums) {
        for(int i=0;i<nums.length-1;i++){
            for(int j=0;j<nums.length-1-i;j++){
                int temp=0;
                if(!(nums[j]<nums[j+1])){
                    temp=nums[j];
                    nums[j]=nums[j+1];
                    nums[j+1]=temp;
                }
            }
        }
    }

    /**
     * 1.2 快速排序: 首先任意选取一个数据(通常选第一个)作为关键数据,
     * 然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,
     * 即确定该数的最终位置,该数两边为两个区间,在它们中各找一个关键
     * 数据继续进行前面的操作,直到所有区间都只有一个数为止,表示所有数
     * 的最终位置都被确定
     * 
     * 一趟快速排序的算法是:
     * 1)设置两个变量i,j,排序开始的时候:i=0,j=N-1;
     * 2)以第一个数组元素作为关键数据,赋值给key,即key=nums[0];
     * 3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值nums[j],将nums[j]和nums[i](key)互换;
     * 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的nums[i],将nums[i]和nums[j](key)互换;
     * ......
     * 5)当i=j时,该轮排序结束,此时i,j都指向key,key所在的位置i或j即为分割点.
     * 
     * EX: int[] nums={8(key),1,4,2,23,10}; <
     * 
     *                 8(key),1,4,2,23,10
     *                 2,1,4,8(key),23,10
     *                 
     *                 2(key),1,4 | 8 | 23(key),10
     *                 1,2,4      | 8 | 10,23
     *                 
     *                 1(key) | 2 | 4(key) | 8 | 10(key) | 23
     *                 
     * 快速排序是不稳定的排序。
     * 快速排序的时间复杂度为O(nlogn)。
     * 当n较大时使用比较好,当序列基本有序时不好。
     * 
     */

    public static void quickSort(int[] nums, int head, int tail) {
        if(head>=tail)
            return ;
        int i=head;
        int j=tail;
        int key=nums[i];
        int temp=0;
        while(i!=j){
            while(i!=j){
                if(key>nums[j]){
                    temp=nums[j];
                    nums[j]=nums[i];
                    nums[i]=temp;
                    break;
                }else {
                    j--;
                }
            }
            while(i!=j){
                if(nums[i]>key){
                    temp=nums[j];
                    nums[j]=nums[i];
                    nums[i]=temp;
                    break;
                }else {
                    i++;
                }
            }
        }
        int mid=i;
        quickSort(nums,head,mid-1);
        quickSort(nums,mid+1,tail);
    }

    /**
     * 2. 选择排序:简单选择排序,堆排序.
     */

    /**
     * 2.1 简单选择排序: 在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
     * 然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最
     * 后一个数比较为止.
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                 8,1(min),4,2,23,10
     *                 |
     *                 1(min),8,4,2,23,10
     *                 
     *                 1,8,4,2(min),23,10
     *                   |
     *                 1,2(min),4,8,23,10
     *                 
     *                 1,2,4(min),8,23,10
     *                     |
     *                 1,2,4(min),8,23,10
     *                 
     *                 1,2,4,8(min),23,10
     *                       |
     *                 1,2,4,8(min),23,10
     *                 
     *                 1,2,4,8,23,10(min)
     *                         |
     *                 1,2,4,8,10(min),23
     *                 
     * 简单选择排序是不稳定的排序.
     * 时间复杂度:T(n)=O(n2).
     * 
     */

    public static void simpleSelectionSort(int[] nums) {
        int min_index;
        int temp=0;
        for(int i=0;i<nums.length-1;i++){
            min_index=i;
            for(int j=i+1;j<nums.length;j++){
                if(nums[min_index]>nums[j]){
                    min_index=j;
                }
            }
            temp=nums[min_index];
            nums[min_index]=nums[i];
            nums[i]=temp;
        }
    }

    /**
     * 2.2 堆排序: 堆排序是一种树形选择排序,是对直接选择排序的有效改进;
     * 堆的定义: 具有n个元素的序列 (h1,h2,...,hn),当且仅当满足
     * (hi>=h2i,hi>=2i+1)(大顶堆) 或 (hi<=h2i,hi<=2i+1)(小顶堆) (i=1,2,...,n/2)时称之为堆,
     * 即任何一非叶节点的大于等于或者小于等于其左右孩子节点的值;
     * 在这里只讨论满足前者条件(hi>=h2i,hi>=2i+1)的堆(大顶堆):最后得到顺序;
     * 由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项;
     * 完全二叉树可以表示堆的结构,堆顶为根,其它为左子树、右子树.
     * 
     * 步骤:
     * (1)建立大顶堆
     * (2)堆顶与最右叶子节点(最后一个节点)交换
     * 循环...
     * 
     * 可参考:http://blog.csdn.net/xiaoxiaoxuewen/article/details/7570621/
     *                 
     * 堆排序是一种不稳定的排序算法:
     * 堆排序的最坏时间复杂度为O(nlogn).
     * 堆序的平均性能较接近于最坏性能.
     * 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件.
     * 堆排序优于简单选择排序的原因: 
     * 直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,
     * 然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较.
     * 事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,
     * 但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作.
     * 堆排序可通过树形结构保存部分比较结果,可减少比较次数.
     * 
     */

    public static void buildMaxHeap(int[] nums, int last_index){
        int half=(last_index-1)/2;
        int temp;
        int max_index;
        for (int i=half;i>=0;i--){
            max_index=i;
            int left_index=2*i+1;
            int right_index=2*i+2;
            if(left_index<=last_index){
                if(nums[left_index]>nums[max_index]){
                    max_index=left_index;
                }
                if(right_index<=last_index){
                    if(nums[right_index]>nums[max_index]){
                        max_index=right_index;
                    }
                }
            }
            if(nums[i]<nums[max_index]) {
                temp=nums[i];
                nums[i]=nums[max_index];
                nums[max_index]=temp;
            }
        }
    }

    public static void heapSort(int[] nums) {
        int temp;
        for(int i=0;i<nums.length-1;i++){
            buildMaxHeap(nums,nums.length-1-i);//从lastIndex处节点(最后一个节点)的父节点开始
            temp=nums[0];
            nums[0]=nums[nums.length-1-i];
            nums[nums.length-1-i]=temp;
        }
    }

    /**
     * 3. 插入排序:直接插入排序,二分法插入排序,希尔排序.
     */

    /**
     * 3.1 直接插入排序: 每步将一个待排序的记录,按其顺序码大小插入到前面已经
     * 排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                 8,1,4,2,23,10
     *                   |
     *                 1,8,4,2,23,10
     *                     |
     *                 1,4,8,2,23,10
     *                       |
     *                 1,2,4,8,23,10
     *                         |
     *                 1,2,4,8,23,10
     *                            |
     *                 1,2,4,8,10,23
     *                 
     * 直接插入排序是稳定的排序.
     * 文件初态不同时,直接插入排序所耗费的时间有很大差异.
     * 若文件初态为正序,则每个待插入的记录只需要比较一次就能够找到合适的位置插入,
     * 故算法的时间复杂度为O(n),这时最好的情况.若初态为反序,则第i个待插入记录
     * 需要比较i+1次才能找到合适位置插入,故时间复杂度为O(n2),这时最坏的情.
     * 直接插入排序的平均时间复杂度为O(n2).
     * 
     */

    public static void insertSort(int[] nums) {
        int insert;
        for(int i=1;i<nums.length;i++){
            if(nums[i]<nums[i-1]){
                insert=nums[i];
                for(int j=i-1;j>=0;j--){
                    nums[j+1]=nums[j];
                    if(j==0){
                        nums[0]=insert;
                    }
                    else if(insert>=nums[j-1]){
                        nums[j]=insert;
                        break;
                    }
                }
            }
        }
    }

    /**
     * 3.2 二分法插入排序: 按二分法找到合适的位置,可以减少比较的次数.
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                 8,1,4,2,23,10
     *                   |
     *                 1,8,4,2,23,10
     *                     |
     *                 1,4,8,2,23,10
     *                       |
     *                 1,2,4,8,23,10
     *                         |
     *                 1,2,4,8,23,10
     *                            |
     *                 1,2,4,8,10,23
     *                 
     * 二分法插入排序是稳定的.
     * 二分插入排序的比较次数与待排序记录的初始状态无关,仅依赖于记录的个数.
     * 当n较大时,比直接插入排序的最大比较次数少得多,但大于直接插入排序的最小比较次数.
     * 算法的移动次数与直接插入排序算法的相同,最坏的情况为n2/2,最好的情况为n.
     * 平均时间复杂度为O(n2).
     * 
     */

    public static void binarySort(int[] nums) {
        int half,insert;
        for(int i=1;i<nums.length;i++){
            half=i/2;
            insert=nums[i];
            if(nums[i]==nums[half]){
                for(int j=i-1;j>half;j--){
                    nums[j+1]=nums[j];
                }
                nums[half+1]=insert;
            }else if(nums[i]<nums[half]){
                for(int j=i-1;j>half;j--){
                    nums[j+1]=nums[j];
                }
                for(int j=half;j>=0;j--){
                    nums[j+1]=nums[j];
                    if(j==0)
                        nums[0]=insert;
                    else if(insert>=nums[j-1]){
                        nums[j]=insert;
                        break;
                    }
                }
            }else if((nums[i]>nums[half])&&(nums[i]<nums[i-1])){
                    for(int j=i-1;j>=half+1;j--){
                        nums[j+1]=nums[j];
                        if(insert>=nums[j-1]){
                            nums[j]=insert;
                            break;
                        }
                    }
            }
        }
    }

    /**
     * 3.3 希尔排序: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组;
     * 所有距离为d1的倍数的记录放在同一个组中,先在各组内进行直接插入排序;
     * 取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1);
     * 即所有记录放在同一组中进行直接插入排序为止,该方法实质上是一种分组插入方法.
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     * d=length/2=3    8,1,4,2,23,10
     *                 |     |     
     *                 2,1,4,8,23,10
     *                     
     *                 2,1,4,8,23,10
     *                   |     |   
     *                 2,1,4,8,23,10
     *                 
     *                 2,1,4,8,23,10
     *                     |      |   
     *                 2,1,4,8,23,10
     *                         
     *      d=d/2=1    2,1,4,8,23,10
     *                 | | | | |  |
     *                 1,2,4,8,10,23
     *                 
     * 希尔排序是不稳定的.
     * 希尔排序的时间性能优于直接插入排序.
     * 希尔排序的平均时间复杂度为O(nlogn).
     * 
     */

    public static void shellSort(int[] nums) {
        int d=nums.length;
        int insert;
        do{
            d/=2;
            for(int i=0;i<d;i++){
                for(int j=i+d;j<nums.length;j=j+d){
                    if(nums[j]<nums[j-d]){
                        insert=nums[j];
                        for(int k=j-d;k>=0;k=k-d){
                            nums[k+d]=nums[k];
                            if(k==i){
                                nums[i]=insert;
                            }
                            else if(insert>=nums[k-d]){
                                nums[k]=insert;
                                break;
                            }

                        }
                    }
                }
            }
        }while(d!=1);
    }

    /**
     * 4. 归并排序: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,
     * 即把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列.
     * 
     * 步骤:
     * (1)用除二来划分数组,对最小单位为二的数组排序
     * (2)合并两个数组并排序,具体方法为:
     *    在两个数组中每次找最小的放到临时空间中
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                [8,1] [4] [2,23] [10]
     *                [1,8] [4] [2,23] [10]
     *                     
     *                [1,8,4] [2,23,10]
     *                [1,4,8] [2,10,23]
     *                 
     *                [1,4,8,2,10,23]
     *                [1,2,4,8,10,23]
     *                 
     * 归并排序是稳定的排序方法.
     * 归并排序的时间复杂度为O(nlogn).
     * 速度仅次于快速排序,一般用于对总体无序,但是各子项相对有序的数列.
     * 
     */

    public static void mergeSort(int[] nums,int head,int tail) {
        if(head>=tail)
            return ;
        int mid=(head+tail)/2;
        mergeSort(nums, head, mid);
        mergeSort(nums, mid+1, tail);

        int[] temp = new int[tail-head+1];
        int count=0;
        int left = head;
        int right = mid+1;

        while(left<=mid && right<=tail){
            if(nums[left]<=nums[right]){
                temp[count++]=nums[left++];
            }else{
                temp[count++]=nums[right++];
            }
        }
        while(left<=mid){
            temp[count++] = nums[left++];
        }
        while(right<=tail){
            temp[count++] = nums[right++];
        }
        count=0;
        while(head<=tail){
            nums[head++] = temp[count++];
        }
    }

    /**
     * 5. 基数排序: 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零.
     * 从最低位开始,依次进行一次排序;这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列.
     * 
     * 步骤:
     * (1)找到最大数
     * (2)确定最大数的位数
     * (3)把数组中的所有数都修改成为最大位数形式的,高位用0补充
     * (4)建立10个数组分别表示单个位为0-9
     * (5)把数组中的数分配到表示各位的数组中
     * (6)按当前位置排序0-9
     * ...
     * 
     * EX: int[] nums={8,1,4,2,23,10}; <
     * 
     *                 08,01,04,02,23,10
     *                 
     *                 
     * 基数排序是稳定的排序算法.
     * 基数排序的时间复杂度为O(d(n+r)),d为位数,r为基数.
     * 
     */

    public static void radixSort(int[] nums) {
        int max=nums[0];
        for(int i=1;i<nums.length;i++){
            if(max<nums[i])
                max=nums[i];
        }
        int count=0;
        do{
            max=max/10;
            count++;
        }while(max!=0);

        List<ArrayList<Integer>> digit = new ArrayList<ArrayList<Integer>>();
        for (int i = 0; i < 10; i++) {
            ArrayList queue = new ArrayList();
            digit.add(queue);
        }

        for(int i=0;i<count;i++){
            for(int j=0;j<nums.length;j++){
                int add=(nums[j]%((int)Math.pow(10, i+1)))/((int)Math.pow(10, i));
                digit.get(add).add(nums[j]);
            }
            int index = 0;
            for(int j=0;j<10;j++){
                while(digit.get(j).size()!=0){
                    nums[index++]=(int) digit.get(j).get(0);
                    digit.get(j).remove(0);
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] nums={8,1,4,2,23,10};
        bubbleSort(nums);//冒泡排序:Bubble Sort
        quickSort(nums, 0, nums.length-1);//快速排序:Quick Sort
        simpleSelectionSort(nums);//简单选择排序:Simple Selection Sort
        heapSort(nums);//堆排序:Heap Sort
        insertSort(nums);//直接插入排序:Straight Insertion Sort
        binarySort(nums);//二分法插入排序:Binary Sort
        shellSort(nums);//希尔排序:Shell Sort
        mergeSort(nums,0,nums.length-1);//归并排序:Merge sort
        radixSort(nums);//基数排序:Radix Sort
        for(int i=0;i<nums.length;i++)
            System.out.print(nums[i]+" ");
        System.out.println();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值