常见排序方法详解(图示+方法)

news2024/11/15 8:56:45

一、插入排序

1.1基本思想

把待排序的记录 按其关键码值的大小逐个插入到一个已经排好序的有序序列中 ,直到所有的记录插入完为止,得到 一个新的有序序列。

1.2直接插入排序

当插入第 i(i>=1) 个元素时,前面的 array[0],array[1],…,array[i-1] 已经排好序,此时用 array[i] 的排序码与 array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将 array[i] 插入,原来位置上的元素顺序后移。

示例代码如下:

class Sort{
    public static void insertSort(int[] array){
        //排序完成条件
        for (int i = 1; i < array.length ; i++) {
            //用于比较大小
            int tmp = array[i];
            //j的起始位置
            int j =i-1;
            //单次比较的结束条件
            for(;j>=0;j--){
                if(array[j]>tmp){
                    array[j+1]=array[j];
                }else{
                    //由于前面的元素经排序后已经有序,故直接将tmp的值放入对应位置后,j无需在遍历前面的元素进行比较,
                    //直接跳出循环即可。
                    array[j+1]=tmp;
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }
}

直接插入排序的特性总结:
1. 元素集合 越接近有序 ,直接插入排序算法的时间 效率越高
2. 时间复杂度: O(N^2)
    最坏情况:数据是逆序的;
    最好情况:数据本身就是有序的;
    结论:当一组数据 越接近于有序 ,直接插入排序 越快
3. 空间复杂度: O(1) ,它是一种 稳定 的排序算法
4. 稳定性:稳定

1.3希尔排序(缩小增量排序)

希尔排序法又称缩小增量法。希尔排序法的基本思想是: 先选定一个整数,把待排序文件中所有记录分成多个组, 所有距离为指定的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 =1 时,所有记录在统一组内排好序

思路有了,代码如何实现?如何分组? 

代码如下:

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

    private static void shell(int[] array, int gap) {
        for (int i = gap; i <array.length ; i++) {
            //增量为i++,使排序过程分组交替进行
            int tmp = array[i];
            int j = i-gap;
            for (; j >=0 ; j-=gap) {
                if(array[j]>tmp){
                    array[j+gap]=array[j];
                }else{
                    array[j+gap]=tmp;
                    break;
                }
            }
            array[j+gap]=tmp;
        }
    }
希尔排序的特性总结:
1. 希尔排序是 对直接插入排序的优化
2. gap > 1时都是预排序 ,目的是让数组更 接近于有序 。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为 gap 的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定
4. 稳定性: 不稳定

二、选择排序

2.1直接选择排序

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


2.2.2代码实现

图示:

代码演示:

public static void selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            //每轮比较默认i下标的值为最小值
            int minIndex=i;
            for (int j =i+1; j <array.length ; j++) {
                if(array[j]<array[minIndex]){
                    minIndex=j;
                }
            }
            int tmp=array[i];
            array[i]= array[minIndex];
            array[minIndex]=tmp;
        }
    }
直接选择排序的特性总结
1. 直接选择排序思考非常好理解,但是 效率不是很好 。实际中 很少使用
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1)
4. 稳定性: 不稳定

2.2堆排序

堆排序思路在前面已经了解到,详情见文章中“堆的应用”部分。

https://blog.csdn.net/zhakakqns/article/details/141531282?spm=1001.2014.3001.5501icon-default.png?t=N7T8https://blog.csdn.net/zhakakqns/article/details/141531282?spm=1001.2014.3001.5501

 public static void heapSort(int[] array){
        createHeap(array);

        int end=array.length-1;
        while(end>0) {
            int tmp = array[0];
            array[0] = array[end];
            array[end] = tmp;
            siftDown(array, 0, end);
            end--;
        }
    }
   private static void createHeap(int[] array){
        //创建堆
       for (int parent = (array.length-1-1)/2; parent >=0 ; parent--) {
           siftDown(array,parent,array.length);
       }
   }
   private static void siftDown(int[] array,int parent,int length){
        //向下调整
        int child = parent+1;
        while(child<length){
            if(child+1<length && array[child]<array[child+1]){
                child++;
            }
            if(array[child]>array[parent]){
                int tmp=array[parent];
                array[parent]=array[child];
                array[child]=tmp;
                parent=child;
                child=2*parent+1;
            }else{
                break;
            }
        }
   }

直接选择排序的特性总结
1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(1)
4. 稳定性: 不稳定

 三、交换排序

3.1冒泡排序

3.1.1普通冒泡排序

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

 3.1.2冒泡排序优化

