数据结构七:七大排序(插入排序,希尔排序,选择排序,堆排序冒泡排序,快速排序,归并排序)

目录

1:排序的概率

2.插入排序

2.1:直接插入排序-----稳定

2.1.1:基本思想

2.2:希尔排序

2.2.1:概念:

3.选择排序

3.1:选择排序

3.1.1:概念

3.2:堆排序

4.交换排序

4.1:概念

4.2:冒泡排序

4.3:快速排序

4.3.1:hoare版(左右指针)

4.3.2:挖坑法

4.3.3:前后指针

4.4:快排的优化

4.4.1:三数取中--取中间数值做基准

4.5:快排的非递归

5.归并排序

5.1:海量数据的排序问题


前言:前一篇我们讲了堆和优先级队列。接下来我们将进行的是七大排序,但我不会讲这七大排序都着重的说,会将一些排序大致带过,如冒泡排序,堆排序。

1:排序的概率

排序:所谓排序,就是使一串记录,按照其中的莫个或莫些关键字的大小,递增或递减的排列起来。

稳定性:在原序列中,r[i]=r[j],且r[i]在r[j]的前面,而在排序后的序列中r[i]仍在r[j]的前面,这样称为稳定性。

内部排序:数据元素放在内存中的排序。

外部排序:数据元素不能同时放在内存中,根据排序过程的要求不能在内外之间移动数据的排序。

79ec0088fb684429b009ef93b4ba96fd.png


2.插入排序

2.1:直接插入排序-----稳定

2.1.1:基本思想

将带排序的记录按其关键码的大小逐个插到一个已经排好序的有序序列中,直到所有的记录插入完为止。感觉像不像打牌的时候,自己弄自己手中的牌

d08c77866ea745769f1e30ed167193e0.png

 /**
     * 直接插入排序
     * 时间复杂度O(N^2)
     * 空间复杂度0(1)
     * 稳定
     * @param arr
     */
    public void insertSort(int [] arr){
        for (int i = 0; i <arr.length ; i++) {
          int tmp=arr[i];//记录关键码
            int j=i-1;
            for (; j >=0 ; j--) {//插入到0到i-1中
               if(arr[j]>tmp){//tmp小于arr[j]
                   arr[j+1]=arr[j];//将arr[j]向后移一个
                }else{
                   break;
               }
            }
            arr[j+1]=tmp;
        }
    }

 注意:

一个本身就稳定的排序可以实现为不稳定的排序。


2.2:希尔排序

2.2.1:概念:

先选定一个整数(gap),把待排序的文件中所有记录分成一个gap个组,所有距离相同的分在同一组,并对每一组内的记录进行排序(插入排序)。然后,重复上述分组和排序的工作。

这个gap有多种取法,我一般是按照shell提出的gap=[n/2](n:是待排元素的个数)。

361a928009bf488baedf770b3567ebca.png

 /**
     * 希尔排序
     * 时间复杂度O(2^1.3)
     * 不稳定
     * @param arr
     */
    public  static void shell(int [] arr){
        int gap= arr.length;
        while(gap>1){
            gap=gap/2;
            for (int i = gap; i <arr.length ; i++) {
                int tmp=arr[i];
                int j=i-gap;
                for (; j >=0 ; j=j-gap) {
                    if (arr[j] > tmp) {
                        arr[j + gap] = arr[j];
                    }else{
                        break;
                    }
                }
                arr[j + gap] = tmp;
            }
        }
    }

3.选择排序

3.1:选择排序

3.1.1:概念

每一次从待排序的数据元素中选出最小或(最大)的一个元素,存放到序列的起始位置,直到全部待排序的数据元素排完

322408868df040ad807ce29c0279e831.png

  /**
     * 选择排序
     * 时间复杂度O(N^2)
     *空间复杂度o(1)
     * 不稳定
     * @param arr
     */
    public static  void select(int [] arr){
        //选出最大值和最小值
        int left=0;
        int right=arr.length-1;
        while(left<right) {
            int min=left;
            int max=left;
            for (int i = left+1; i <=right; i++) {
                if (arr[i] < arr[min]) {
                    min =i;
                }
                if (arr[i] >arr[max]) {
                    max = i;
                }
            }
            //最小值和left交换。
            swap(arr,min,left);
            //最大值和right交换
            if(left==max){//这里是害怕max就是left
                max=min;
            }
            swap(arr,max,right);
            left++;
            right--;
        }
    }
    protected static void swap(int [] arr,int i,int j){
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }
}

3.2:堆排序

这里,我上一篇已经讲了,上一篇的建堆就是堆排序。

注意:

排升序要排大堆

排降序要排小堆

时间复杂度:O(N*logN)

空间复杂度:O(1)

不稳定


4.交换排序

4.1:概念

键值较大的记录向序列的尾巴移动,键值较小的记录向序列的前部移动

4.2:冒泡排序

    /**
     * 冒泡排序
     * 时间复杂度O(N^2)
     * 空间复杂度O(1)
     * 稳定
     * @param arr
     */
    public  static  void bubble(int [] arr){
        for (int i = 0; i < arr.length ; i++) {
            for (int j = 0; j < arr.length-1-i ; j++) {
                if(arr[j]>arr[j+1]){
                    swap(arr,j,j+1);
                }
            }
        }
    }

4.3:快速排序

任取待排序元素序列中的莫元素作为基准值,按照该排序列将排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止

4.3.1:hoare版(左右指针)

e2bdedd6f7884eb1bc824f6a2cc6cd2f.png

 /**
     * 快速排序
     * 左右指针找基准
     * 时间复杂度O(N*logN)
     * 空间复杂度O(logN)
     * 不稳定
     * @param arr
     */
    public static void quick(int [] arr){
        quickChild(arr,0,arr.length-1);
    }
    protected static void quickChild(int [] arrt ,int star,int end){
       if(star>=end){
           return;
       }
        int pivot=partition(arrt,star,end);
       quickChild(arrt,0,pivot-1);//这是基准前部分
       quickChild(arrt,pivot+1,end);//基准后部分
    }
    //找基准
    protected static int partition(int [] arr,int start ,int end){
        int pivot=start;
        int left=start;
        int right=end;
        while(left<right){
            if(arr[right]>arr[pivot]){
                right--;
            }
            if(arr[left]<arr[pivot]){
                left++;
            }
            //这时候right找到比基准小的数
            //left找到比基准大的数。两者进行交换
            swap(arr,left,right);
        }//left和right相遇
        //left和para交换
        swap(arr,left,pivot);
        //返回基准值
        return  pivot;
    }

4.3.2:挖坑法

612378e6242f4ae681bffa9cd297ee7b.png

 protected static  int partition2(int [] arr,int start,int end){
        int left=start;
        int tmp=arr[left];
        int right=end;
        while(left<right){
            while(left<right&&arr[right]>=tmp){
                right--;
            }//这时候 right比基准小
            arr[left]=arr[right];
            while(left<right&&arr[left]<=tmp){
                left++;
            }//这是left比基准大
            arr[right]=arr[left];
        }//left和right相遇
        arr[left]=tmp;
        return  left;//返回基准
    }

4.3.3:前后指针

3739388e712a4217a6eed452a9c9bc6d.png

  //前后指针
    protected static int  partition(int [] arr,int start,int end){
        int pivot=end;
        int prev=start-1;
        int cur=start;
        for (; cur <end ; cur++) {
            if(arr[cur]<=arr[pivot]){
                prev++;
                swap(arr,cur,prev);
            }//这时候cur走到了
        }
        swap(arr,prev+1,pivot);
        return (prev+1);
    }

4.4:快排的优化

4.4.1:三数取中--取中间数值做基准

 protected static void quickChild(int [] arrt ,int star,int end){
       if(star>=end){
           return;
       }
       //小区间的是,待排序的数组是几乎接近有序的。
        //这时候我们可以在小区间直接用插入排序
        if(end-star>=2){
            insertSort(arrt,star,end);
        }
       //三数取中
        int index=findMidValOfIndex(arrt,star,end);
        swap(arrt,star,index);
        int pivot=partition(arrt,star,end);
       quickChild(arrt,0,pivot-1);//这是基准前部分
       quickChild(arrt,pivot+1,end);//基准后部分
    }
    //三数取中
    private  static  int findMidValOfIndex(int [] arr,int start,int end){
        int mid=(end+start)/2;
        //大体分两种情况
        if(arr[start]>arr[end]){
            if(arr[mid]>arr[start]){
                return  start;
            }else if(arr[end]>arr[mid]){
                return  end;
            }else{
                return  mid;
            }
        }else{
            if(arr[mid]>arr[start]){
                return  mid;
            }else if(arr[mid]>arr[end]){
                return  end;
            }else{
                return  start;
            }
        }
    }

4.5:快排的非递归

 //快排的非递归
    public static  void noquick(int [] arr){
    Stack<Integer>s1=new Stack<>();
    int start=0;
    int end=arr.length-1;
    int index=partition2(arr,start,end);
    //判断左边的数字个数是否大于1;
        if(index-start>1){
            s1.push(start);//左边的头
            s1.push(index-1);//左边的尾
        }
        //判断右边的字数个数是否大于1
        if(end-index>1){
            s1.push(index+1);
            s1.push(end);
        }
        while(!s1.empty()){
            end=s1.pop();//栈是先进后出
            start=s1.pop();
            index=partition2(arr,start,end);
            //判断左边的数字个数是否大于1;
            if(index-start>1){
                s1.push(start);//左边的头
                s1.push(index-1);//左边的尾
            }
            //判断右边的字数个数是否大于1
            if(end-index>1){
                s1.push(index+1);
                s1.push(end);
            }
        }
    }

5.归并排序

4df074424f2c4b86aa5ac48f39c0e843.png

  /**
     * 归并排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(N)
     * wend
     * @param arr
     */
    public static  void merge(int [] arr){
        mergeChild(arr,0,arr.length-1);
    }
    public  static  void mergeChild(int [] arr,int start,int end){
        if(start==end){
            return;
        }//拆分数组
        int mid=(start+end)/2;
        mergeChild(arr,start, mid);
        mergeChild(arr,mid+1,end);
        //合并数组
        merge1(arr,start,mid,end);
    }

    protected static void merge1(int[] arr, int start, int mid, int end) {
        int s1 = start;
        int a1 = mid;
        int s2 = mid + 1;
        int a2 = end;
        int[] tmp = new int[end - start + 1];
        int count = 0;
        while (s1 <= a1 && s2 <= a2) {
            if (arr[s1] <= arr[s2]) {
                tmp[count++] = arr[s1++];
            } else {
                tmp[count++] = arr[s2++];
            }
        }
        while (s1 <= a1) {
            tmp[count++] = arr[s1++];
        }
        while (s2 <= a2) {
            tmp[count++] = arr[s2++];
        }//将数值拷贝到arr数组里面
        for (int i = 0; i < count; i++) {
            arr[i + start] = tmp[i];
        }
    }

}

5.1:海量数据的排序问题

前提:内存只有1G,需要排序的数据右10G

因为内存中无法把所有的数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序。

1.先把文件切成成10份,每份1G.

2.分别对每份排序,因为内存可以放的下,

3.进行2路归并,同时对200份有序的文件进行归并,最终有序

总结:

以上就是我总结的排序的知识点,如果有错误,请各位铁铁留言指教。

 

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值