【数据结构】排序

news/2025/2/12 2:05:55/

作者:✿✿ xxxflower. ✿✿
博客主页:xxxflower的博客
专栏:【数据结构】篇
语录:⭐每一个不曾起舞的日子,都是对生命的辜负。⭐

文章目录

  • 1.排序
    • 1.1排序的概念
    • 1.2常见的排序算法
  • 2.常见排序算法
    • 2.1插入排序
      • 2.1.1直接插入排序
      • 2.1.2希尔排序(缩小增量排序)
    • 2.2选择排序
      • 2.2.1基本思想
      • 2.2.3 堆排序
    • 2.3交换排序
      • 2.3.1冒泡排序
      • 2.3.2快速排序
      • 2.3.2快速排序的优化
      • 2.3.3快速排序
      • 2.3.5非递归实现快速排序
    • 2.4 归并排序
      • 2.4.1递归实现归并排序
      • 2.4.2非递归的归并排序
  • 3.排序的总结
      • 计数排序

1.排序

1.1排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
在这里插入图片描述
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序的要求不能在内外存之间移动数据的排序。

1.2常见的排序算法

在这里插入图片描述

2.常见排序算法

2.1插入排序

插入排序的基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。

2.1.1直接插入排序

请添加图片描述

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

我们来分析一下插入排序时间复杂度:
最好情况下:顺序。O(N)
最坏情况下:逆序。O(N^2)
在这里插入图片描述
因此我们可以得出一个结论:当数据量不多,且基本上趋于有序的时候,直接插入排序是非常快的。
空间复杂度:O(1)
稳定性:稳定

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

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

//希尔排序private static void shell(int[] array,int gap){for(int i = gap;i < array.length;i++){int tmp = array[i];int j = i - gap;for(;j >= 0;j -= gap){if(tmp > array[j]){array[j+gap] = array[j];}else{break;}}array[i+gap] = tmp;}}public static void shellSort(int[] array){int gap = array.length;while(gap > 1){gap /= 2;shell(array,gap);}}

希尔排序的时间复杂度是数学上未解决的题,它会随着组别的变化而变化。但是大量的实验的得出局部的结论:O(N^1.3)
稳定性:不稳定。
空间复杂度:O(1)。
希尔排序实际上是对直接插入排序的优化。它使得数字趋于有序化,最后在进行直接插入排序。

2.2选择排序

2.2.1基本思想

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
在这里插入图片描述
方法一:

//选择排序public static  void selectSort(int[] array){for(int i = 0;i < array.length;i++){int minIndex = i;for(int j = i+1;j < array.length;j++){if(array[j] < array[minIndex]) {minIndex = j;}}//处理两个下标一样的情况,比如第一个数就是最大值,后面找不到比它小的值if(i != minIndex){swap(array,minIndex,i);}}}public static void swap(int[] array,int i,int j){int tmp =array[i];array[i] = array[j];array[j] = tmp;}

方法二:

//为了提高效率,可以左右一起找,找最小值放在前面,找最大值放在后面public static void selectSort2(int[] array){int left = 0;int right = array.length -1;while(left < right){int minIndex = left;int maxIndex = right;for(int j = left + 1;j <= right;j++){if(array[j] < array[minIndex]){minIndex = j;}if(array[j] > array[maxIndex]){maxIndex = j;}}//将最小值换到前面swap(array,minIndex,left);//如果max下标正好是Left说明上次已经把最大值从left位置换到了minIndex位置if(maxIndex == left){maxIndex = minIndex;}//吧最大值换到后面。swap(array,maxIndex,right);left++;right--;}}

选择排序的时间复杂度为:O(n^2)
空间复杂度:O(1)
稳定性:不稳定

2.2.3 堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
堆排序将要排序的数组创建成一个大根堆,再将第一个值和最后一个值进行交换,再将二叉树调整成为大根堆,依次循环排序。

//堆排序public static void heapSort(int[] array){creatBigHeap(array);int end = array.length -1;while(end > 0){swap(array,0,end);shiftDown(array,0,end);end--;}}private static void creatBigHeap(int[] array){for(int parent = (array.length - 1-1)/2;parent >= 0;parent--){shiftDown(array,parent,array.length);}}private static void shiftDown(int[] array,int parent,int len){int child = (2* parent) + 1;while(child < len){if(child+1 < len && array[child] < array[child+1]){child++;}if(array[child] > array[parent]){swap(array,child,parent);parent = child;child = 2 * parent +1;}else{break;}}}

堆排序的时间复杂度:O(N*logN)
在这里插入图片描述
空间复杂度:O(1)
稳定性:不稳定

2.3交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

2.3.1冒泡排序

//冒泡排序public static void bubbleSort(int[] array){for(int i = 0;i < array.length;i++){boolean flg = false;for(int j = 0; j < array.length - 1-i;j++){if(array[j] > array[j+1]){swap(array,j,j+1);flg = true;}}if(flg == false){break;}}}