如果一组数据本身有序,或者在经过几趟冒泡排序后已经有序,就可以直接退出,不用继续执行代码。

 public static void bubbleSort(int[] array){
       for (int i = 0; i <array.length-1 ; i++) {
           boolean flg=false;//用于判断是否发生交换,若没有交换,则已经有序直接退出
           for (int j = 0; j < array.length-i-1; j++) {
               if(array[j]>array[j+1]){
                   int tmp=array[j];
                   array[j]=array[j+1];
                   array[j+1]=tmp;
                   flg=true;//发生交换,置为true
               }
           }
           if(!flg){
               break;
           }
       }
   }

!!!一般我们所说的冒泡排序的时间复杂度是指未优化过的冒泡排序。 

冒泡排序的特性总结
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1)
4. 稳定性: 稳定

3.2快速排序

3.2.1快速排序

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

3.2.1.1Hoare版

代码实现:

 public static void quickSort(int[] array){
        quick(array,0,array.length-1);
   }
   private static void quick(int[] array,int start,int end){
        if(start>=end){
            return;
        }
           
        //划分区域
        int pivot = partition(array,start,end);
        //递归左边
        quick(array,start,pivot-1);
        //递归右边
        quick(array,pivot+1,end);
   }

    private static int partition(int[] array, int left, int right){
        //以left下标的值作为基准
        int key = left;
        while(left<right) {
            //如果数据为3,4,5,6那么right一直--也找不到比3大的数据
            //,会导致越界,因此必须右left<right这个条件
            while (left<right && array[right] >= array[key]) {
                right--;
            }
            while (left<right && array[left] <= array[key]) {
                left++;
            }
            int tmp = array[right];
            array[right] = array[left];
            array[left] = tmp;
        }
        int tmp = array[left];
        array[left]=array[key];
        array[key]=tmp;
        return left;
    }
特性总结】
1. 时间复杂度: O(N^2)
当数据本身有序时,时间复杂度为n^2
最好情况 N*logN
2. 空间复杂度: O(N)
3. 稳定性: 稳定

 3.2.1.2挖坑法 

递归的思路和Hoare法一样,也就是说只需要修改Hoare法的partition方法即可。

代码如下:

private static int partition(int[] array, int left, int right){
        int tmp = array[left];
        while(left<right) {
            while (left<right && array[right] >= tmp) {
                right--;
            }

            array[left] = array[right];

            while (left<right && array[left] <= tmp) {
                left++;
            }

            array[right] = array[left];
        }

        array[left] = tmp;
        return left;
    }

3.2.2快速排序优化

当一组数据有序时,快速排序的时间复杂度会达到O(N^2),并且当数据量过多时还会导致栈溢出,因此我们右以下几种优化方法:

(1)三数取中法选key 

