数据结构之常见排序算法

目录

一、排序中的概念

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

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、基数排序

①思想:

个位数排序:

十位数排序:

百位数排序:

此时就已经有序了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/572093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

日志集中审计系列(5)--- LogAuditor接收USG设备日志

日志集中审计系列(5)--- LogAuditor接收USG设备日志 前言拓扑图设备选型组网需求配置思路操作步骤结果验证前言 近期有读者留言:“因华为数通模拟器仅能支持USG6000V的防火墙,无法支持别的安全产品,导致很多网络安全的方案和产品功能无法模拟练习,是否有真机操作的实验或…

文旅强势复苏 苏州金龙新V系客车助力湖北“文旅升级”

2024年4月24日&#xff0c;苏州金龙携多款新V系客车登陆素有九省通衢之称的湖北武汉&#xff0c;在当地文旅行业&#xff0c;刮起一场客运品质升级之风。作为“五一”出行热门城市、自然人文资源丰富的旅游大省&#xff0c;武汉乃至湖北旅游市场堪称客运产品的试金石&#xff0…

赵磊老师:共同的利益和文化理念契合才是两项留人的重要法宝

优秀人才对企业的发展与影响毋庸置疑&#xff0c;优秀人才的流失往往直接带来业绩的损失&#xff0c;甚至导致企业从此一蹶不振&#xff0c;当然一个组织也需要大量的执行者&#xff0c;通过执行者的辛劳工作&#xff0c;使其为客户创造价值的想法变成产品&#xff0c;变成产生…

shellshock题解思路分享

shellshock 考查bash远程任意代码执行漏洞。 可以看到含有bash文件&#xff0c;使用以下命令测试bash是否受shellshock影响 env x() { :;}; echo test ./bash -c:test受影响&#xff0c;执行bash远程任意代码执行漏洞 env x() { :;}; /bin/cat flag ./shellshock具体可以看…

海南封关怎么看?win战略会任志雄解析

今年海南自由贸易港建设也进入了新阶段:将在2025年年底前适时启动全岛封关运作,封关后的海南将以全新姿态迎接更广泛的发展机遇。 封关在即,企业有何感受?还有哪些准备工作?封关后的海南将呈现怎样的状态?近日,红星资本局记者深入实地了解海南自贸港如何成型起势。 利好当…

劳保工具佩戴监测识别摄像机

随着工业生产技术的不断进步和劳动保护意识的提高&#xff0c;劳保工具的佩戴已成为维护工人安全健康的重要环节。为了更好地监测和识别工人是否正确佩戴劳保工具&#xff0c;以及工作场所是否存在安全隐患&#xff0c;智能劳保工具佩戴监测识别摄像机应运而生。这种摄像机结合…

嵌入式Linux driver开发实操(十八):Linux音频ALSA开发

