【算法刷题】八大排序算法总结(冒泡、选择、插入、二分插入、归并、快速、希尔、堆排序)

在这里插入图片描述

文章目录

  • 八大排序算法总结
  • 1.冒泡排序
  • 2.选择排序
  • 3.插入排序
  • 4.二分插入排序
  • 5.归并排序
  • 6.快速排序
  • 7.希尔排序
  • 8.堆排序

八大排序算法总结

排序排序方法平均情况最好情况最坏情况空间稳定性
1冒泡排序O(n2)O(n)O(n2)O(1)稳定
2选择排序O(n2)O(n2)O(n2)O(1)不稳定
3插入排序O(n2)O(n)O(n2)O(1)稳定
4二分插入排序O(n2)O(n)O(n2)O(1)稳定
5归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
6快速排序O(nlogn)O(nlogn)O(n2)O(logn)~O(n)不稳定
7希尔排序O(nlogn) ~ O(n2)O(n1.3)O(n2)O(1)不稳定
8堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定

1.冒泡排序

  1. 核心思想: 通过相邻元素的比较和交换来将最大(或最小)的元素逐渐“冒泡”到数组的一端

    动图

  2. 具体步骤:

    1. 从数组的第一个元素开始,依次比较相邻的两个元素。
    2. 如果前一个元素大于后一个元素,则交换它们的位置,使得较大的元素“冒泡”到后面
    3. 继续进行相邻元素的比较和交换,直到数组的末尾
    4. 重复执行上述步骤,每次都会将当前未排序部分的最大元素“冒泡”到数组的末尾
    5. 重复执行上述步骤,直到整个数组排序完成。
  3. 实现代码:

    public static void bubbleSort(int[] arr) {int temp = 0;for (int i = arr.length - 1; i > 0; i--) { 	// 每次需要排序的长度for (int j = 0; j < i; j++) { 			// 从第一个元素到第i个元素if (arr[j] > arr[j + 1]) {			// 确保较大的元素排到后面temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
    }
    
  4. 时间和空间复杂度:

    • 平均情况:O(n2)
    • 最好情况:O(n)
    • 最坏情况:O(n2)
    • 空间复杂度:O(1)
    • 稳定性:稳定
  5. 优化: 增加标记符swap,用于确定当前一轮冒泡是否有元素进行交换,若没有则跳出循环,表示数组已经有序了

    public static void bubbleSort(int[] arr) {int temp = 0;boolean swap;for (int i = arr.length - 1; i > 0; i--) { // 每次需要排序的长度swap=false;for (int j = 0; j < i; j++) { // 从第一个元素到第i个元素if (arr[j] > arr[j + 1]) {temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swap=true;}}//loop jif (swap==false){			//前面的一轮循环没有进行交换,数组已经有序break;}}//loop i
    }// method bubbleSort
    

2.选择排序

  1. 核心思想: 在未排序部分的数据中选择最小(或最大)的元素,然后将其放置到已排序部分的末尾

  2. 具体步骤:

    1. 遍历数组,将第一个元素作为当前最小(或最大)元素。
    2. 在未排序部分的剩余元素中,找到最小(或最大)的元素,并记录其位置。
    3. 将找到的最小(或最大)元素与未排序部分的第一个元素交换位置,将其放置到已排序部分的末尾。
    4. 重复执行上述步骤,每次都会将当前未排序部分的最小(或最大)元素放置到已排序部分的末尾。
    5. 这样,经过n-1轮的遍历和交换,整个数组就会按照升序(或降序)排列。
  3. 实现代码:

    public static void selectionSort(int[] arr) {int temp, min = 0;for (int i = 0; i < arr.length - 1; i++) {min = i;// 循环查找最小值for (int j = i + 1; j < arr.length; j++) {if (arr[min] > arr[j]) {min = j;}}if (min != i) {temp = arr[i];arr[i] = arr[min];arr[min] = temp;}}
    }
    
  4. 时间和空间复杂度:

    • 平均情况:O(n2)
    • 最好情况:O(n2)
    • 最坏情况:O(n2)
    • 空间复杂度:O(1)
    • 稳定性:不稳定
  5. 关于稳定性的探讨:

    • 稳定性:在排序过程中,相等元素的相对顺序是否会被改变。如果排序算法能够保持相等元素的相对顺序不变,则称其为稳定的排序算法;反之,则称其为不稳定的排序算法

    • 冒泡排序(升序):当相邻元素进行比较并需要交换位置时,只有当后面的元素小于前面的元素才会进行交换。因此,对于相等的元素,即使它们相邻,它们的相对顺序也不会被改变,从而保持了排序的稳定性

    • 选择排序:每次选择最小的元素并放置到已排序部分的末尾。在寻找最小(或最大)元素的过程中,可能会导致相等元素的相对顺序发生改变。例如,在一组相等的元素中,由于选择排序每次只选择一个最小(或最大)元素,所以在选择的过程中可能会交换这些相等元素的位置,从而导致排序的不稳定性

    • 举例:假设我们有以下一组待排序的元素:

      [5①, 2, 7, 5②, 1]
      
      • 冒泡排序:比较相邻的元素,并根据需要交换它们的位置。当遇到相等元素时,只有在后面的元素小于前面的元素时才会进行交换。

        第一轮冒泡排序:[2, 5①, 5②, 1, 7]
        第二轮冒泡排序:[2, 5, 1, 5, 7]
        第三轮冒泡排序:[2, 1, 5, 5, 7]
        第四轮冒泡排序:[1, 2, 5, 5, 7]
        
      • 选择排序:

        第一轮选择排序:[1, 2, 7, 5②, 5①](选择1) (两个相等的5的位置发生变化,不稳定!!!)
        第二轮选择排序:[1, 2, 5, 5, 7](选择2)
        第三轮选择排序:[1, 2, 5, 5, 7](选择5)
        第四轮选择排序:[1, 2, 5, 5, 7](选择5)
        第五轮选择排序:[1, 2, 5, 5, 7](选择7)
        

3.插入排序

  1. 核心思想:待排序的元素逐个插入到已排序部分的正确位置

    动图

  2. 具体步骤:

    1. 将数组分为已排序部分和未排序部分。一开始,已排序部分只包含第一个元素,即数组的第一个元素被认为是已排序的。
    2. 从未排序部分取出一个元素,将其插入到已排序部分的正确位置。为了找到正确的位置,可以从已排序部分的末尾开始,依次向前比较已排序元素,直到找到小于等于该元素的位置。
    3. 将该元素插入到找到的位置,并将已排序部分中的元素向后移动,为新元素腾出位置。
    4. 重复步骤2和步骤3,直到未排序部分为空。
    5. 最终,已排序部分即为排序好的数组。
  3. 实现代码:

    public static void insertionSort(int[] arr) {int n = arr.length;for (int i = 1; i < n; i++) {		//从第二个元素开始,第一个元素默认有序int key = arr[i];				//保存当前元素 keyint j = i - 1;//找到合适的位置while (j >= 0 && arr[j] > key) {//发现已排序元素比 key 大,则将该元素向后移动一位,直到找到 key 的正确位置arr[j + 1] = arr[j];j = j - 1;					//往后移}	arr[j + 1] = key;1				//找到 key 保存的位置}
    }
    
    待排序数组:[5, 2, 4, 6, 1, 3]
    1:[2, 5, 4, 6, 1, 3]
    2:[2, 4, 5, 6, 1, 3]
    3:[1, 2, 4, 5, 6, 3]
    4:[1, 2, 3, 4, 5, 6]
    
  4. 时间和空间复杂度:

    • 平均情况:O(n2)
    • 最好情况:O(n)
    • 最坏情况:O(n2)
    • 空间复杂度:O(1)
    • 稳定性:稳定

4.二分插入排序

  1. 核心思想:利用二分查找来确定待插入元素在已排序部分中的位置,以减少比较次数

  2. 实现步骤:

    1. 初始状态:将数组的第一个元素视为已排序部分,其余元素视为待排序部分。
    2. 插入过程:从第二个元素开始遍历待排序部分,对于每个元素,使用二分查找确定其在已排序部分中的插入位置。
    3. 二分查找:在已排序部分中,使用二分查找找到第一个大于待插入元素的位置,这个位置及之后的元素都需要向后移动一个位置来腾出空间。
    4. 插入操作:将待插入元素插入到找到的位置,完成一轮插入操作。
    5. 重复步骤2~4,直到待排序部分中的所有元素都被插入到已排序部分中,整个数组就被排序完成了。
  3. 实现代码:

    public static void binaryInsertionSort(int[] arr) {int n = arr.length;for (int i = 1; i < n; i++) {int key = arr[i];int left = 0;int right = i - 1;// 二分查找确定插入位置int insertIndex = binarySearch(arr, left, right, key);// 将大于 key 的元素向后移动一位for (int j = i - 1; j >= insertIndex; j--) {arr[j + 1] = arr[j];}// 插入 keyarr[insertIndex] = key;}
    }// 二分查找
    private static int binarySearch(int[] arr, int left, int right, int key) {while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] == key) {return mid; // key 在已排序部分的位置} else if (arr[mid] < key) {left = mid + 1;} else {right = mid - 1;}}return left; // 返回待插入位置
    }
    
  4. 时间和空间复杂度:

    • 平均情况:O(n2)
    • 最好情况:O(n)
    • 最坏情况:O(n2)
    • 空间复杂度:O(1)
    • 稳定性:稳定

5.归并排序

  1. 核心思想: 采用分治法,将已有序的子序列合并,得到完全有序的序列;

  2. 步骤:

    1. 分解(Divide):将原始数组分解为较小的子数组,直到每个子数组只有一个元素为止。这可以通过递归的方式实现,将数组不断二分直到每个子数组的长度为1。
    2. 解决(Conquer):对每个子数组进行排序。对于只有一个元素的子数组来说,可以认为它们已经是有序的。
    3. 合并(Merge):合并相邻的子数组,形成一个更大的有序数组。合并过程中,需要按照大小顺序逐个比较两个子数组中的元素,并将较小(或较大)的元素依次放入一个临时数组中,直到合并完成。
    4. 递归合并(Recursively Merge):重复以上步骤,直到所有子数组都被合并成一个大的有序数组为止。

    动图

  3. 实现代码:

    public static void mergeSort(int[] arr){int[] temp =new int[arr.length];internalMergeSort(arr, temp, 0, arr.length-1);
    }
    private static void internalMergeSort(int[] arr, int[] temp, int left, int right){//当left==right的时,已经不需要再划分了if (left<right){int middle = (left+right)/2;internalMergeSort(arr, temp, left, middle);          //左子数组internalMergeSort(arr, temp, middle+1, right);       //右子数组mergeSortedArray(arr, temp, left, middle, right);    //合并两个子数组}
    }
    // 合并两个有序子序列
    private static void mergeSortedArray(int arr[], int temp[], int left, int middle, int right){int i=left;      int j=middle+1;int k=0;while (i<=middle && j<=right){temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];}while (i <=middle){temp[k++] = arr[i++];}while ( j<=right){temp[k++] = arr[j++];}//把数据复制回原数组for (i=0; i<k; ++i){arr[left+i] = temp[i];}
    }
    
  4. 时间和空间复杂度:

    • 平均情况:O(nlogn)
    • 最好情况:O(nlogn)
    • 最坏情况:O(nlogn)
    • 空间复杂度:O(n)
    • 稳定性:稳定
  5. 适用场景: 归并排序在数据量比较大的时候也有较为出色的表现(效率上),但是,其空间复杂度O(n)使得在数据量特别大的时候(例如,1千万数据)几乎不可接受。而且,考虑到有的机器内存本身就比较小,因此,采用归并排序一定要注意。

6.快速排序

  1. 核心思想: 通过选取基准元素,将数组划分为两个子数组,并对子数组递归排序,实现整个数组的快速排序

  2. 实现步骤:

    1. 从数列中挑出一个元素,称为 “基准”(pivot),
    2. 重新排序数列,所有比基准值小的元素摆放在基准前面所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
    3. 递归地(recursively)把小于基准值元素的子数列大于基准值元素的子数列排序。
  3. 实现代码:

    public static void quickSort(int[] arr){qsort(arr, 0, arr.length-1);
    }private static void qsort(int[] arr, int low, int high){if (low >= high)return;int pivot = partition(arr, low, high);        //将数组分为两部分qsort(arr, low, pivot-1);                   //递归排序左子数组qsort(arr, pivot+1, high);                  //递归排序右子数组
    }private static int partition(int[] arr, int low, int high){int pivot = arr[low];     //基准while (low < high){while (low < high && arr[high] >= pivot){--high;} arr[low]=arr[high];             //比基准小的元素会被移动到基准的左边while (low < high && arr[low] <= pivot){++low;}arr[high] = arr[low];           //比基准大的元素会被移动到基准的右边}//扫描完成,基准到位arr[low] = pivot;//返回的是基准的位置return low;
    }
    
  4. 举例:

    初始:[7, 2, 1, 6, 8, 5, 3, 4]	# 7为基准第一:[4, 2, 1, 6, 8, 5, 3, 4] # 4移到左边[4, 2, 1, 6, 8, 5, 3, 8] # 8移到右边[4, 2, 1, 6, 3, 5, 3, 8] # 3移到左边[4, 2, 1, 6, 3, 5, 7, 8] # 基准7插入到low位置划分为两个数组:[4, 2, 1, 6, 3, 5]和[8]
    第二: [4, 2, 1, 6, 3, 5] # 以4为基准[3, 2, 1, 6, 3, 5] # 3移到左边[3, 2, 1, 6, 6, 5] # 6移到右边[3, 2, 1, 4, 6, 5] # 基准4插入到low位置以此类推......
    
  5. 时间和空间复杂度:

    • 平均情况:O(nlogn)
    • 最好情况:O(nlogn)
    • 最坏情况:O(n2)
    • 空间复杂度:O(logn)~O(n)
    • 稳定性:不稳定

7.希尔排序

  • 明天总结

8.堆排序

  • 明天总结

在这里插入图片描述

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

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

相关文章

windows wireshark抓包rtmp推流出现TCP Retransmission

解决办法&#xff1a;tcp.port1935 && !(tcp.analysis.retransmission)

将扁平数据转换为树形数据的方法

当遇到了好多扁平数据我们都无从下手&#xff1f;不知道如何处理&#xff1f; 家人们 无脑调用这个函数就好了 接口请求回来以后 调用这个函数传入实参就可以用啦~ // 树形菜单函数 function GetTreeData(data) {let TreeData [];let map new Map(); //存在id,对应所在的内…

Unity之Unity面试题(三)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Unity面试题&#xff08;三&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取…

关于Salesforce DevOps的理解

“DevOps”是一组结合了软件开发 &#xff08;Dev&#xff09; 和运营 &#xff08;Ops&#xff09; 的实践&#xff0c;可帮助团队更快、更可靠地构建、测试和发布软件。 DevOps 的核心理念包括持续集成&#xff08;Continuous Integration&#xff09;、持续交付&#xff08;…

1.微服务

一、微服务是什么 微服务是一种架构风格&#xff0c;即&#xff0c;一个应用应该是一组小型服务&#xff0c;每个服务器只负责一种服务&#xff0c;服务之间可以通过 HTTP 的方式进行互通。每一个功能元素最终都是一个可独立替换和独立升级的软件单元。 可以说&#xff0c;微…

SSL数字证书

SSL数字证书产品提供商主要来自于国外&#xff0c;尤其是美国&#xff0c;原理和使用操作系统一样&#xff0c;区别在于SSL数字证书目前无法替代性&#xff0c;要想达到兼容性99%的机构目前全球才3-4家&#xff0c;目前国内的主流网站主要使用的是国际证书&#xff0c;除了考虑…

【简单讲解下Kotlin】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Maven与Jave web结构

Maven 简介 https://www.liaoxuefeng.com/wiki/1252599548343744/1255945359327200 java web module web目录 –src 应用程序源代码和测试程序代码的根目录 –main –java  应用程序源代码目录     --package1     --class1     --class2 –resources  应用…

华为S5735S核心交换配置实例

以下脚本实现创建vlan2,3&#xff0c;IP划分&#xff0c;DHCP启用&#xff0c;接口划分&#xff0c;ssh,telnet,http,远程登录启用 默认用户创建admin/admin123提示首次登录需要更改用户密码 sysname test-Hxvlan 2 description to test1…

LeetCode 53. 最大子序和

解题思路 相关代码 class Solution {public int maxSubArray(int[] nums) {//f[i]是以nums[i]结尾的连续子数组的最大和。int f[] new int[100010];f[0] nums[0];int resnums[0];for(int i1;i<nums.length;i){f[i] Math.max(f[i-1]nums[i],nums[i]);res Math.max(res,f…

js的模块是怎么加载的

js的模块是怎么加载的 以上是几种常见的 JavaScript 模块加载方式。不同的加载方式适用于不同的场景&#xff0c;选择合适的模块加载方式可以更好地组织和管理 JavaScript 代码。 1.ES6 模块 (ESM)&#xff1a;ES6 模块是 JavaScript 中官方的模块化方案&#xff0c;使用 imp…

阻抗匹配(低频和高频)

一、当信号为低频时 二、当信号为高频时 三、最理想的阻抗要求&#xff1f; 四、为什么射频阻抗基本都是50欧姆&#xff08;信号源阻抗传输线特征阻抗负载阻抗50欧姆&#xff09; 综合考虑&#xff0c;射频行业标准选定50欧姆阻抗。

SqlServer快速导出数据库结构的方法

1、查询出所有的表 SELECT name, id From sysobjects WHERE xtype u ORDER BY name ASC 2、根据表名查询出表结构 select syscolumns.name as "列名", systypes.name as "数据类型", syscolumns.length as "数据长度", sys.extended_prope…

【三十九】【算法分析与设计】综合练习(5),79. 单词搜索,1219. 黄金矿工,980. 不同路径 III

79. 单词搜索 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平…

hive-3.1.2分布式搭建与hive的三种交互方式

hive-3.1.2分布式搭建&#xff1a; 一、上传解压配置环境变量 在官网或者镜像站下载驱动包 华为云镜像站地址&#xff1a; hive&#xff1a;Index of apache-local/hive/hive-3.1.2 mysql驱动包&#xff1a;Index of mysql-local/Downloads/Connector-J # 1、解压 tar -zx…

C/C++的前置++、后置++以及前置--、后置--使用的详细讲解

在c/c语言中&#xff0c;变量的自增和自减经常被使用到&#xff0c;所以今天就来个详细讲解。本次讲解用到的语言是c语言&#xff0c;在c中的原理也是一样的。 变量自增自减分为四种情况&#xff0c;每种情况的结果都有所差异&#xff0c;四种情况分别为前置、后置、前置--、后…

STK Components 二次开发-卫星碎片

1.碎片数据下载 2.处理数据 和处理卫星数据一致。 3.批量创建卫星碎片 其实创建方式和卫星一样 var issPoint new Sgp4Propagator(tleList).CreatePoint();var debris new Platform{Name tleList.Name,LocationPoint issPoint,OrientationAxes new AxesVehicleVelocityL…

基于Whisper语音识别的实时视频字幕生成 (二): 在线实时字幕

Whisream Whistream&#xff08;微流&#xff09;是基于Whisper语音识别的的在线字幕生成工具&#xff0c;支持rtsp/rtmp/mp4等视频流在线语音识别 1. whistream介绍 whistream将在whishow基础上引入whisper进行在线语音识别生成视频字幕 2. 使用 python&#xff1a; pyth…

第23次修改了可删除可持久保存的前端html备忘录:增加了百度引擎

第22次修改了可删除可持久保存的前端html备忘录视频背景分离&#xff0c;增加了本地连接&#xff0c;增加了纯CSS做的折叠隐藏修改说明 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport…

JVM-结合MAT工具分析OOM问题

JVM-结合MAT工具分析OOM问题 启动项目前配置VM参数 -XX:UseParNewGC -XX:UseConcMarkSweepGC -Xms1m #初始化大小 -Xmx1m #最大值 -XX:PrintGCDetails -Xloggc:gc_dandan.log -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./运行结果截图 使用MAT打开java_pid12164.hprof…