数据结构---一些简单的排序算法

本文详细介绍了几种常见的排序算法,包括直接插入排序、折半插入排序、希尔排序、冒泡排序和快速排序。直接插入排序和折半插入排序通过不同方式寻找插入位置,希尔排序利用增量对序列进行划分,冒泡排序通过相邻元素比较交换,而快速排序则采用分治策略,效率最高。每种算法的时间复杂度和空间复杂度也进行了讨论。

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

排序算法


本博客排序顺序默认从小到大,非降序,数组如无特殊说明则从下标1开始到下标n

插入排序

直接插入排序

插入排序顾名思义,插入,排序,
先将前i-1个序列作为一个顺序序列,然后把第i个元素插入这个序列中,得到一个长度为i的序列
同时这种做法,因为是从后往前插入,故得到的是稳定排序
故整体时间复杂度为O(n^2),空间复杂度为O(1),
最好的情况为O(n),即元素基本有序,不需要插入太多,即for循环里面的for没怎么用过

具体代码实现如下:

void Direct_insert_sort(ElemType a[], int n){
	for(int i = 2; i <= n; i++){
		int pos  = 1, tmp = a[i];	// pos为插入的位置,默认为1,tmp表示要插入的数
		for(int j = i-1; j >= 1; j--){	// 从后往前查找第一个小于tmp的数以保证稳定性
			if(tmp>a[j]){
				pos = j+1;break;	// 找到之后,tmp的实际位置为j+1,跳出循环
			}
		}
		for(int j = i; j > pos; j--){	// 从后往前移动位置
			a[j] = a[j-1];
		}
		a[pos] = tmp;	//更新要插入的位置
		/*for(int j = 1; j <= i; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}

折半插入排序

和插入排序的区别是查找第i个元素在i-1个序列中的位置的方法是折半查找,这一个循环的复杂度降了,为O(logn)
但之后移动元素的复杂度依然是O(n)的复杂度
故整体时间复杂度为O(n^2),空间复杂度为O(1)
最好时间复杂度和直接插入同理,也是O(n)
但折半插入排序感觉没有什么优化,因为平均复杂度还是O(n^2)

具体实现代码如下:

void Binary_insert_sort(ElemType a[], int n){
	for(int i = 2; i <= n; i++){
		int l = 1, r = i-1, tmp = a[i];	// 在1~i-1中查找tmp的位置,l为其最终位置
		while(l<=r){
			int mid = (l+r)>>1;
			if(a[mid]>tmp) r = mid-1;
			else l = mid+1;
		}
		for(int j = i; j > l; j--){
			a[j] = a[j-1];
		}
		a[l] = tmp;
		/*for(int j = 1; j <= i; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}

希尔排序

个人认为乱排,勿喷

时间复杂度未知
方法为取增量d(或者说为步长也行),d可变,对序列a的子序列a[i,i+d,i+2d,…,i+kd],进行插入排序,
当d为1时,即为直接插入排序,得到不下降的序列,
或者当d为较小时,进行直接插入排序,因为此时的序列已经是基本有序序列,复杂度可降低到O(n)
时间复杂度未知,空间复杂度O(1),非稳定排序

具体实现代码如下:

void Sheel_sort(ElemType a[], int n){
	for(int d = n/2; d >= 1; d/=2){		// 增量的取值可以任意,这里取为n/2,即logn的复杂度
		for(int i = d+1; i <= n; i++){	// 对其中各个子序列进行排序
			if(a[i]<a[i-d]){
				int tmp = a[i], pos = 0;
				for(int j = i-d; j >= 1 && a[j]>tmp; j -= d){	// 进行交换位置
					a[j+d] = a[j];
					pos = j;
				}
				a[pos] = tmp;	// 进行插入
			}
		}
		/*for(int j = 1; j <= n; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}

交换排序

冒泡排序

很经典的排序方法了,就是一个一个往上走
时间复杂度为O(n^2),空间复杂度为O(1)

具体实现代码如下:

void Bubble_sort(ElemType a[], int n){
	for(int i = 1; i <= n; i++){
		bool flag = 1;
		for(int j = n; j > i; j--){	// 从后往前走,把当前子序列中最小的数推到第i个位置上,以此类推
			if(a[j]<a[j-1]){
				int tmp = a[j];
				a[j] = a[j-1];
				a[j-1] = tmp;
				flag = 0;
			}
		}
		if(flag) break;		// 减少时间复杂度,避免不必要的比较发生
	}
}

快速排序

效率最高的排序方法,分治思想
方法为以一个元素为中心把序列划分为两部分,大于这个元素和小于这个元素,
然后再对两边分别划分,以此类推,也可说是有递归的思想
时间复杂度为O(nlogn),空间复杂度为O(n)
最坏情况为O(n^2)

具体实现代码如下:

int Partition(ElemType a[], int l, int r){	// 对a进行划分
	ElemType pivot = a[l];	// 默认选择的元素为第一个,即a[l],即中心
	while(l<r){				// 跳出条件
		while(l<r && a[r]>=pivot) --r;	// 如果a[r]比中心大,则不动,同时r--,之后出现比中心小的再进行交换
		a[l] = a[r];	// 比中心小的移动到左端,
						// 注意这里的赋值是直接的,因为a[l]当前的值存储在pivote中
		while(l<r && a[l]<=pivot) ++l;	// 如果a[l]比中心小,则不动,同时l++,之后出现比中心大的再进行交换
		a[r] = a[l];	// 比中心大的移动到右端
						// 与上面同理
	}
	a[l] = pivot;	// 中心最终存放的位置l
	return l;
}
void Quick_sort(ElemType a[], int l, int r){
	if(l<r){
		int pos = Partition(a, l, r);	// 将序列a分为两个部分,确定一个元素的位置
		Quick_sort(a, l, pos-1);		// 对前半部分进行排序
		// 注意这里是pos+1不是pos,因为pos的位置已经确定了,再进行一次划分会导致死循环
		Quick_sort(a, pos+1, r);		// 对后半部分进行排序
 	}
}

选择排序

简单选择排序

没什么好说的,和冒泡排序的差别就是,冒泡是一趟交换多次交换,选择是一趟只交换一次
写冒泡排序的时候就差不多可以想到这种方法,因为冒泡也是在子序列中找一个最小的放到前面,只不过在寻找的过程中会进行元素的交换,
而简单选择排序是在子序列中直接找到一个最小的元素,然后进行交换

具体代码如下:

void Simple_select_sort(ElemType a[], int n){
	for(int i = 1; i <= n; i++){
		int min = i;
		for(int j = i+1; j <= n; j++){
			if(a[j]<a[min]) min = j;
		}
		if(min!=i){
			swap(a[i], a[min]);
		}
	}
}

堆排序 待补


归并排序 待补


基数排序 待补


堆排序,归并排序,基数排序这三种排序相对来说都是有点复杂的,就分别开一个章写把,之后会补齐链接


全部代码如下

#include <bits/stdc++.h>
using namespace std;
#define ElemType int
/*
 
排序顺序默认从小到大,非降序,数组如无特殊说明则从下标1开始到下标n

 */
/*

插入排序:
	直接插入排序
	折半插入排序
	希尔排序
*/
/*
直接插入
	插入排序顾名思义,插入,排序,
	先将前i-1个序列作为一个顺序序列,然后把第i个元素插入这个序列中,得到一个长度为i的序列
	同时这种做法,因为是从后往前插入,故得到的是稳定排序
	故整体时间复杂度为O(n^2),空间复杂度为O(1)
*/
void Direct_insert_sort(ElemType a[], int n){
	for(int i = 2; i <= n; i++){
		int pos  = 1, tmp = a[i];	// pos为插入的位置,默认为1,tmp表示要插入的数
		for(int j = i-1; j >= 1; j--){	// 从后往前查找第一个小于tmp的数以保证稳定性
			if(tmp>a[j]){
				pos = j+1;break;	// 找到之后,tmp的实际位置为j+1,跳出循环
			}
		}
		for(int j = i; j > pos; j--){	// 从后往前移动位置
			a[j] = a[j-1];
		}
		a[pos] = tmp;	//更新要插入的位置
		/*for(int j = 1; j <= i; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}

/*
折半插入排序
	和插入排序的区别是查找第i个元素在i-1个序列中的位置的方法是折半查找,这一个循环的复杂度降了,为O(logn)
	但之后移动元素的复杂度依然是O(n)的复杂度
	故整体时间复杂度为O(n^2),空间复杂度为O(1)
 */
void Binary_insert_sort(ElemType a[], int n){
	for(int i = 2; i <= n; i++){
		int l = 1, r = i-1, tmp = a[i];	// 在1~i-1中查找tmp的位置,l为其最终位置
		while(l<=r){
			int mid = (l+r)>>1;
			if(a[mid]>tmp) r = mid-1;
			else l = mid+1;
		}
		for(int j = i; j > l; j--){
			a[j] = a[j-1];
		}
		a[l] = tmp;
		/*for(int j = 1; j <= i; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}

/*
希尔排序
	乱排,时间复杂度未知
	方法为取增量d(或者说为步长也行),d可变,对序列a的子序列a[i,i+d,i+2d,...,i+kd],进行插入排序,
	当d为1时,即为直接插入排序,得到不下降的序列,
	或者当d为较小时,进行直接插入排序,因为此时的序列已经是基本有序序列,复杂度可降低到O(n)
	时间复杂度未知,空间复杂度O(1),非稳定排序
 */

void Sheel_sort(ElemType a[], int n){
	for(int d = n/2; d >= 1; d/=2){		// 增量的取值可以任意,这里取为n/2,即logn的复杂度
		for(int i = d+1; i <= n; i++){	// 对其中各个子序列进行排序
			if(a[i]<a[i-d]){
				int tmp = a[i], pos = 0;
				for(int j = i-d; j >= 1 && a[j]>tmp; j -= d){	// 进行交换位置
					a[j+d] = a[j];
					pos = j;
				}
				a[pos] = tmp;	// 进行插入
			}
		}
		/*for(int j = 1; j <= n; j++) printf("%d ", a[j]);
		printf("\n");*/
	}
}
/*
交换排序:
	冒泡排序
	快速排序
*/

/*
冒泡排序
	很经典的排序方法了,就是一个一个往上走
	时间复杂度为O(n^2),空间复杂度为O(1)
 */
void Bubble_sort(ElemType a[], int n){
	for(int i = 1; i <= n; i++){
		bool flag = 1;
		for(int j = n; j > i; j--){
			if(a[j]>a[j-1]){
				int tmp = a[j];
				a[j] = a[j-1];
				a[j-1] = tmp;
				flag = 0;
			}
		}
		if(flag) break;
	}
}

/*
快速排序
	效率最高的排序方法,分治思想
	方法为以一个元素为中心把序列划分为两部分,大于这个元素和小于这个元素,
	然后再对两边分别划分,以此类推,也可说是有递归的思想
 */
int Partition(ElemType a[], int l, int r){
	ElemType pivot = a[l];
	while(l<r){
		while(l<r&&a[r]>=pivot) --r;
		a[l] = a[r];
		while(l<r&&a[l]<=pivot) ++l;
		a[r] = a[l];
	}
	a[l] = pivot;
	return l;
}
void Quick_sort(ElemType a[], int l, int r){
	if(l<r){
		int pos = Partition(a, l, r);
		//Quick_sort(a, l, pos-1);
		Quick_sort(a, pos, r);
 
}
/*
选择排序:
	简单选择排序
	堆排序
*/
/*
归并排序
*/
/*
基数排序
 */

/*
打印数组
 */
void Print(ElemType a[], int n){
	for(int i = 1; i <= n; i++){
		printf("%d ", a[i]);
	}
}
int main(){
	int a[] = {0,4,2,1,5,6,3,7,9,8,0};	// 第一个元素无效
	int n = 10;
	// Direct_insert_sort(a,n);
	// Binary_insert_sort(a,n);
	// Sheel_sort(a,n);
	Quick_sort(a,1,n);
	Print(a,n);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值