排序(一)插入 希尔 选择 堆排 冒泡

排序在日常生活中应用很广,帮助我们进行筛选,我们处在一个筛选盛行的世界,中考,高考,考研,考编,不都在筛选嘛。

我们以升序为例

1.插入排序

1.1 直接插入排序

从a[1]到a[n]依次插入到前面已经有序的序列中

时间复杂度最好是O(N),初始即有序;最坏是O(N^2),初始逆序

c 复制代码
void InsertSort(int* a,int n){
	int tmp,end;
	for (int i = 1; i < n; i++) {//将a[i]插入到前面有序序列中
		tmp = a[i];
		end = i - 1;
		while (end >= 0) {//将tmp插入到a[end]及之前有序的序列中
			if (tmp < a[end]) {//如果tmp<a[end],就让a[end]向后挪一个单位
				a[end + 1] = a[end];
				end--;
			}
			else
				break;
		}
		a[end + 1] = tmp;//要么是tmp>=a[end],要么是end=-1(说明此时tmp小于序列中所有的数),此时将tmp插入
	}
}

1.2 希尔排序

预排序,分组预排,这样大的数据就能减少移动到最后的次数,大的数更快的跳到后面,小的数更快的跳到前面。

时间复杂度是O(NlogN)

我们举个例子,刚开始gap≈n/3,一共有n/3组,每组大约有3个元素,最多挪动1+2=3次,3*(n/3)=n,时间复杂度是O(N);当gap很小,gap=1,是直接插入排序,但是这时候不考虑最坏情况下时间复杂度,考虑最好情况下时间复杂度,因为经过前面的多轮预排序,此时接近有序。

下图出自严蔚敏老师的《数据结构(C语言版)》

下图出自殷人昆老师的《数据结构精讲与习题详解(C语言版)(第2版)》

c 复制代码
void ShellSort(int* a, int n) {
	int tmp,end,gap=n,i,j;
	while (gap > 1) {
		gap /= 2;
		for (j = 0; j < gap; j++) {
			for (i = gap + j; i < n; i++) {
				end = i - gap;
				tmp = a[i];
				while (end >= 0) {
					if (tmp < a[end]) {
						a[end + 1] = a[end];
						end--;
					}
					else
						break;
				}
				a[end + 1] = tmp;
			}
		}
	}
}

也可以像下面这样写,但效率并没有提高,看时间复杂度一味地看循环不是很准确,还是要看逻辑

c 复制代码
void ShellSort(int* a, int n) {
	int tmp, end, gap = n, i;
	while (gap > 1) {
		gap /= 2;//也可改为gap=gap/3+1;
		for (i = gap; i < n; i++) {//i=gap,gap+1.gap+2,...进行的是每组第一次间隔为gap的希尔排序,i=2gap,2gap+1.2gap+2,进行的是每组第二次的希尔排序,以此类推
			end = i - gap;
			tmp = a[i];
			while (end >= 0) {
				if (tmp < a[end]) {
					a[end + gap] = a[end];
					end-=gap;
				}
				else
					break;
			}
			a[end + gap] = tmp;
		}
	}
}

2.选择排序