下面是寻找中间大小数据的方法:

 private static int getMiddleNum(int[] array,int left,int right){
        int mid = (left+right)/2;
        if(array[left]<array[right]){
            if(array[mid]<array[left]){
                return left;
            }else if(array[mid]>array[right]){
                return right;
            }else{
                return mid;
            }
        }else{
           if(array[mid]>array[left]){
               return left;
           }else if(array[mid]<array[right]){
               return right;
           }else{
               return mid;
           }
        }

(2)递归到小的子区间时,可以考虑使用插入排序 ,但是在使用插入排序时,应注意排序的范围

i要从start+1开始,当j<start时结束该轮插入。

         代码如下:

 private static void quick(int[] array,int start,int end){
        if(start>=end){
            return;
        }
        //当数据小于10个时,使用直接插入排序
        if(end-start+1<10){
            insertSortRange(array,start,end);
            return;
        }
        int pivot = partition(array,start,end);
        
        quick(array,start,pivot-1);
        
        quick(array,pivot+1,end);
   }

    private static void insertSortRange(int[] array, int start, int end) {
        for (int i = start+1; i <=end ; i++) {
            int tmp=array[i];
            int j=i-1;
            for(;j>=start;j--){
                if(array[j]>tmp){
                    array[j+1]=array[j];
                }else{
                    array[j+1]=tmp;
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }

3.2.3快速排序非递归

 

代码如下: 

public static void quickNor(int[] array,int start,int end){
        //首先先用pivot将两个子数组的start,end放入栈中
        int pivot = partition(array,start,end);
        Stack<Integer> stack = new Stack<>();
        //如果pivot<=start+1,pivot>=end-1说明只剩下一个数据,则已经有序,不用再排序
          if(pivot>start+1){
              stack.push(start);
              stack.push(pivot-1);
          }
          if(pivot<end-1){
              stack.push(pivot+1);
              stack.push(end);
          }

          while(!stack.isEmpty()){
              end=stack.pop();
              start=stack.pop();
              pivot=partition(array,start,end);
              if(pivot>start+1){
                  stack.push(start);
                  stack.push(pivot-1);
              }
              if(pivot<end-1){
                  stack.push(pivot+1);
                  stack.push(end);
              }
          }
    }

3.2.4快速排序总结
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫 快速 排序
2. 时间复杂度: O(N*logN) (我们平时所说的快速排序的时间复杂度都是指优化过的)
3. 空间复杂度: O(logN)
4. 稳定性: 不稳定

四、归并排序

归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用 分治法 (Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即 先使每个子序列有序,再使子序列段间有序 。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
public static void mergeSort(int[] array){
        mergeSortTmp(array,0,array.length-1);
    }


    private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = (left+right)/2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        //分解完毕,开始合并
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        //相当于合并连个有序数组
        int[] tmp=new int[right-left+1];
        int k=0;
        int s1=left;
        int e1=mid;
        int s2=mid+1;
        int e2=right;

        while(s1<=e1 && s2<=e2){
            if(array[s1]<=array[s2]){
                tmp[k++]=array[s1++];
            }else{
                tmp[k++]=array[s2++];
            }
        }

        while(s1<=e1){
            tmp[k++]=array[s1++];
        }
        while(s2<=e2){
            tmp[k++]=array[s2++];
        }
        for (int i = 0; i < k; i++) {
            array[i+left]=tmp[i];
        }
    }
1. 归并的缺点在于需要 O(N) 的空间复杂度,归并排序的思考更多的是 解决在磁盘中的外排序 问题。
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(N)

4. 稳定性:稳定

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大文件上传vue插件vue-simple-uploader

https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html

springboot-es(elasticsearch)搜索项目

目标界面 html页面 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>途牛旅游</title><link rel"stylesheet" href"https://a.amap.com/jsapi_demos/static/demo-center/css/d…

windows安装php7.4

windows安装php7.4 1.通过官网下载所需的php版本 首先从PHP官网&#xff08;https://www.php.net/downloads.php&#xff09;或者Windows下的PHP官网&#xff08;http://windows.php.net/download/&#xff09;下载Windows版本的PHP安装包。下载后解压到一个路径下。 2.配…

2024/9/4 Canlink配置介绍与常见故障排查

双击一个站进去配置&#xff0c;如果双击PLC则是PLC往外面发数据&#xff0c;双击伺服&#xff0c;则是伺服往外发数据。 例如我想读伺服的功能吗&#xff1f; 点击伺服的配置 将0b00的地址数据发给PLC&#xff08;D100&#xff09; ,寄存器长度是一个 然后下载程序即可

使用docker安装jenkins,然后使用jenkins本地发版和远程发版

使用docker安装jenkins&#xff0c;然后使用jenkins本地发版和远程发版 1、安装docker 1.安装必要的一些系统工具 sudo yum install docker-ce 2.添加软件源信息 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 3.更新…

电子行业最全【芯片标签二维码】知识剖析

电子行业最全【芯片标签二维码】知识剖析 本文为辰逸绅士小编原创&#xff0c;未经许可请勿私下复制转载 长 文 预 警 目录 ★01--------前言 ★02--------关于电子元器件协会ECIA ★03--------关于矩阵二维码 3.1--------矩阵二维码 构成 3.2--------矩阵二维码 种类 3.…

【数学分析笔记】第3章第1节 函数极限(3)

3. 函数极限与连续函数 3.1 函数极限 3.1.1 函数极限的性质 【局部有界性】若 lim ⁡ x → x 0 f ( x ) A \lim\limits_{x\to x_{0}}f(x)A x→x0​lim​f(x)A&#xff0c;则 ∃ δ > 0 , ∀ x ( 0 < ∣ x − x 0 ∣ < δ ) : m ≤ f ( x ) ≤ M \exists \delta>…

BUUCTF Crypto wp--RSA1

第一步 查看下载文件 我们发现出现了dp dq&#xff0c;属于dp、dq泄露攻击 上述方程本来是用于在加密中进行快速解密的&#xff0c;但是如果二者发生泄露&#xff0c;就有可能进行对密文的解密。 当我们知道了 dp、 dq、p、q、c,在不知道e的情况下&#xff0c;也可以求解明文。…

社交达人秘籍:巧妙维护你的人脉关系!

在这个人脉为王的时代&#xff0c;微信不仅连接了亲朋好友&#xff0c;更成为了拓展职业网络、深化人际关系的重要平台。如何巧妙地在微信上维护并优化你的人脉关系&#xff0c;成为了每位社交达人必修的功课。今天&#xff0c;就让我们一起探索那些让社交关系更加稳固与活跃的…

【DSP】无法在线仿真无法进入main()函数

DSP无法在线仿真无法进入main()函数 1.问题描述 ​ 接手前人的DSP代码&#xff0c;硬件平台是DSP C6701&#xff0c;软件IDE是CCS 12.3.0。仿真器版本是XDS 560V2。 ​ 在进行在线仿真时&#xff0c;经常出现“伪在线”的情况。简单来说&#xff0c;正常的在线过程&#xff…

使用Python的Elasticsearch客户端 elasticsearch-py 来完成删除现有索引、重新创建索引并测试分词的示例代码

以下是一个使用Python的Elasticsearch客户端 elasticsearch-py 来完成删除现有索引、重新创建索引并测试分词的示例代码 一、安装依赖 pip install elasticsearch二、运行效果 三、程序代码 from elasticsearch import Elasticsearch, NotFoundError# 连接到Elasticsearch es…

基本滤波器响应(低通+高通+带通+带阻)+滤波器的响应特性(阻尼系数+截止频率下降率)

2024-9-4&#xff0c;星期三&#xff0c;20:40&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;多云。又是上班的一天&#xff0c;至于心情为什么多云&#xff0c;是因为女朋友换季感冒了&#xff0c;我有上班回不去&#xff0c;难受&#xff0c;赶紧到周五吧&…

多模态:DetCLIPv3解析

文章目录 前言一、介绍二、方法2. Open vocabulary detector2.1 encoder2.2 decoder 3. Object captioner 三、数据工程1. Auto-annotation data pipeline 四、训练策略五、实验总结 前言 目前多模态目标检测逐渐成为检测领域的主要发力方向&#xff0c;从最初的检测大模型gro…

2024年最新版Ajax+Axios 学习【包含原理、Promise、报文、接口等...】

基础知识 AJAX概念 AJAX概念&#xff1a;是浏览器与服务器进行数据通信的技术。 认识URL 定义&#xff1a;统一资源定位符&#xff0c;简称网址&#xff0c;用于访问网络上的资源。 组成&#xff1a; http协议&#xff1a;超文本传输协议&#xff0c;规定浏览器和服务器之…

C++系统教程001

1. 安装 Dev-C编程软件 2. 熟悉 Dev-C的界面 3. cout 输出语句的使用 4. 学会 C程序的编译运 一、认识编译器 我们平时所说的程序&#xff0c;一般指双击后就可以直接运行的程序&#xff0c;这样的程序又称为可执行程序。Windows系统下&#xff0c;可执行程序的后缀一般为.ex…

【C++】手动实现栈的封装

完成了以下功能的封装 目录 代码实现&#xff1a; 输出结果如下&#xff1a; 代码实现&#xff1a; #include <iostream> #include <cstring>using namespace std;class Static { private:int *arr; //动态分配栈int top; //指向栈顶元素i…

Docker中的容器内部无法使用vi命令怎么办?

不知道你是否遇到过,在修改容器内部的配置的时候,有时候会提示vi命令不可用。尝试去安装vi插件,好像也不是很容易,有什么办法可以帮助我们修改这个配置文件呢? 解决办法 这时候,我们就需要用到docker cp 命令了,它可以帮助我们把容器内部的文件复制到宿主机上,也可以将…

深入探究 RocketMQ:分布式消息中间件的卓越之选》

《深入探究 RocketMQ&#xff1a;分布式消息中间件的卓越之选》 一、引言 在当今复杂的网络通讯环境中&#xff0c;传统的 Http 请求同步方式存在诸多弊端。当客户端与服务器进行通讯时&#xff0c;客户端必须等待服务端完成处理后返回结果才能继续执行&#xff0c;这种同步调…

万字详解 Redis

1 Redis 是什么 1.1 定义 Redis是一种开源的、基于内存的数据结构存储系统&#xff0c;可以用作数据库、缓存、消息队列等。它支持多种数据结构&#xff0c;如字符串&#xff08;String&#xff09;、哈希&#xff08;Hash&#xff09;、列表&#xff08;List&#xff09;、集…

黑马点评2——商户查询缓存(P37店铺类型查询业务添加缓存练习题答案)redis缓存、更新、穿透、雪崩、击穿、工具封装

文章目录 什么是缓存&#xff1f;添加Redis缓存店铺类型查询业务添加缓存练习题 缓存更新策略给查询商铺的缓存添加超时剔除和主动更新的策略 缓存穿透缓存空对象布隆过滤 缓存雪崩解决方案 缓存击穿解决方案基于互斥锁方式解决缓存击穿问题基于逻辑过期的方式解决缓存击穿问题…