2.3.2快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
1.Hoare排序
hoare排序是从右边开始找一个比key值小的,从左边找一个比key值大的数,两者进行交换,当left==right时,将此数与key交换。以此来排序。

//快速排序public static int partitionHoare(int[] array,int left,int right){int key = array[left];int i = left;while(left < right){while(left < right && array[right] >= key){right--;}while(left < right && array[left] <= key){left++;}swap(array,left,right);}swap(array,left,i);return left;}
  1. 挖坑法
    挖坑法就是以第一个数字为基准值,从右边找到一个比基准值大的数字放进基准的位置。再从左边找一个比基准值小的数字放进右边的位置
//挖坑法public static int partition2(int[] array,int left,int right){int key = array[left];while(left < right){while(left < right && array[right] >= array[key]){right--;}array[left] = array[right];while(left < right && array[left] <= array[right]){left++;}array[right] = array[left];}array[left] = key;return left;}
  1. 前后指针法
 //前后指针法private static int partition(int[] array, int left, int right) {int prev = left ;int cur = left+1;while (cur <= right) {if(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;} swap(array,prev,left);return prev;}

在这里插入图片描述

2.3.2快速排序的优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序
public static void insertSort(int[] array,int left,int right){for (int i = 1; i < array.length; i++) {int tmp = array[i];int j = i-1;for (;j >= 0;j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}}array[j+1] = tmp;}}
public static void quick(int[] array,int start,int end){if(start >= end){return;}if(end - start + 1 <= 15){insertSort(array,start,end);return;}int index = findMidValIndex(array,start,end);swap(array,start,index);int key = partition(array,start,end);quick(array,start,key-1);quick(array,end,key+1);}public static int findMidValIndex(int[] array,int start,int end){int midIndex = (start + end)/2;if(array[start] > array[end]){if(array[midIndex] > array[start]){return start;} else if (array[midIndex] < array[end]) {return end;}else{return midIndex;}}else{if(array[midIndex] < array[start]){return start;} else if (array[midIndex] > array[end]) {return end;}else{return midIndex;}}}

2.3.3快速排序

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

2.3.5非递归实现快速排序

在这里插入图片描述

先找基准
判断基准元素左边和右边是否有两个及以上元素?
如果有,则将首位置和p-1位置放入栈中(注意放入顺序)(处理左边),再将p+1位置和end位置放入栈中(处理右边)
当栈不为空的时候弹出两个元素在判断左边和右边的情况。依次循环

//非递归实现快速排序public static void quickSort(int[] array){Stack<Integer> stack = new Stack<>();int start = 0;int end = array.length-1;int pivot = partition(array,start,end);//1.判断左边是否有两个元素if(pivot > start + 1){stack.push(start);stack.push(pivot-1);}//2.判断右边是否有两个元素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);//3.判断左边是否有两个元素if(pivot > start + 1){stack.push(start);stack.push(pivot-1);}//4.判断右边是否有两个元素if(pivot < end-1){stack.push(pivot+1);stack.push(end);}}}

2.4 归并排序

2.4.1递归实现归并排序

归并排序,首先要了解二叉树的基本知识,通过递归将数组分成一个一个的然后在合并。合并的时候可以参考前面写过的例题即两个有序数组合并问题。通过合并可以使数组有序,此时我们建立了新的数组。注意在最后给数组赋值的时候,tmpArr当中的数据是right left之间的有序数据。因此要从右侧开始。

在这里插入图片描述

