【数据结构】关于快速排序,归并排序,计数排序,基数排序,你到底了解多少???(超详解)

本文主要是介绍【数据结构】关于快速排序,归并排序,计数排序,基数排序,你到底了解多少???(超详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

🌟🌟Hello家人们,这期继续讲解排序算法的原理,希望你能帮到屏幕前的你。

🌈上期博客在这里:http://t.csdnimg.cn/g7PyB

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

目录

📚️1.比较排序与非比较排序

📚️2.比较排序

2.1快速排序

1.递归基本思想:

2.非递归基本思想 :

 3.Hoare法:

4.挖坑法:

5.双指针(了解):

 6.快速排序总结:

2.2归并排序

1.递归基本思想:

2.非递归基本思想:

3.实现合并:

4.归并排序总结:

📚️3.非比较排序

3.1计数排序

3.2基数排序

📚️4.总结


📚️1.比较排序与非比较排序

排序算法主要分为比较排序和非比较排序两大类:
1.比较排序:

比较算法通过比较元素的大小来确定它们的相对顺序。常见的比较排序算法有冒泡排序、插入排序、选择排序、快速排序、归并排序等,本期小编将讲述快速排序,归并排序。

2.非比较排序:

非比较排序算法则不依赖于元素之间的直接比较来确定顺序。例如,计数排序、桶排序、基数排序等,小编这期将
 两者的区别:


1.比较排序的优点是其思想相对简单直观,易于理解和实现。但它的缺点也较为明显,比较操作的次数通常与待排序元素的数量呈特定的函数关系,导致其时间复杂度在最坏情况下可能较高。
 
2.非比较排序的优势在于在某些特定情况下,能够在较低的时间复杂度内完成排序。但它们往往需要额外的空间来辅助排序,并且对数据的特征有一定要求。

总的来说,比较排序适用于一般性的排序需求,而非比较排序在数据具有特定特征或对时间复杂度有极高要求时表现出色。具体使用哪种排序算法,取决于数据的特点、问题的规模以及对时间和空间复杂度的权衡。

📚️2.比较排序

2.1快速排序

1.递归基本思想:

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

这里就要每次根据基准来进行递归,实现快速排序:

图解:

代码实现:

public static void Quick(int[] array,int start,int end){if(start>=end)return;int privot=privot2(array,start,end);Quick(array,start,privot-1);Quick(array,privot+1, end);}

 这里要规定开始的索引下标,以及结束的索引下标; 

2.非递归基本思想 :

在非递归思想就是要运用栈这个结构,我们要找到实现基准查找的左右指针,先导入第一次基准后,进行循环,实现右树基准的查找,这里每次要进行出栈进行基准查找,找完之后进栈左右指针。

代码实现:

  public static void Quicknor(int[] array){Stack<Integer> stack=new Stack<>();int left=0;int right= array.length-1;int privot=privot1(array,left,right);if (privot-1>left){stack.push(left);stack.push(privot-1);}if(privot+1<right){stack.push(privot+1);stack.push(right);}while (!stack.isEmpty()){right=stack.pop();left=stack.pop();privot=privot1(array,left,right);if (privot-1>left){stack.push(left);stack.push(privot-1);}if(privot+1<right){stack.push(privot+1);stack.push(right);}}}

注意:这里要判断基准的左右是否还存在至少两个数据,存在才进栈,否则不进入。 

上述已经完成了基本的递归思想,那么接下来就要进行基准的实现了~~~

 3.Hoare法:

查找基准思路:

设置两个最左端索引(left),和最右端索引(right),和一个关键下标表示key;最右端索引进行向前遍历,若所表示的值大于key,那么right--,如果发现了大于key的值,那么就进行left向前遍历,若小于key,那么left++,直到发现比key大的数据,然后交换right和left,直到两者相遇,最后与key进行交换。

图解: 

代码实现:

 public static int privot(int[] array,int left,int right){int key=array[left];int i=left;while (right>left){while (array[right]>=key&&left<right){right--;}while (array[left]<=key&&left<right){left++;}swap(right,left,array);}swap(i,left,array);return left;}
4.挖坑法:

查找基本思路:

和上述Hoare思想基本一致,但是当right索引发现比key小的数值时,left索引所指向的数值就为right索引所指的值,当left索引发现比key大的数值时,right索引所指向的数值就为left索引所指的值,两者相遇后与最初始的left索引所指的数值进行交换。

 图解:

代码实现:

 public static int  privot1(int[] array,int left,int right){int privot=array[left];while (left<right){while (array[right]>=privot&&left<right){right--;}array[left]=array[right];while (array[left]<=privot&&left<right){left++;}array[right]=array[left];}array[left]=privot;return left;}
5.双指针(了解):

代码实现:

 public static int privot2(int[] array,int left,int right){int prev=left;int k=array[prev];int cur=left+1;while (cur<=right){if(array[cur]<k&&(++prev)!=cur){swap(cur,prev,array);}cur++;}swap(prev,left,array);return prev;}

这里的双指针法了解就好,小编不再赘述,可以画图理解一下。

 6.快速排序总结:


1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序


2. 时间复杂度:O(N*logN)


3. 空间复杂度:O(logN)


4. 稳定性:不稳定

2.2归并排序

1.递归基本思想:

运用递归实现,我们需要进行查找中间索引,进行两两拆分直到只有一个数据,再实现两两合并。

图解: 

代码实现: 

public static void Merge(int[] array,int left,int right){if(left>=right){return;}int mid=(left+right)/2;Merge(array,left,mid);Merge(array,mid+1,right);mergeCore(array,left,right,mid);}
2.非递归基本思想:

即分组进行合并排序,先是一个和一个,再次为二和二到最后全部合并(这里的合并是排好序的)

图解:

代码实现:

public static void Mergenor(int[] array){int left;int right;int gap=1;int mid;while (gap<array.length){for (int i = 0; i < array.length; i+=2*gap) {left=i;mid=left+gap-1;right=mid+gap;if(mid >= array.length) {mid = array.length-1;}if(right >= array.length) {right = array.length-1;}mergeCore(array,left,right,mid);}gap*=2;}}
3.实现合并:

实现合并思想:

小编在这里认为当两个数组进行合并时,第一个数组的第一个元素与第二个数组的第一个元素进行比较,如果那个更小,在与另一个数组下标加一后的值进行比较,直到索引越界。

图解: 

代码实现 :

 public static void mergeCore(int[] array,int left,int right,int mid){int[] tmp=new int[right-left+1];int k=0;int s1=left;int s2=mid+1;while (s1<=mid&&s2<=right){if(array[s1]<=array[s2]){tmp[k]=array[s1];k++;s1++;}else {tmp[k]=array[s2];k++;s2++;}}while (s1>mid&&s2<=right){tmp[k]=array[s2];s2++;k++;}while (s2>right&&s1<=mid){tmp[k]=array[s1];k++;s1++;}for (int i = 0; i <k ; i++) {array[i+left]=tmp[i];}}
4.归并排序总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

📚️3.非比较排序

3.1计数排序

思路步骤:

1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中

图解: 

代码实现: 

 public static void CountSort(int[] array){int max=array[0];int min=array[0];for (int i = 1; i < array.length; i++) {if (array[i]<min){min=array[i];}if(array[i]>max){max=array[i];}}int[] tmp=new int[max-min+1];for (int i = 0; i < array.length ; i++) {tmp[array[i]-min]++;}for (int i = 0; i < tmp.length ; i++) {while (tmp[i]!=0){System.out.print(i+min+" ");tmp[i]--;}}}

注意:若为90-100区间,不可能申请100个长度,这样会造成浪费,所以数组长度是最大值减去最小值加1;所以0下标的值就为数据0+数据最小值,1下标的值为1+最小值。

计数排序总结:

1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
2. 时间复杂度: O(MAX(N, 范围 ))
3. 空间复杂度: O( 范围 )
4. 稳定性:稳定

3.2基数排序

思路:

通过二维数组进行存储,导入对应位置的数值,然后再设置一个一维数组进行每行个数的值,进行打印,循环最大数的长度,就能在最后一次打印中取得顺序数组。

 图解:

代码如下: 

 public static void BaseSort(int[] array){int max=max(array);int maxLength=0;while (max!=0){max/=10;maxLength++;}int[][] bucket = new int[10][array.length-1];int[] elementCount = new int[10];int n=1;for (int i = 0; i < maxLength; i++) {for (int j = 0; j < array.length ; j++) {int element=array[j]/n%10;bucket[element][elementCount[element]]=array[j];elementCount[element]++;}//拿出数据int index=0;for (int j = 0; j <elementCount.length ; j++) {if(elementCount[j]!=0){for (int k = 0; k < elementCount[j]; k++) {array[index]=bucket[j][k];index++;}}elementCount[j]=0;}n*=10;}}public static int max(int[] array){int max=array[0];for (int i = 1; i < array.length ; i++) {if(array[i]>max){max=array[i];}}return max;}
}

注意:这里不仅要求最大值,还要求最大值的长度来表示循环次数,每次循环条件是要变化的,依次按照位数来进行操作,每次输出后再重新载入到二维数组中。

📚️4.总结

💬💬小编这期讲解了关于比较排序的最后两个排序:快速排序,归并排序,关于他们的代码以及思路都罗列了出来,还有包括非比较排序的基数排序,计数排序的思路原理和代码实现。

对于每个排序都要掌握它的基本原理,对于以上两个比较排序来说,还要了解二叉树的概念。

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


                               💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                         😊😊  期待你的关注~~~ 

 

这篇关于【数据结构】关于快速排序,归并排序,计数排序,基数排序,你到底了解多少???(超详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099607

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP