排序算法详细汇总对比分析(C++)

本文详细介绍了C++中的各种排序算法,包括选择排序、插入排序、希尔排序、冒泡排序、归并排序、快速排序、桶排序和堆排序。对于每种算法,讨论了其时间复杂度、空间复杂度、适用场景和优化方法。特别强调了插入排序和快速排序在接近有序序列中的高效性,以及归并排序的空间换时间策略。文章还提供了代码示例和关键知识点,如双路快速排序、自底向上的归并排序和三路快速排序,以应对不同情况下的排序需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

选择题考点:

(1)堆排序,快速排序,冒泡排序每趟都会把一个值放在正确的位置上,但是希尔排序不会!
(2)归并排序和快速排序各自的优缺点:
归并排序比快速排序稳定,并且归并排序最差情况是O(nlogn),而快速排序最差情况是O(n^2)。归并排序空间换时间,快速排序不要耗空间。

在学习算法的时候,一定要搞清楚每一个排序算法的适用情况,每个排序算法都有自身的适用特点,记住他们的优势,这样可以针对性的使用以及在面试的时候和面试官探讨。
在这里插入图片描述
对于近乎有序的数组,插入排序的效率非常高,时间复杂度最高可缩小到O(n)

第一种最常用的就是

1.选择排序:

时间复杂度:O(N*N)
空间复杂度:O(1)
大致思想为:从第一个数开始,依次从后面所有数中挑出最小的那个数(包括比较第一个数),然后与第一个数进行数值交换。

自己写代码总会出现或多或少的小问题:
注意:
(1)swap函数记得加上头文件#include
(2)自己写交换函数记得用引用传递,int temp = a;a=b;
b=temp;这个交换顺序自己在纸上写一写。
(3)为了可以容纳多种类型数组,加入模板T,同时包装成一个函数,这样float型数组(小数),string型数组(字符串)都可以进行排序。

#include<iostream>

using namespace  std;
#include<algorithm>

//void swap(int& a, int& b)
//{
   
//	int temp = a;
//		a=b;
//		b=temp;
//}
template<typename T>
void selectSort(T arr[], int n){
   

	for(int i=0; i<n; i++)
	{
   
		int min = i;
	for(int j=i+1; j<n; j++)
		{
   
			if(arr[j]<arr[min])
				min = j;
		}
		swap(arr[i],arr[min]);
	}
}


int main(){
   
	int n=6;
	int arr[6]={
   5,2,4,3,6,1};
	selectSort(arr,6);
	
	for(int k=0; k<n; k++)
	{
   
		//cout<<"排序后:"<<endl;
		cout<<arr[k]<<" ";
	}
	while(1);
return 0;
}

适用范围:若n较小(如n≤50),可采用直接插入或直接选择排序。

下面这小部分可以不用看。
后来专门搞了一个随机生成数组值得函数,放在SorttestHelper.h文件里,代码如下。
这里学习到得一个点是它得返回类型是namespace,根据这个代码可知,该返回类型是一个指向新开辟的数组空间的指针。,但事实上它返回的数组空间,只是用指针表示一下。为什么这么说呢,int * arr = 该函数, 也就是用一个指针指向该函数返回的数组空间!注意在最后用delete[] arr; 释放一下那个函数开辟的数组空间。

#include<iostream>
#include<ctime>
#include<cassert>
using namespace  std;
namespace SortTestHelper {
   

    // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
    int *generateRandomArray(int n, int range_l, int range_r) {
   

        int *arr = new int[n];

        srand(time(NULL));
        for (int i = 0; i < n; i++)
            arr[i] = rand() % (range_r - range_l + 1) + range_l;
        return arr;
    }

2.插入排序:

时间复杂度:O(N*N)
空间复杂度:O(1)
选择排序的思想是从该数后面找到最小与该数进行交换,而插入排序的思想是,从第二个开始,把该数放到前面所有数的数组中对应的位置。(一个是把后面整体看成数组,一个是把前面整体看成一个数组)

template<typename T>
void InsertSort(T arr[], int n){
   
	for(int i =1; i<n; i++){
   
		for(int j=i;j>0; j--){
   
			if(arr[j]<arr[j-1])//!这里是关键,j和j-1比较!
				swap(arr[j],arr[j-1]);
			else
				break;
		}
	}

}

再补充一个比较:这个插入排序是j和j-1进行比较,而选择排序是j和保存的最小值进行比较!

适用范围:

若n较小(如n≤50),可采用直接插入或直接选择排序。
注意:插入排序如果第一轮该数比前面的数组的第一个比较的数大,那么后面就不用再继续了。所以相对选择排序还说应该是更快一点!但是事实上,是可能快也可能慢,因为有时候插入排序相比选择排序需要交换更多次!所以下面出现了插入排序的优化!

但是插入排序最大的优点在于,对于接近有序的序列,插入排序可以实现效率非常的高,甚至比很多时间复杂度小的排序方法都高,而现实生活中很多事物的排序都属于接近有序的排序,比如不小心被打乱的时间,或者注册表等!所以其实用价值很高!可以降到o(n)的复杂度。

插入排序优化:

观察下面的程序,改进后的方法:就是减少交换,把交换都换成了赋值。怎么个赋值法?

把当前的数保存在一个临时变量中(对应T e = arr[i];),然后将前面的数组和临时变量比,要是临时变量小(或者说该数小),暂时先不和之前一样让他们交换,而是就把较大的数赋值给后面的小数,一直到前面数组中哪个数比该数小,好,跳出来,把该数放在数组中这个数后面!
临时变量e中保存着最小的数,然后在循环中找到最小数的位置,然后把临时变量放进去。

template<typename T>
void InsertSort(T arr[], int n){
   
	
	for(int i =1; i<n; i++){
   
		T e = arr[i];
		int j;
		for( j=i;j>0 && e <arr[j-1]; j--){
   
			   
			   arr[j]=arr[j-1];				 
		}
		arr[j] = e;
	}
}

最后再强调一下:
插入排序最大的优点在于,对于接近有序的序列,插入排序可以实现效率非常的高,甚至比很多时间复杂度小的排序方法都高,而现实生活中很多事物的排序都属于接近有序的排序,比如不小心被打乱的时间,或者注册表等!所以其实用价值很高!

3.希尔排序(难)

自我感觉希尔排序的原理很好理解,就是把序列分组之后再进行插入排序,没有看过的可以看看这篇希尔排序

难就难在代码的分析上:
注意下面是三层循环,里面两层循环是模拟插入排序
下面是相对插入排序几个改动的地方,我依次解释:

template<typename T>
void Shell_sort(T arr[], int n){
   
for(int gap = n/2; gap>0; gap/=2)//改动
{
   
	//改动i=gap
	for(int i =gap; i<n; i++){
   
		//e里面保存的是最小值
		T e = arr[i];
		int j;
		//改动j>gap,j<gap时就结束
		//改动e <arr[j-gap]
		//改动j-=gap
		//改动j-=gap
		for( j=i;j-gap>=0 && e <arr[j-gap]; j-=gap){
   
			   
			   arr[j]=arr[j-gap];				 
		}
		arr[j] = e;
	}
}

}

第一个改动就是加入了gap循环,这个gap既代表有几个分组,又代表分组后每个元素之间的间隔。它循环起来是因为希尔排序要进行多次分组,每次分组都不同。直到gap=0就不用分了,这时候就排好了。

第二次改动把插入排序的初始值换了,以前是从第2个数开始作为要插入的对象,往前面的数组插入,现在是先把第gap个数作为插入对象,这个gap在它现在的分组中其实就是第2个数!

第三个改动j-gap>=0,它就相当于之前插入排序的j-1>=0,这个不用理解,只要知道数组下标不能为负数,所以j-1≥0,所以j-gap>=0。

另外两个比较简单就不说了就不用说了。

注意:分组之后的排序是交错排序,不是先把其中一组排完再排另一组!!!是按照顺序交错排序!

时间复杂度:O(n^(1.3—2))
时间复杂度相对选择排序减小了一点,但是也没减小多少。主要优点在于可以处理大型数组。

  1. 最好情况:O(n^1.3)
  2. 最坏情况:O(n^2)
    空间复杂度:O(1)

适用场景
希尔排序是对直接插入排序的一种优化,可以用于大型的数组,希尔排序比插入排序和选择排序要快的多,并且数组越大,优势越大

4.冒泡排序(史上最菜的排序算法)

时间复杂度:O(N*N)
空间复杂度为 O(1)
适用于数据量很小的排序场景。
这个比较简单,就从第一个开始,与它相邻的比较,要是偏大就交换,这样一直交换下去,最后面的那个就得到了最大值。第一轮交换最后面那个值排好,第二轮倒数后两个值排好,既然每次倒着数都增加一个排好的值,那就每一轮减少一个值的排序,只用排序前n-i个。

template<typename T>
void maopao_sort(T arr[], int n){
   
	for(int i=0;i<n-1;i++){
   
		for(int j=0;j<n-1-i; j++){
   
			if(arr[j]>arr[j+1])
				swap(arr[j
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值