c 复制代码
//下面两个函数要用的交换函数
void Swap(int* p1, int* p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

2.1 简单选择排序

朴实无华,蛮干派,又名"躺平排序",因为无论原始数据是否有序,也即最好情况、最坏情况时间复杂度都是O(N^2),因为要依次比较n-1, n-2, n-3, 2个元素并从中选出最大值,可是效率太低啊,我们要一些技巧和方法的

c 复制代码
void SelectSort(int* a, int n) {
	int left=0, right=n-1,i,maxi,mini;
	while (left < right) {
		maxi = mini = left;
		for (i = left + 1; i <= right; i++) {
			if (a[i] < a[mini])
				mini = i;
			else if (a[i] > a[maxi])
				maxi = i;
		}
		Swap(&a[mini], &a[left]);
		if (left == maxi)//没有必要管mini是否等于maxi,一旦mini和maxi相等,说明整个left到right都是同一个值,无所谓交换
			maxi = mini;
		Swap(&a[maxi], &a[right]);

		left++;
		right--;
	}
}

2.2 堆排序

时间复杂度是O(NlogN)

堆排是简单选择排序的优化,将待排序的数组逻辑结构看作完全二叉树,先向下调整建堆,之后交换堆顶元素和末尾元素,再处理剩下的n-1个元素,堆顶元素向下调整为堆,以此类推,直到处理完第二个元素

注:向下调整用于建堆、Top K、堆排比向上调整效率更高,但是插入元素后只能用向上调整建堆,因为如果想用向下调整,要挪动本来的n个元素,时间复杂度就已经是O(N),接着,原本堆中节点的父子关系乱了,没有原来节点间的大小关系,没法运用堆的性质,更麻烦

c 复制代码
void AdjustDown(int* a, int n, int parent) {
	int child = parent * 2 + 1;//左孩子
	while (child<n) {
		if (child + 1 < n && a[child + 1] > a[child])//找到孩子节点值较大的
		child++;
		if(a[child]>a[parent])
			Swap(&a[parent], &a[child]);//交换父子节点的值
		
		parent = child;
		child = parent * 2 + 1;
	}
}

void HeapSort(int* a, int n) {
	//先建大根堆,向下调整建堆
	int child = (n-2) / 2;//最后一个分支节点的下标
	while (child >= 0) {//向下调整,从最后一个分支节点调到根节点
		AdjustDown(a, n,child);
		child--;
	}
	for (int i = n - 1; i > 0; i--) {//外层循环是N,内层循环是logN
		Swap(&a[0], &a[i]);
		AdjustDown(a, i, 0);
	}
}

3.交换排序

3.1 冒泡排序

实用价值不大,有教学意义

现在各种语言排序底层基本都是快排,基础学科、基础知识的突破是技术革命的关键!!!

从a[0]开始往后冒,如果a[i]>a[i+1],交换两个元素的值,将最大的元素冒到末尾,接着将次大元素冒到倒数第二个元素,以此类推

加入flag优化,如果一趟下来没有任何交换,说明已经有序,此时最好时间复杂度是O(N).

刚开始逆序,时间复杂度是O(N^2).

c 复制代码
void BubbleSort(int* a, int n) {
	int flag = 0, j;
	for (int i = 0; i <n-1; i++) {//0到n-2往后冒
		for (j = 0; j < n - i-1;j++ ) {
			if (a[j] > a[j +1 ]) {
				Swap(&a[j], &a[j + 1]);
				flag = 1;
			}
		}
		if (flag == 0)//说明此次未发生交换,已有序,跳出循环
			break;
	}
}

void BubbleSort2(int* a, int n) {
	int flag=0,j;
	for (int i = n - 1; i > 0; i--) {
		j = i;
		while (j > 0) {
			if (a[j] < a[j - 1]) {
				Swap(&a[j], &a[j - 1]);
				flag = 1;
			}
			j--;
		}
		if (flag==0)//说明此次未发生交换,已有序,跳出循环
			break;
	}
}

快排和归并内容较多,分别放一篇文章~

相关推荐
lihao lihao2 小时前
模板进阶
java·数据结构·算法
慕容青峰2 小时前
【加拿大计算机竞赛 CCO 小行星采矿】题解
c++·算法·sublime text
Ghost-Silver2 小时前
2025年度总结
开发语言·数据结构·c++·算法
谈笑也风生2 小时前
经典算法题型之排序算法(四)
数据结构·算法·排序算法
AI科技星2 小时前
空间螺旋电磁耦合常数 Z‘:拨开迷雾,让电磁力变得直观易懂
服务器·人工智能·科技·算法·生活
亚伯拉罕·黄肯2 小时前
强化学习算法笔记
笔记·算法
only-qi2 小时前
LeetCode 148. 排序链表
算法·leetcode·链表
岁岁的O泡奶3 小时前
NSSCTF_crypto_[SWPUCTF 2023 秋季新生赛]dpdp
经验分享·python·算法·密码学
smj2302_796826523 小时前
解决leetcode第3791题.给定范围内平衡整数的数目
python·算法·leetcode