应用程序程序员应该使用库API,而不是内核API。alsa库提供了内核API 100%的功能,但增加了可用性方面的主要改进,使应用程序代码更简单、更美观。未来的修复程序或兼容性代码可能会放在库代码中,而不是放在内核驱动程序中。 使用ALSA API和libasound进行简单的声音播放: /*…

遥控车模的电机控制器

一、项目简介 基于CH32V103单片机结合RTT开发一套无刷电机无感矢量控制器&#xff0c;使用无感矢量控制无刷电机具有噪音小、控制线性度好、电机效率高等优点。使用三相全桥电路将直流电转换为交流电驱动无刷电机&#xff0c;利用串联电阻和差分采样电路采集UV两相的电流信号。…

智慧码头港口:施工作业安全生产AI视频监管与风险预警平台方案

一、建设思路 随着全球贸易的快速发展&#xff0c;港口作为连接海洋与内陆的关键节点&#xff0c;其运营效率和安全性越来越受到人们的关注。为了提升港口的运营效率和安全性&#xff0c;智慧港口视频智能监控系统的建设显得尤为重要。 1&#xff09;系统架构设计 系统应该采…

[计算机效率] 网站推荐:其它类(找台词、表格转换)

4.9 其它 4.9.1 找台词 找台词网&#xff08;http://www.zhaotaici.cn/&#xff09;是一个专为电影电视剧爱好者打造的台词搜索引擎网站。它的核心功能是帮助用户快速查找特定电影或电视剧中的台词&#xff0c;并提供详细的上下文信息。 在使用找台词网时&#xff0c;用户只需…

docker的默认路径存储不足

docker的默认路径存储不足 添加磁盘 [rootlocalhost ~]# fdisk -l磁盘 /dev/sda&#xff1a;42.9 GB, 42949672960 字节&#xff0c;83886080 个扇区 Units 扇区 of 1 * 512 512 bytes 扇区大小(逻辑/物理)&#xff1a;512 字节 / 512 字节 I/O 大小(最小/最佳)&#xff1a…

一网打尽 Rust 语法

❝ 悲观者永远正确&#xff0c;而乐观者永远前行 ❞ 大家好&#xff0c;我是「柒八九」。一个「专注于前端开发技术/Rust及AI应用知识分享」的Coder 前言 在之前的Rust学习专栏中&#xff0c;由于受制与文章的脉络&#xff0c;我们只能从概念到使用场景进行事无巨细的解释。相当…

伪分布Hadoop下安装Hive

一、下载并安装Mysql &#xff08;1&#xff09;下载mysql安装包&#xff08;mysql-8.0.26-1.el7.x86_64.rpm-bundle.tar&#xff09; 下载官网&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/ &…

【大模型书籍】从零开始大模型开发与微调:基于PyTorch与ChatGLM(附PDF)

哈喽各位&#xff0c;今天又来给大家分享大模型学习书籍了&#xff0c;今天是这本<从零开始大模型开发与微调&#xff1a;基于PyTorch与ChatGLM 书籍PDF分享>&#xff0c;大模型是深度学习自然语言处理皇冠上的一颗明珠&#xff0c;也是当前AI和NLP研究与产业中最重要的方…

解决IDEA中Tomcat控制台乱码问题(包括sout输出乱码)

文章目录 前言一、控制台直接输出乱码二、sout输出内容在控制台显示乱码 前言 今天在使用Tomcat的时候发现控制台输入出现了乱码问题&#xff0c;其实之前就出现过一次&#xff0c;解决了&#xff0c;但是新创建一个项目后又会出现sout的内容在控制台输出的乱码问题&#xff0…

【论文复现|智能算法改进】融合正余弦策略的算术优化算法

目录 1.算法原理2.改进策略3.结果展示4.参考文献 1.算法原理 【智能算法】算术优化算法&#xff08;AOA&#xff09;原理及实现 2.改进策略 基于适应度的自适应 MOA 策略 正弦余弦策略 3.结果展示 4.参考文献 [1] 黄学雨,罗华.融合正余弦策略的算术优化算法[J].计算机工…

分布式版本控制工具 Git 的使用方式

文章目录 Git简介下载安装基本使用起始配置Git 的三个区域基本操作流程查看仓库状态删除&#xff08;撤销暂存区&#xff09;差异对比查看版本日志版本回退修改提交日志分支概念&#xff1a;创建分支与切换分支合并分支&#xff08;快速合并&#xff09;合并分支&#xff08;提…

Linux的FTP服务

目录 1.什么是FTP服务&#xff1f; 2.FTP的工作原理和流程 1 主动模式 2 被动模式 3.搭建和配置FTP服务 1 下载服务包、备份配置文件 2 修改配置文件​编辑 3 匿名访问测试 4 设置黑白命令 1.什么是FTP服务&#xff1f; FTP&#xff08;file Transfer Protocol&#…

Java:二叉树(1)

从现在开始&#xff0c;我们进入二叉树的学习&#xff0c;二叉树是数据结构的重点部分&#xff0c;在了解这个结构之前&#xff0c;我们先来了解一下什么是树型结构吧&#xff01; 一、树型结构 1、树型结构简介 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>…

三角函数与其他复杂函数在C语言中的实现:CORDIC算法、泰勒公式、查表法与math库详解

在C语言中实现三角函数&#xff0c;通常有四种主要方法&#xff1a;CORDIC算法、泰勒公式展开、查表法以及直接调用C语言的标准数学库。接下来我们将详细介绍这四种方法&#xff0c;并探讨其他可能的补充实现手段。 1. CORDIC算法 CORDIC&#xff08;Coordinate Rotation Dig…