[Collection与数据结构] 七大排序算法汇总

news2024/11/24 20:48:17

1.概述

1.1 概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

1.2 常见应用

  1. 高校排行
    在这里插入图片描述
  2. 商品排序
    在这里插入图片描述

2. 常见排序

2.1 直接插入排序

从序列的第二个位置开始为基准,把前面的一个元素依次与下一个元素比较,前一个元素大于后一个元素(升序),则交换,如果碰到小于的,则证明前几个元素已经有序了,因为是从第二个元素开始进行插入排序的,直接break掉.
动态演示:
在这里插入图片描述

/**
     * 插入排序
     * @param array 传入的数组
     */
    public static void insertSort(int[] array){
        for (int i = 1; i < array.length; i++) {//基准从1开始
            for (int j = i-1; j >= 0; j--) {
                if (array[j] > array[j+1]){//把前面的元素依次比较
                    swap(j,j+1,array);
                }else {
                    break;//遇到不符合条件的直接break
                }
            }
        }
    }
    private static void swap(int a,int b,int[] array){
        int tmp = array[a];
        array[a] = array[b];
        array[b] = tmp;
    }

特性总结:

  1. 元素越趋于有序,插入排序越快,因为遇到比较不符合条件的直接回break掉.
  2. 时间复杂度:两层循环,O(n2)
  3. 空间复杂度:只在数组本身上进行了操作,O(1)
  4. 稳定性:稳定

2.2 希尔排序

希尔排序实际上是直接插入排序的一种升级版,先把序列中的元素按照一定的间隔个数分成多个组,之后把各个组中的元素进行排序,之后缩小缩小间隔个数,也就是缩小组数,增大各个组中的元素个数,再次排序,直到元素之间的间隔小于1为止.
动态演示:
在这里插入图片描述

 /**
     * 希尔排序,本质上是插入排序的一种升级版
     * @param array
     */
    public static void shellSort(int[] array){
        int gap = array.length;
        while (gap > 1){//数据间隙大于1时继续希尔排序
            gap = gap/2;//缩小数据间隙
            shell(array,gap);
        }
    }
    private static void shell(int[] array,int gap){
        for (int i = gap; i < array.length; i++) {//针对每一组数据进行插入排序
            for (int j = i-gap; j >= 0; j-=gap) {//只不过每次的步数变成了gap
                if (array[j] > array[j+gap]){
                    swap(j,j+gap,array);
                }else {
                    break;
                }
            }
        }
    }

其实我们观察上述代码,我们为什么说他是直接插入排序的升级版,是因为,它相比直接插入排序只是改变了每次走的步数.

特性总结:

  1. 希尔排序是针对直接插入排序的优化
  2. 当gap>1时都是预排序,目的是让数据越来越接近有序.当gap==1的时候,就是直接插入排序,这时数据已经趋于有序了,直接插入排序就会特别快.
  3. 时间复杂度:目前仍然存在争议,一般认为是O(n1.25~1.6n1.25)
  4. 稳定性:不稳定

2.3 选择排序

从第一个元素开始,寻找比基准元素小的元素,下标保存到tmp中,之后继续寻找比tmp小的元素,直到找到最小的元素为止,之后交换基准元素和tmp下标的元素,基准后移,一些此类推.
动态演示:

在这里插入图片描述

/**
     * 选择排序
     * @param array
     */
    public static void selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            int min = i;//最小值的下标赋值为i,如果没有找到比min下标小的,说明i就是最小的
            int j = i+1;
            for (; j < array.length; j++) {
                if (array[min] > array[j]){
                    min = j;
                }
            }
            swap(min,i,array);
        }
    }

特性总结:

  1. 时间复杂度:O(n2)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定
  4. 这种排序方法虽然好理解,但是由于效率太低,日常开发中很少用.

2.4 冒泡排序

