美文网首页
八大内部排序总结

八大内部排序总结

作者: jqboooo | 来源:发表于2019-01-24 16:25 被阅读0次
    1. 程序 = 数据结构 + 算法
    2. 程序好坏 = 时间复杂度 + 空间复杂度 + 应用场景

    如何选择算法应用的场景:根据算法本身特点和应用场景 来选择。

    算法的稳定与不稳定:
    1. 稳定:排序算法次数不会发生变化。稳定的算法经过改进后,基本变成不稳定。
    2. 不稳定:排序算法次数会发生变化

    接下来总结8大内部排序算法和代码实现:

    1. 冒泡排序
    2. 选择排序
    3. 快速排序
    4. 归并排序
    5. 链式基数排序
    6. 插入排序
    7. 希尔排序
    8. 堆排序

    1、冒泡排序

    1. 应用场景:8个以内的数据,速度最快

    2.算法思路:从前面2个进行大小比较,进行交换。然后依次比较。

    3.也是属于蛮力法的一种,先来了解下蛮力法的概念:

    蛮力法(brute force method, 也称为穷举法或枚举法)。是一种简单直接解决问题的方法,常常直接基于问题的描述,所以蛮力法也是最容易应用的方法。但是,用蛮力法设计的算法时间特性往往也是最低的,典型的指数时间算法一般都是通过蛮力法搜索而得到的。

    代码

    /**
     * 蛮力法,冒泡排序
     * 例子:3 1 5 8 2 9 4 6 7 
     * 时间复杂度:n*(n-1)/2  -> 趋近于n
     * @param array
     */
    public static void bubbleSort(int[] array){
        for(int i=array.length-1;i>0;i--) {
            boolean flag=true;
            for (int j = 0; j < i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag=false;
                }
            }
            if(flag){//说明后面都是排好序的,不用排序了
                break;
            }
        }
    }
    

    2、选择排序

    1.应用场景:8个以内的数据,速度最快

    2.算法思路:定位最前的一个值,然后在进行后面所有的值比较大小,找到最小的就和定位的那个值进行交换,然后依次进行。

    代码

    /**
     * 选择排序法
     * 例子:{1,2,5,8,3,9,4,6,7}
     * @param array
     */
    public static void selectSort(int[] array){
        for(int i=0;i<array.length-1;i++) {
            int index = i;
            for (int j = i+1; j < array.length; j++) {
                if (array[j] < array[index]) {//查找最小值的位置
                    index = j;
                }
            }
            if(index!=i) {//如果已经是最小的,就不需要交换
                int temp = array[index];
                array[index] = array[i];
                array[i] = temp;
            }
        }
    }
    

    3、快速排序(前序)

    应用场景:数据量大并且是线性结构,如果是链表,那么没有性能可言的。
    短处:
    1. 有大量重复数据的时候,性能不好
    2. 单向链式结构处理性能不好(一般来说,链式都不使用)
    3.png

    代码

    /**
     * 快速排序  31  21  59  68  12  40
     *         x=31
     * @param array
     * @param begin
     * @param end
     */
    //
    public static void quickSort(int[] array,int begin,int end){
        if(end-begin<=0) return;
        int x=array[begin];
        int low=begin;//0
        int high=end;//5
        //由于会从两头取数据,需要一个方向
        boolean direction=true;
        L1:
        while(low<high){
            if(direction){//从右往左找
                for(int i=high;i>low;i--){
                    if(array[i]<=x){
                        array[low++]=array[i];
                        high=i;
                        direction=!direction;
                        continue L1;
                    }
                }
                high=low;//如果上面的if从未进入,让两个指针重合
            }else{
                for(int i=low;i<high;i++){
                    if(array[i]>=x){
                        array[high--]=array[i];
                        low=i;
                        direction=!direction;
                        continue L1;
                    }
                }
                low=high;
            }
        }
        //把最后找到的值 放入中间位置
        array[low]=x;
        //开始完成左右两边的操作
        quickSort(array,begin,low-1);
        quickSort(array,low+1,end);
    }
    

    4、归并排序(后序)

    应用场景:数据量大并且有很多重复数据,链式结构。
    短处:需要空间大
    4.png

    代码

    /**
     * 归并排序
     * @param array
     * @param left
     * @param right
     */
    public static void mergeSort(int array[],int left,int right){
        if(left==right){
            return;
        }else{
            int mid=(left+right)/2;
            mergeSort(array,left,mid);
            mergeSort(array,mid+1,right);
            merge(array,left,mid+1,right);
        }
    }
    
    /**
     * 归并排序
     *
     * 0    4   7
     * 1  2  5  9 === 3  4  10  11
     *
     * @param array
     * @param left
     * @param mid
     * @param right
     */
    public static void merge(int[] array,int left,int mid,int right){
        int leftSize=mid-left;
        int rightSize=right-mid+1;
        //生成数组
        int[] leftArray=new int[leftSize];
        int[] rightArray=new int[rightSize];
        //填充数据
        for(int i=left;i<mid;i++){
            leftArray[i-left]=array[i];
        }
        for(int i=mid;i<=right;i++){
            rightArray[i-mid]=array[i];
        }
        //合并
        int i=0;
        int j=0;
        int k=left;
        while(i<leftSize && j<rightSize){
            if(leftArray[i]<rightArray[j]){
                array[k]=leftArray[i];
                k++;i++;
            }else{
                array[k]=rightArray[j];
                k++;j++;
            }
        }
        while(i<leftSize){
            array[k]=leftArray[i];
            k++;i++;
        }
        while(j<rightSize){
            array[k]=rightArray[j];
            k++;j++;
        }
    }
    

    5、链式基数排序

    只适合几十个数据以内的情况。 数据量不大的情况下,用链式是非常快的。

    例如:麻将的排序过程

    5.png

    代码

    /**
     * 基数排序
     * author: bobo
     * create time: 2018/12/11 2:08 PM
     * email: jqbo84@163.com
     */
    public class BaseSort {
        
        /**
         * 基数排序
         */
        public static void radixSort(LinkedList<Mahjong> list){
            //对应点数进行分组
            LinkedList[] rankList = new LinkedList[9];
            for (int i = 0; i < rankList.length; i++) {
                rankList[i] = new LinkedList();
            }
            //把数据一个个放到对应的组中
            while (list.size() > 0) {
                //取一个
                Mahjong m = list.remove();
                //放到组中,下标=点数减1
                rankList[m.rank - 1].add(m);
            }
            for (int i = 0; i < rankList.length; i++) {
                list.addAll(rankList[i]);
            }
    
            //花色进行分组
            LinkedList[] suitList = new LinkedList[3];
            for (int i = 0; i < suitList.length; i++) {
                suitList[i] = new LinkedList();
            }
            //把数据一个个放到组中
            while (list.size() > 0) {
                //取一个
                Mahjong m = list.remove();
                //放到组中,下标=点数减1
                suitList[m.suit - 1].add(m);
            }
    
            //把3个组合到一起
            for (int i = 0; i < suitList.length; i++) {
                list.addAll(suitList[i]);
            }
    
        }
        
        class Mahjong {
            public int suit;// 花色 筒 万 索
            public int rank;// 点数,一 二 三
        
            public Mahjong(int suit, int rank) {
                this.suit = suit;
                this.rank = rank;
            }
        
            @Override
            public String toString() {
                return "("+this.suit+" "+this.rank+")";
            }
        }
    
    }
    
    测试
    @Test
    public void testMahjong (){
        LinkedList<Mahjong> list=new LinkedList<Mahjong>();
        list.add(new Mahjong(3,1));
        list.add(new Mahjong(2,3));
        list.add(new Mahjong(3,7));
        list.add(new Mahjong(1,1));
        list.add(new Mahjong(3,8));
        list.add(new Mahjong(2,2));
        list.add(new Mahjong(3,2));
        list.add(new Mahjong(1,3));
        list.add(new Mahjong(3,9));
        System.out.println("排序之前:" + list);
        radixSort(list);
        System.out.println("排序之后:" + list);
    }
    
    结果
    排序之前:[(3 1), (2 3), (3 7), (1 1), (3 8), (2 2), (3 2), (1 3), (3 9)]
    排序之后:[(1 1), (1 3), (2 2), (2 3), (3 1), (3 2), (3 7), (3 8), (3 9)]
    

    6、插入排序

    应用场景:主要是为希尔排序做准备的

    例如:玩扑克牌的过程中,在一张一张摸牌的手机,会直接在手中插入牌,并排好序。

    6.png

    代码

    /**
     * 插入排序
     *
     * @param array
     */
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int k = i;
            int target = array[k];
            while (k > 0 && target < array[k - 1]) {
                array[k] = array[k - 1];
                k--;
            }
            array[k] = target;
        }
    }
    
    测试
    @Test
    public void main() {
        int[] array = {8, 5, 7, 3, 6, 4, 1, 2, 9};
        insertSort(array);
        System.out.print("插入排序:");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "  ");
        }
    }
    
    结果:
    插入排序:1  2  3  4  5  6  7  8  9  
    

    7、希尔排序

    应用场景:在插入排序的基础上,适合数据量在中等的情况,几十个到几万个。
    1. 比如: 麻将在玩的过程中的重复排序(因为数据源本身即是有序)

    2. 概念:已知的最好步长序列由Marcin Ciura设计(1,4,10,23,57,132,301,701,1750,…)
      这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。” 用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快, 但是在涉及大量数据时希尔排序还是比快速排序慢。

    7.png
    1. 代码

       /**
        * 希尔排序
        *
        * @param array 数组
        * @param step  步长
        */
       public static void shellSort(int[] array, int step) {
           for (int j = 0; j < step; j++) {
               //直接插入排序
               for (int i = j + step; i < array.length; i = i + step) {
                   int k = i;
                   int target = array[k];
                   while (k > step - 1 && target < array[k - step]) {
                       array[k] = array[k - step];
                       k = k - step;
                   }
                   array[k] = target;
               }
           }
       }    
       
       测试:
       @Test
       public void main() {
           int[] array = {8, 5, 7, 3, 6, 4, 1, 2, 9};
           shellSort(array, 3);
           shellSort(array, 1);
      
           System.out.print("希尔排序:");
           for (int i = 0; i < array.length; i++) {
               System.out.print(array[i] + "  ");
           }
           System.out.println();
       }
       
       结果:
       希尔排序:1  2  3  4  5  6  7  8  9  
      

    8、堆排序

    应用场景:在大数据情况下找到前n个数据
    1. 概念:堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

    2. 堆排序的过程:

      1. 从最后一个非叶子节点开始,每三个节点做一次大小比较,最小的做根
        如果移动过程中如果子树上的顺序被破坏了,子树上重新调整三个节点的位置
      2. 取走整个树的根节点,把最后一个叶子做为根节点
      3. 重复1和2,直到所有节点被取走了
    3. 完全二叉树链式结构和线性结构的换算

      如果当前节点是 k

      1. 父节点是:(k-1)/2
      2. 左孩子是: 2 * k + 1
      3. 右孩子是: 2 * k + 2
    4. 时间复杂度为:O(nlogn)

    8-1.png
    1. 代码:
        /**
         * 堆排序
         * @param array
         * @param len
         */
        public void heapSort(int array[], int len) {
            //建堆  len/2-1最后一个非叶子节点
            for (int i = len / 2 - 1; i >= 0; i--) {
                maxHeapify(array, i, len - 1);
            }
            //排序,根节点和最后一个节点交换
            //换完以后,取走根,重新建堆
            //len-1 最后一个节点
            for (int i = len - 1; i > 0; i--) {
                int temp = array[0];
                array[0] = array[i];
                array[i] = temp;
                maxHeapify(array, 0, i - 1);
            }
        }
        
        /**
         * 调整堆
         */
        public void maxHeapify(int array[], int start, int end) {
            //父亲的位置
            int dad = start;
            //儿子的位置
            int son = dad * 2 + 1;
            while (son <= end) {//如果子节点下标在可以调整的范围内就一直调整下去
                //如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
                if (son + 1 <= end && array[son] < array[son + 1]) {
                    son++;
                }
                //和父节点比大小
                if (array[dad] > array[son]) {
                    return;
                } else {//父亲比儿子小,就要对整个子树进行调整
                    int temp = array[son];
                    array[son] = array[dad];
                    array[dad] = temp;
                    //递归下一层
                    dad = son;
                    son = dad * 2 + 1;
                }
            }
        }
    
        测试:
        @Test
        public void main() {
            int[] array = {8, 5, 7, 3, 6, 4, 1, 2, 9};
            heapSort(array, array.length);
            System.out.print("堆排序: ");
            for (int i = 0; i < array.length; i++) {
                System.out.print(array[i] + " ");
            }
    
        }
        
        结果:
        堆排序: 1 2 3 4 5 6 7 8 9 

    相关文章

      网友评论

          本文标题:八大内部排序总结

          本文链接:https://www.haomeiwen.com/subject/rztujqtx.html