数据结构之常见排序算法

本文详细介绍了排序中的基本概念,以及常见的升序排序算法,包括插入排序、希尔排序、选择排序(单指针、双指针)、堆排序、交换排序(冒泡、双指针快速、挖洞快速、前后指针快速、优化版快速)、归并排序和非递归版本,以及计数排序、桶排序和基数排序。着重分析了每种算法的工作原理、代码实现和主要特性。
摘要由CSDN通过智能技术生成

目录

一、排序中的概念

二、常见排序算法--升序

1、插入排序

(1)直接插入排序

(2)希尔排序

2、选择排序

(1)单指针选择排序

(2)双指针选择排序

(3)堆排序

3、交换排序

(1)冒泡排序

(2)双指针快速排序

(3)挖洞快速排序

(4)前后指针快速排序

(5)优化版快速排序

(6)非递归快速排序

4、归并排序

(1)归并排序

(2)归并排序非递归

5、计数排序

6、桶排序

7、基数排序

一、排序中的概念

1、稳定性:在待排序的记录中,存在多个相同的关键字记录,若在排序前和排序后这些相同关键字的相对次序保持不变,则这种排序算法是稳定的,否则称为不稳定的。

二、常见排序算法--升序

1、插入排序
(1)直接插入排序

①思想:将要插入的数据和前面比较,要是前面的小,结束比较,要是前面的大,元素后移,再次比较,直到遇到较小的,元素插入。

②代码实现:

public static void insertSort(int[] a){
        int size=a.length;
        for(int i=1;i<size;i++){
            int j=i-1;
            int temp=a[i];  //后面会被覆盖
            for(;j>=0;j--){
                if(a[j]>temp){
                    a[j+1]=a[j];
                }else {
                    break;
                }
            }
            a[j+1]=temp;
        }
    }

③特性:

元素越接近有序,时间效率越高;

时间复杂度:O(n^2);空间复杂度:O(1);稳定性:稳定。

(2)希尔排序

①思想:对元素进行分组,如有10个元素,先分成5组(元素间隔为5),对着5组进行插入排序;再分成2组(元素间隔为2),对这2组进行插入排序;最后分成1组(元素间隔为1),对着1组进行插入排序,此时插入排序时元素已经接近有序了。

②代码实现:

public static void shellSort(int[] a){
        int size=a.length;
        while(size/2>=1){
            size/=2;
            shell(a,size);
        }
    }

    public static void shell(int[] a,int group){
        int size=a.length;
        for(int i=1;i<size;i++){
            int temp=a[i];
            int j=i-group;
            for(;j>=0;j-=group){
                if(a[j]>temp){
                    a[j+group]=a[j];
                }else{
                    break;
                }
            }
            a[j+group]=temp;
        }
    }

③特性:

是直接插入排序的优化。

时间复杂度:O(n^1.25)到O(1.6n^1.25);空间复杂度:O(1);稳定性:不稳定。

2、选择排序
(1)单指针选择排序

①思想:

从第一个数开始,每次找到待排序数的最小值,与第一个数交换位置;然后开始第二个数,每次找到待排序数的最小值,与第二个数交换位置......直到最后一个数结束。

②代码实现:

public static void selectSort(int[] a){
        int size=a.length;
        for(int i=0;i<size-1;i++){
            int min=i;
            for(int j=i+1;j<size;j++){
                if(a[j]<a[min]){
                    min=j;
                }
            }
            if(i!=min){
                swap(a,i,min);
            }
        }
    }
    public static void swap(int[] a,int m,int n){
        int temp=a[m];
        a[m]=a[n];
        a[n]=temp;
    }

③特性:

时间复杂度:O(n^2);空间复杂度:O(1);稳定性:不稳定。

(2)双指针选择排序

①思想:

一个指针指向要排序数的最小值,一个指针指向要排序数的最大值,最小值放左边,最大值放右边,往中间遍历,但需注意:最大值在要排序数第一个的情况。

②代码实现:

public static void selectSort2(int[] a){
        int size=a.length;
        int left=0;
        int right=size-1;
        while(left<right){
            int max=right;
            int min=left;
            for(int i=left;i<=right;i++){
                if(a[i]>a[max]){
                    max=i;
                }else if(a[i]<a[min]){
                    min=i;
                }
            }
            if(max==left){
                max=min;
            }
            if(min!=left){
                swap(a,min,left);
            }
            if(max!=right){
                swap(a,max,right);
            }
            left++;
            right--;
        }
    }
    public static void swap(int[] a,int m,int n){
        int temp=a[m];
        a[m]=a[n];
        a[n]=temp;
    }
(3)堆排序

①思想:

若是升序的话,建立大根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。

若是降序的话,建立小根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。