之所以叫冒泡排序,是因为就像水中的气泡一样,一点一点地浮出水面.先确定排序的趟数(下标最大值),就是外层循环,再确定排序交换的次数(下标最大值-i),就是内层循环,遇到比自己小的就交换,这样最大值就像冒泡一样排到了最后.
优化: 如果发现在内层循环遍历的时候,一次都没有交换,说明已经有序,我们通过flag来标记.
动态演示:
在这里插入图片描述

/**
     * 冒泡排序
     * @param array
     */
    public static void bubbleSort(int[] array){
        for (int i = 0; i < array.length-1; i++) {//如果遍历到最后一个元素,就没有必要比较了,就要-1
            boolean flag = true;
            for (int j = 0; j < array.length-i-1; j++) {//这里必须-1,因为如果不-1,就会遍历到最后一个元素,j+1就会越界
                if (array[j] > array[j+1]){
                    swap(j+1,j,array);
                    flag = false;//发生了交换.说明不有序,设置为false
                }
            }
            if (flag){//如果flag == true,说明没有发生交换,整个数组已经有序
                return;//直接返回
            }
        }
    }

特性总结:

  1. 时间复杂度:O(n2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

2.5 堆排序

首先如果是升序的话,先创建一个大根堆,让end指向最后一个元素为什么不创建小根堆呢?是因为小根堆只可以保证父节点是最小的,而不可以保证子节点是从小到大排列的.之后把堆顶元素和堆尾元素交换,就把整个堆的最大元素排到了最后.之后继续把该堆向下调整为大根堆,之后让end向前移动
动态演示:
在这里插入图片描述

/**
     * 堆排序,从小到大排序,创建大根堆
     * @param array
     */
    public static void heapSort(int[] array){
        int end = array.length-1;
        createBigHeap(array);//先创建大根堆
        while (end > 0){
            swap(0,end,array);//交换堆顶元素和最后一个元素,把堆顶最大的元素放在最后
            end--;//end向前移动
            shiftDown(0,end,array);//再次向下调整为大根堆
            //先向下调整,再--,因为向下调整的时候,while循环没有等号,如果传入end-1,倒数第二个结点就调整不到
        }
    }
    private static void createBigHeap(int[] array){
        for (int i = (array.length-1-1)/2; i >= 0 ; i--) {
            shiftDown(i,array.length-1,array);
        }
    }
    private static void shiftDown(int parent,int end,int[] array){
        int child = parent*2+1;
        while (child <= end){
            if (child+1 < end && array[child] < array[child+1]) {
                child++;
            }
            if (array[child] > array[parent]){
                swap(parent,child,array);
                parent = child;
                child = child*2+1;
            }else {
                break;
            }
        }
    }

特点总结:

  1. 堆排序适用于海量数据排序,数据越多,堆排序效率越高.
  2. 时间复杂度:复杂度主要集中于向下调整中,元素个数x树的高度,O(n*log2n)
  3. 空间复杂度:没有额外空间,O(1)
  4. 稳定性:不稳定

2.6 快速排序(重点)

之所以叫快速排序,说明它是真的快.快速排序整体思想为分治思想,就是把通过递归的思想把整个数组通过一定的方法切成二叉树的形式,之后对每科子树进行排序.
快速排序的方法有两种,一种是霍尔法,一种是挖坑法.

2.6.1 霍尔法

分别定义start和end指向数组首和尾,选取第一个元素为key,之后先让end向前移动,找到比key小的元素,之后再让start向后移动,找到比key大的元素,之后交换end和start下标的值,以此类推.直到start和end相遇,把相遇位置的元素和key交换,之后以相遇点分治,递归以此类推.
动态演示:
在这里插入图片描述

/**
     * 快速排序,整体思想:把小的往前放,把大的往后放
     * @param array
     */
    public static void quickSort(int[] array){
        quick(array,0,array.length-1);//规定开始和结束位置
    }
    private static void quick(int[] array,int s,int e){
        int left = s;
        int right = e;
        if (left >= right){//当左下标大于右下标的时候,说明递归完成,直接返回
            return;
        }
        swap(left,mid,array);//把区间内的中间值放在头
        int pos = position(left,right,array);//通过霍尔法找到分治点
        quick(array,left,pos-1);
        quick(array,pos+1,right);//以分制点为基准,向两侧递归
    }
     /**
     * 霍尔法找到相遇位置
     * @param start 开始位置
     * @param end 结束位置
     * @param array 原始数组
     * @return
     */
    private static int position(int start,int end,int[] array){
        int tmp = array[start];
        int i = start;//提前准备比较数据的下标
        while (start < end){
            while (start < end && array[end] >= tmp){//先走后面,注意加上等号
                end--;
            }
            while (start < end && array[start] <= tmp){//加前一个条件是为了防止出现已经排好序的极端情况
                start++;
            }
            swap(start,end,array);//当在前面找到比tmp大的数据,在后面找到比tmp大的数据,就交换
        }
        swap(i,start,array);//当start和end相遇的时候,交换数列头元素和相遇地方的元素
        return start;//返回相遇的点,以相遇的点分制
    }

2.6.2 挖坑法

先把key(首)元素下标放入tmp中,让end向前移动,找到比tmp小的元素,填上key元素的坑,之后让start向后移动找到比tmp大的元素,填上上一个end的坑.以此类推,最后让tmp填上start和end相遇位置的坑.之后分治,递归重复上述操作.

 /**
     * 快速排序,整体思想:把小的往前放,把大的往后放
     * @param array
     */
    public static void quickSort(int[] array){
        quick(array,0,array.length-1);//规定开始和结束位置
    }
    private static void quick(int[] array,int s,int e){
        int left = s;
        int right = e;
        if (left >= right){//当左下标大于右下标的时候,说明递归完成,直接返回
            return;
        }
        swap(left,mid,array);//把区间内的中间值放在头
        int pos = position2(left,right,array);
        quick(array,left,pos-1);
        quick(array,pos+1,right);//以分制点为基准,向两侧递归
    }
    /**
     *挖坑法找相遇位置
     * @param start 开始位置
     * @param end 结束位置
     * @param array 原始数组
     * @return
     */
    private static int position2(int start,int end,int[] array){
        int tmp = array[start];
        while (start < end){
            if (start < end && array[end] >= tmp){//先走后面,否则下面没法填坑
                end--;
            }
            array[start] = array[end];
            if (start < end && array[start] <= tmp){
                start++;
            }
            array[end] = array[start];
        }
        array[start] = tmp;
        return start;
    }

2.6.3 快速排序优化

  • 由于快速排序越来越趋向有序,所以我们可以以分治之后数组的长度作为基准,当小于一定的值之后,就可以对分治区域使用插入排序.
  • 为了防止出现二叉树单分支的情况而降低效率,所以我们需要在分治区间找到中间大小的元素,与首元素交换.

快速排序最终版:

/**
     * 快速排序,整体思想:把小的往前放,把大的往后放
     * @param array
     */
    public static void quickSort(int[] array){
        quick(array,0,array.length-1);//规定开始和结束位置
    }
    private static void quick(int[] array,int s,int e){
        int left = s;
        int right = e;
        if (left >= right){//当左下标大于右下标的时候,说明递归完成,直接返回
            return;
        }
        //优化2:在霍尔法或者挖坑法使得数组不断趋于有序时,我们就可以发挥直接插入排序的优势
        //越趋于有序,越快
        if (right-left < 7){
            insertSort2(left,right,array);
        }
        //优化1:寻找中间大小的数字,防止出现单分支情况导致效率太低
        int mid = findmid(array,left,right);
        swap(left,mid,array);//把区间内的中间值放在头
        int pos = position(left,right,array);
        quick(array,left,pos-1);
        quick(array,pos+1,right);//以分制点为基准,向两侧递归
    }
    private static void insertSort2(int start,int end,int[] array){
        for (int i = start+1; i <= end; i++) {
            for (int j = i-1; j < end; j++) {
                if (array[j] > array[j+1]){
                    swap(j,j+1,array);
                }else {
                    break;
                }
            }
        }
    }
    //比较区间内的开始,中间,结束位置的值
    private static int findmid(int[] array,int start,int end){
        int mid = (start+end)/2;//区间内的中间位置
        //先比较左右开始和结束的值
        if (array[start] < array[end]){
            if (array[end] < array[mid]){//mid插入它们两个之间的三个空隙
                return end;
            } else if (array[mid] > array[start]) {
                return mid;
            }else {
                return start;
            }
        }else {
            if (array[start] < array[mid]){
                return start;
            } else if (array[end] < array[mid]) {
                return mid;
            }else {
                return end;
            }
        }
    }
    /**
     * 霍尔法找到相遇位置
     * @param start 开始位置
     * @param end 结束位置
     * @param array 原始数组
     * @return
     */
    private static int position(int start,int end,int[] array){
        int tmp = array[start];
        int i = start;//提前准备比较数据的下标
        while (start < end){
            while (start < end && array[end] >= tmp){//先走后面,注意加上等号
                end--;
            }
            while (start < end && array[start] <= tmp){//加前一个条件是为了防止出现已经排好序的极端情况
                start++;
            }
            swap(start,end,array);//当在前面找到比tmp大的数据,在后面找到比tmp大的数据,就交换
        }
        swap(i,start,array);//当start和end相遇的时候,交换数列头元素和相遇地方的元素
        return start;//返回相遇的点,以相遇的点分制
    }

    /**
     *挖坑法找相遇位置
     * @param start 开始位置
     * @param end 结束位置
     * @param array 原始数组
     * @return
     */
    private static int position2(int start,int end,int[] array){
        int tmp = array[start];
        while (start < end){
            if (start < end && array[end] >= tmp){//先走后面,否则下面没法填坑
                end--;
            }
            array[start] = array[end];
            if (start < end && array[start] <= tmp){
                start++;
            }
            array[end] = array[start];
        }
        array[start] = tmp;
        return start;
    }

2.7 归并排序

先把数组从中间拆分开,让每个小数组有序,之后把小数组合并回来,大数组就是有序的.也是分治思想,整体也是一棵二叉树的形状.
动态演示:

在这里插入图片描述

/**
     * 归并排序
     * @param array
     */
    public static void mergSort(int[] array){
        mergFunc(0,array.length-1,array);
    }
    private static void mergFunc(int left,int right,int[] array){
        //拆分数组
        int mid = (left+right)/2;
        if (left >= right){//递归到left>=right的时候,直接返回
            return;
        }
        //向两侧递归
        mergFunc(left,mid,array);
        mergFunc(mid+1,right,array);
        //开始合并
        merg(left,right,mid,array);
    }
    //在合并的时候进行排序
    private static void merg(int left,int right,int mid,int[] array){
        int s1 = left;
        int e1 = mid;
        int s2 = mid+1;
        int e2 = right;
        int k = 0;
        int [] tmp = new int[right-left+1];
        while (s1 <= e1 && s2 <= e2){
            if (array[s1] < array[s2]){
                tmp[k] = array[s1];
                k++;
                s1++;
            }else {
                tmp[k] = array[s2];
                k++;
                s2++;
            }
        }
        //看哪个数组还有数据,拷贝进去
        while (s1 <= e1){
            tmp[k] = array[s1];
            k++;
            s1++;
        }
        while (s2 <= e2){
            tmp[k] = array[s2];
            k++;
            s2++;
        }
        //把临时数组中的数据拷贝到原数组中
        for (int i = 0; i < tmp.length; i++) {
            array[left+i] = tmp[i];
        }
    }

特性总结:

  1. 时间复杂度:元素个数x数的高度,O(n*log2n)
  2. 空间复杂度:额外申请了一个数组,所以是O(n)
  3. 稳定性:稳定

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

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

相关文章

合泰杯(HT32F52352)RTC的应用(计时)--->掉电不丢失VBAT(代码已经实现附带源码)

摘要 在HT32F52352合泰单片机开发中&#xff0c;rtc在网上还是挺少人应用的&#xff0c;找了很久没什么资料&#xff0c;现在我根据手册和官方的代码进行配置理解。 RTC在嵌入式单片机中是一个很重要的应用资源。 记录事件时间戳&#xff1a;RTC可以记录事件发生的精确时间&…

简单记录一下在linux中安装pytorch成功!

1. 安装版本 pytorch版本:2.0.0 torchvision版本:0.15.0 torchaudio版本&#xff1a;2.0.0 cuda版本&#xff1a;cuda11.8 python版本&#xff1a;3.9 # CUDA 11.8 pip install torch2.0.0cu118 torchvision0.15.1cu118 torchaudio2.0.1 --index-url https://download.py…

最详细的SSL证书说明及免费申请方法

JoySSL官网 注册码230918 SSL&#xff08;Secure Sockets Layer&#xff09;证书&#xff0c;现在通常指的是其继任者TLS&#xff08;Transport Layer Security&#xff09;证书&#xff0c;是确保数据传输安全的核心技术之一。本文将深入探讨SSL证书的工作原理、重要性、类型以…

CSS-IN-JS Emotion

为什么会有css-in-js 优点 缺点 使用emotion插件库 npm i emotion/core emotion/styled使用时需要解析css属性 使用方式一&#xff1a; 通过注释告诉babel不讲jsx转化为react.create Element的调用&#xff0c;而是转化为jsx语法。会导致一个警告react未使用。 使用方式二&am…

windows11安装nginx

1.解压nginx安装包到没有中文的目录 2.双击运行nginx.exe 3.任务管理器查看是否有nginx进程 4.任务管理器->性能->资源监视器 5.网络->侦听端口&#xff0c;查看nginx侦听的端口&#xff0c;这里是90端口

JAVA顺序表相关习题1

1.笔试题:cvte str1 :welcome to cvte str2:come 描述:删除第一个字符串当中出现的所有的第二个字符串的字符!结果:wlt vt 要求 用ArrayList完成! public class Test {public static List<Character> findSameWords(String u1, String u2){List<Character> listn…

IoT Scenario: Smart Retail System-Multiple Sources and Multiple Terminals

物联网/大数据可视化领域发文可以联系&#xff1a;nascimsina.com IoT Scenario: Smart Retail System Overview The use of IoT in the retail industry enhances customer experiences, optimizes inventory management, and provides valuable insights into consumer beh…

S32K3系列 --- PTE13用作ADC采集电压异常问题

一、前言 当我们在将PTE13引脚配置未ADC功能的时候, 发现采集的电压异常。 二、问题排查 在S32DS的引脚配置里面,我们发现PTE13这个引脚默认是专用信号VRC_CTRL 但是在官方的IO表里也没有说默认路由到VRC_CTRL, 这个就很蛋疼。 通过芯片手册,我们发现: VRC_CTRL可以和B…

selenium 4.x 入门(环境搭建、八大元素定位)

背景 Web自动化测现状 1. 属于 E2E 测试 2. 过去通过点点点 3. 好的测试&#xff0c;还需要记录、调试网页的细节 一、selenium4.x环境搭建 一键搭建 pip3 install webdriver-helper 安装后自动的完成&#xff1a; 1. 查看浏览器的版本号 2. 查询操作系统的类型…

LeetCode in Python 10. Regular Expression Matching (正则表达式匹配)

正则表达式匹配涉及到两个字符串的匹配问题&#xff0c;类似于寻找最大公共子串&#xff0c;可使用动态规划思想解决。重点和难点在于如何构建正确的状态转移方程。 示例&#xff1a; 图1 正则表达式匹配输入输出 代码&#xff1a; class Solution:def isMatch(self, s: st…

二、再识VUE-MVVM

一、初识VUE 二、再识VUE-MVVM 三、VUE数据代理 MVVM Vue.js 专注于 MVVM 模型的 ViewModel 层。它通过双向数据绑定把 View 层和 Model 层连接了起来。实际的 DOM 封装和输出格式都被抽象为了 Directives 和 Filters。 ViewModel 一个同步 Model 和 View 的对象。在 Vue.js…

计算机复试项目:SpringCloud实战高并发微服务架构设计

秒杀购物商城--环境搭建 秒杀购物商城基础服务组件--详细介绍 秒杀购物商城基础服务--权限中心 秒杀购物商城业务服务--收货地址 秒杀购物商城业务服务--秒杀活动服务 秒杀购物商城--购物车的功能设计及分析 秒杀购物商城基础服务-用户中心 秒杀购物商城业务服务--商品中…

二分查找法实例

本文是根据数据结构中常常提到的二分法而作的一篇博客&#xff0c;主要通过一个二分法实例进行展开说明&#xff1a; 实例内容 通过一个二分法函数来寻找某个数是否在给定的数组中&#xff1b; 代码展示 # 执行二分查找法的算法函数 # 二分法查找的对象必须是一个有序的集…

初步认识Vscode

4.26初步认识Vscode &#xff08;一&#xff09;快捷键的使用 1. 打开控制端 ctrl ~2. 结束终端 ctrl c3. 多行同时对齐输出 按住shift alt 光标多选4. 多行同时任意位置输出 按住alt 光标单点你想要输入的位置5. 代码太长了&#xff0c;想混行编辑 alt z6. 打开设置控制…

工信部CAPPVD公布24年一季度积分情况,海云安位居全国第二!

近日&#xff0c;工业和信息化部移动互联网APP产品安全漏洞专业库&#xff08;以下简称“CAPPVD漏洞库”&#xff09;根据《CAPPVD漏洞库支撑单位能力评定办法》和综合24年第一季度的漏洞报送、重要行业企事业单位漏洞加分、高危漏洞处置加分、标准支撑等&#xff0c;最终公布了…

剧本杀小程序开发,剧本杀行业的新发展方向

前几年&#xff0c;剧本杀走入了大众的视野中&#xff0c;并迅速呈现出了井喷式的发展态势&#xff0c;一度成为了年轻消费者的首选娱乐方式&#xff0c;吸引了大量的消费者和跻身入局的创业者。 随着互联网的发展&#xff0c;剧本杀在线上的发展也非常火爆。我国网民群体非常…

final原理

文章目录 1. 设置 final 变量的原理2. 获取 final 变量的原理 1. 设置 final 变量的原理 理解了 volatile 原理&#xff0c;再对比 final 的实现就比较简单了 public class TestFinal {final int a 20; }字节码 0: aload_0 1: invokespecial #1 // Method java/lang/Object…

数据结构与算法(Java版) | 详解十大经典排序算法之一:插入排序

接下来&#xff0c;我来给大家讲解第三种排序算法&#xff0c;即插入排序。 基本介绍 首先&#xff0c;我们来看下插入排序的基本介绍。 插入排序&#xff0c;其属内部排序法&#xff0c;是对于欲排序的元素以插入的方式来找寻该元素的适当位置&#xff0c;以便最终达到排序…

解决RTC内核驱动的问题bm8563

常用pcf-8563 , 国产平替BM8563(驱动管脚一致)&#xff1b; 实时时钟是很常用的一个外设&#xff0c;通过实时时钟我们就可以知道年、月、日和时间等信息。 因此在需要记录时间的场合就需要实时时钟&#xff0c;可以使用专用的实时时钟芯片来完成此功能 RTC 设备驱动是一个标准…

最新版pycharm安装教程

目录 PyCharm 简介 访问 PyCharm 官网&#xff1a; 选择版本&#xff1a; 这里我们选择社区版即可 环境变量的配置 第一步 第二步 第三步 Pycharm的使用 【报错解决】 通用注意事项&#xff1a; PyCharm 简介 ​ PyCharm是一种Python IDE&#xff08;Integrated Devel…