public static void mergeSort1(int[] array){mergeSortChild(array,0,array.length-1);}private static void mergeSortChild(int[] array,int left,int right){if(left == right){return;}//拆分int mid = (left+right)/2;mergeSortChild(array,left,mid);mergeSortChild(array,mid+1,right);//合并merge(array,left,mid,right);}public static void merge(int[] array,int left,int mid,int right){int s1 = left;int e1 = mid;int s2 = mid +1;int e2 = right;int[] tmpArr = new int [right - left +1];int k = 0;while(s1 <= e1 && s2 <= e2){if(array[s1] <= array[s2]){tmpArr[k++] = array[s1++];}else{tmpArr[k++] = array[s2++];}}while(s1 <= e1){tmpArr[k++] = array[s1++];}while(s2 <= e2){tmpArr[k++] = array[s2++];}for(int i = 0;i < k;i++){array[i+left] = tmpArr[i];}}

由归并排序的思想可得,其
空间复杂度为:O(N)
时间复杂度为:O(N*logN)
稳定性:稳定
我们学过的稳定排序为:插入,冒泡,归并

2.4.2非递归的归并排序

非递归实现归并排序时,先将数组分成一个一个的,然后通过调整left,mid right来合并数组。
在这里插入图片描述

 //非递归实现归并排序public static void mergeSort(int[] array){int gap = 1;while(gap < array.length){for(int i = 0;i < array.length;i+=gap){int left = i;int mid = left + gap-1;int right = mid+gap;//需要考虑mid和right越界的问题,i在循环中,所以不用考虑i越界的问题if(mid >= array.length){mid = array.length -1;}if(right >= array.length){right = array.length-1;}merge(array,left,mid,right);}gap = 2*gap;}}

3.排序的总结

在这里插入图片描述

计数排序

在这里插入图片描述

public static void countSort(int[] array) {//1、遍历数组 找到 最小值 和最大值  -》 才能确定 计数数组的大小int maxVal = array[0];int minVal = array[0];//O(n)for (int i = 0; i < array.length; i++) {if(array[i] > maxVal) {maxVal = array[i];}if(array[i] < minVal) {minVal = array[i];}}//2、确定计数数组的长度int len = maxVal - minVal + 1 ;int[] countArr = new int[len];//3. 开始遍历 当前数组 统计每个数字出现的次数  O(n)for (int i = 0; i < array.length; i++) {int val = array[i];countArr[val-minVal] ++;//??????????????}int index = 0;//4. 遍历计数数组,看每个下标的值是几,就打印几个下标的数据就好了 O(范围 + n)//范围遍历一次,位置上所有的数的个数加起来等于nfor (int i = 0; i < countArr.length; i++) {while (countArr[i] > 0) {//不敢打印array[index] = i+minVal;//??????????????index++;countArr[i]--;}}}

http://www.ppmy.cn/news/34378.html

相关文章

22 k8s常用命令

一、k8s网络 service网络 pod网络 节点网络 》 svc、pod网络都是虚拟机网络&#xff0c;真实网络是节点网络 二、内核升级 因为coentos系统3.10存在一些bug&#xff0c;docker、kubernetes不稳定&#xff0c;建议升级到4.4版本以上 三、集群资源分类 名称空间级别&#xff1…

SpringBoot 结合RabbitMQ与Redis实现商品的并发下单【SpringBoot系列12】

SpringCloud 大型系列课程正在制作中&#xff0c;欢迎大家关注与提意见。 程序员每天的CV 与 板砖&#xff0c;也要知其所以然&#xff0c;本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 1 项目准备 SpringBoot 整合 RabbitMQ 消息队…

AI热潮来袭||网友:AI会不会抢自己的饭碗啊~~~

ChatGPT还没搞懂&#xff0c;平地一声雷&#xff0c;GPT-4重磅发布&#xff01;瑟瑟发抖的吃瓜群众逐渐变多&#xff1a;AI会不会抢自己的饭碗啊~~~ 答案是&#xff1a;会&#xff01; 人工智能助手“阿里小蜜”承担95%的客服咨询&#xff1b; 机器人“天巡”接替运维人员以…

Springboot Long类型数据太长返回给前端,精度丢失问题 复现、解决

前言 惯例&#xff0c;收到兄弟求救&#xff0c;关于long类型丢失精度的问题&#xff1a; 存在一个初学者不会&#xff0c;就会有第二个初学者不会&#xff0c;所以我出手。 正文 不多说&#xff0c;开搞。 如题&#xff0c; 后端返回的数据 给到 前端&#xff0c; Long类型数…

美颜sdk是什么?美颜SDK基础知识讲解、代码分析

美颜sdk是如何在美颜相机、短视频、直播中发挥作用的&#xff1f;本篇文章小编将以直播平台为例&#xff0c;给大家详细讲解美颜sdk的一些基础知识。 一、美颜sdk是什么 美颜sdk&#xff0c;是可用于开发面向特定平台的软件应用程序工具包。举个例子&#xff0c;如果你想组装…

DJ2-5 DNS:Internet 的目录服务

目录 1. DNS 简介 2. DNS 服务器提供的功能 3. 分布式、层次数据库 4. DNS 查询方法 5. DNS 缓存和权威 DNS 服务器记录更新 6. DNS 记录 7. DNS 报文 8. 在 DNS 数据库中插入记录 9. DNS 攻击 1. DNS 简介 名称&#xff1a;Domain Name System DNS 是&#xff1a; …

单片机stm32新建工程后的编程准备

STM32学习之新建工程模板_stm32工程模板_榕林子的博客-CSDN博客 1、按基本模板新建全新的工程文件&#xff0c;编译检查一下 2、在工程里新建一个文件夹名称为app&#xff0c;在app里存放相应的驱动文件 3、在app里新建文件夹“led” 4、在led里新建一个led.h文件&#xff…

共享文件和文档方法指南

将文件从一台PC传输到另一台PC可能很麻烦。如果两台计算机位于同一个房间或房屋中&#xff0c;那么您可以使用中间硬盘驱动器或设备&#xff08;如空白CD或USB闪存驱动器&#xff09;。 它适用于一次性转账&#xff0c;但如果您发现自己定期转移文件&#xff0c;那么很快就会成…