②代码实现:

public static void buildPile(int[] a){
        int size=a.length;
        int parent=(size-2)/2;
        for(int i=parent;i>=0;i--){
            buildChildPile(a,i,size);
        }
    }
    public static void buildChildPile(int[] a,int parent,int size){
        int child=2*parent+1;
        while(child<size){
            if(child+1<size&&a[child]<a[child+1]){
                child=child+1;
            }
            if(a[child]>a[parent]){
                swap(a,child,parent);
            }
            parent=child;
            child=2*parent+1;
        }
    }
    public static void pileSort(int[] a){
        int size=a.length-1;
        while(size!=0){
            swap(a,0,size);
            buildChildPile(a,0,size-1);
            size--;
        }
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(1);稳定性:不稳定。

3、交换排序
(1)冒泡排序

①思想:

将大的往左边放,每次遇到一个数,比后面数大,就往后交换。

②代码实现:

   public static void bubbleSort(int[] a){
        int size=a.length;
        boolean flag=true;
        for(int i=0;i<size-1;i++){
            for(int j=1;j<size-i;j++){
                if(a[j-1]>a[j]){
                    swap(a,j-1,j);
                    flag=false;
                }
            }
            if(flag){
                break;
            }
        }
    }

③特性:

时间复杂度:O(n^2);空间复制度:O(1);稳定性:稳定。

(2)双指针快速排序

①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,left大于基准,两者交换,循环进行到left<right不满足,基准值要和最后一个left交换,接着划分区间进行判断,直到区间里只有一个元素结束。

②代码实现:

public static void quickSort(int[] a){
        quickChild(a,0,a.length-1);
    }
    public static void quickChild(int[] a,int left,int right){
        if(left>=right){  //一个元素
            return;
        }
        int index=index(a,left,right); //进行交换,并找到基准值的位置
        quickChild(a,left,index-1);  //左区间接着判断
        quickChild(a,index+1,right);  //右区间接着判断
    }
    public static int index(int[] a,int left,int right){
        int temp=a[left];
        int k=left;
        while (left<right){
            while(left<right&&a[right]>=temp){
                right--;
            }
            while(left<right&&a[left]<=temp){
                left++;
            }
            swap(a,left,right);
        }
        swap(a,k,left);
        return  left;   //left后走的 right一定是走到了小于等于temp的地方
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(3)挖洞快速排序

①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,right和left交换;如果left大于基准,left和right交换,循环进行到left<right不满足,基准值放在最后一个left,接着划分区间进行判断,直到区间里只有一个元素结束。

②代码实现:

public static void quickSort2(int[] a){
        quickChild2(a,0,a.length-1);
    }
    public static void quickChild2(int[] a,int left,int right){
        if(left>=right){
            return;
        }
        int index=index2(a,left,right);
        quickChild2(a,left,index-1);
        quickChild2(a,index+1,right);
    }
    public static int index2(int[] a,int left,int right){
        int temp=a[left];
        while(left<right){
            while(left<right&&a[right]>=temp){
                right--;
            }
            swap(a,left,right);
            while(left<right&&a[left]<=temp){
                left++;
            }
            swap(a,left,right);
        }
        a[left]=temp;
        return left;
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(4)前后指针快速排序

①思想:前指针pre为left,后指针cur为前指针+1,当cur<=right时,若指针left的值大于指针cur的值,且++pre指针的值不等于cur,交换cur与pre的值,否则不交换;cur++;循环结束后交换left与pre的值。  ----琢磨

②代码实现:

 public static void quickSort3(int[] a){
        quickChild3(a,0,a.length-1);
    }
    public static void quickChild3(int[] a,int left,int right){
        if(left>=right){
            return;
        }
        int index=index3(a,left,right);
        quickChild3(a,left,index-1);
        quickChild3(a,index+1,right);
    }
    public static int index3(int[] a,int left,int right){
        int pre=left;
        int cur=pre+1;
        while(cur<=right){
            if(a[left]>a[cur]&&a[++pre]!=a[cur]){
                swap(a,pre,cur);
            }
            cur++;
        }
        swap(a,left,pre);
        return pre;
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(5)优化版快速排序

①思想:长度如果小于等于5采用直接插入排序;基准值不再默认为left的值,选择left、right、mid三者的中间值。

②代码实现:

 public static void quickSort4(int[] a){
        int size=a.length;
        if(size<=5){
            insertSort(a);
        }else{
            quickChild4(a,0,size-1);
        }
    }
    public static void quickChild4(int[] a,int left,int right){
        if(left>=right){
            return;
        }
        int mid=findMid(a,left,right);  
        swap(a,mid,left);  //改变基准值
        int index=index4(a,left,right);
        quickChild4(a,left,index-1);
        quickChild4(a,index+1,right);
    }
    public static int index4(int[] a,int left,int right){
        int temp=a[left];
        while(left<right){
            while(left<right&&a[right]>=temp){
                right--;
            }
            swap(a,left,right);
            while(left<right&&a[left]<=temp){
                left++;
            }
            swap(a,left,right);
        }
        a[left]=temp;
        return left;
    }
    public static int findMid(int[] a,int left,int right){
        int mid=(left+right)/2;
        if(a[left]>a[right]){
            if(a[mid]<a[right]){
                return right;
            }else if(a[mid]>a[left]){
                return left;
            }else {
                return mid;
            }
        }else{
            if(a[mid]<a[left]){
                return left;
            }else if(a[mid]>a[right]){
                return right;
            }else {
                return mid;
            }
        }
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

(6)非递归快速排序

①思想:和双指针思想一样,只是此时将left和right的值放进了队列中,只要队列不为空,再拿出来进行交换并获取下标。

②代码实现:

 public static void quickSort5(int[] a){
        int right=a.length-1;
        int left=0;
        if(right-left>1){
            quickChild5(a,left,right);
        }
    }
    public static void quickChild5(int[] a,int left,int right){
        Queue<Integer> queue=new LinkedList<>();
        queue.offer(left);
        queue.offer(right);
        while (!queue.isEmpty()){
            int start=queue.poll();
            int end=queue.poll();
            int index=index(a,start,end);
            if(index-1>start){
                queue.offer(start);
                queue.offer(index-1);
            }
            if(right>index+1){
                queue.offer(index+1);
                queue.offer(right);
            }
        }
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。

4、归并排序
(1)归并排序

①思想:不断划分区间,直到区间内只有一个元素时结束,然后在区间内进行排序(类似于双指针中的滑动窗口,指针指的数谁小,数往前放,指针后移)合并。

②代码实现:

 public static void mergeSort(int[] a){
        mergeChild(a,0,a.length-1);
    }
    public static void mergeChild(int[] a,int left,int right){
        if(left>=right){
            return;
        }
        int mid=(left+right)/2;
        mergeChild(a,left,mid);  //左边拆分
        mergeChild(a,mid+1,right);  //右边拆分
        merge(a,left,mid,right);  //合并
    }
    public static void merge(int[] a,int left,int mid,int right){
        int s1=left;
        int e1=mid;
        int s2=mid+1;
        int e2=right;
        int[] temp=new int[right-left+1];
        int k=0;
        while(s1<=e1&&s2<=e2){
            if(a[s1]<a[s2]){
                temp[k++]=a[s1++];
            }else {
                temp[k++]=a[s2++];
            }
        }
        if(s1>e1){
            while(s2<=e2){
                temp[k++]=a[s2++];
            }
        }else {
            while(s1<=e1){
                temp[k++]=a[s1++];
            }
        }
        for(int i=0;i<temp.length;i++){
            a[i+left]=temp[i]; //a是从left开始的 temp是从0开始的
        }
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。

(2)归并排序非递归

①思想:每次直接求出left,right,mid,然后调用方法进行排序。left根据分组元素求出,mid=left+group-1,right=mid+group。

②代码实现:

 public static void mergeSort2(int[] a){
        int group=1;  //区间内元素个数:group+1
        int size=a.length;
        while(group<size){
            for(int i=0;i<size;i+=group*2) { //left值
                int left=i;
                int mid=left+group-1;
                if(mid>size-1){  //区间里元素个数不够凑成一个group 可能出现mid越界
                    mid=size-1;
                }
                int right=mid+group;
                if(right>size-1){
                    right=size-1;
                }
                merge(a,left,mid,right);  //进行合并
            }
            group*=2;
        }
    }

③特性:

时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。

5、计数排序

①思想:统计每个数据出现的次数,并对应下标按照顺序存储,按照顺序再放入排序数组。

②代码实现:

public static void countSort(int[] a){
        int size=a.length;
        int min=a[0];
        int max=a[0];
        for(int i=0;i<size;i++){
            if(a[i]>max){
                max=a[i];
            }else if(a[i]<min){
                min=a[i];
            }
        }
        int[] count=new int[max-min+1];
        for(int i=0;i<size;i++){
            count[a[i]-min]++;
        }
        int j=0;
        for(int i=0;i<count.length;i++){
            while (count[i]>0){
                a[j++]=i+min;
                count[i]--;
            }
        }
    }

③特性:

时间复杂度:O(n);空间复杂度:O(范围);稳定性:稳定

6、桶排序

①思想:

7、基数排序

①思想:

个位数排序:

十位数排序:

百位数排序:

此时就已经有序了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ambition…

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值