数据结构 之 【排序】(直接选择排序、堆排序、冒泡排序)

目录

1.直接选择排序

1.1直接选择排序的思想

1.2直接选择排序的代码逻辑

1.3完整排序代码

1.3.1一次只选一个最值

1.3.2一次筛选出两个最值

1.4直接选择排序的时间复杂度与空间复杂度

2.堆排序

2.1堆排序的思想

2.2堆排序的具体步骤

2.3堆排序图解

2.4完整排序代码

3.冒泡排序

3.1冒泡排序的思想

3.2冒泡排序图解

3.3单趟排序代码(一轮遍历进行的操作)

3.4完整排序代码

3.5冒泡排序的时间复杂度与空间复杂度


所有排序的实现皆以升序为例

1.直接选择排序

1.1直接选择排序的思想

直接选择排序的思想就是对数组进行多轮遍历 ,在每一轮遍历中,从当前未排序 部分的元素里选出最大值或最小值 ,然后依据排序要求,将该最大值或最小值与未排序部分的起始位置或末尾位置的元素 进行交换,从而逐步确定数组中各元素的最终位置,最终实现数组有序

1.2直接选择排序的代码逻辑

就是根据思想:遍历+筛选

1.3完整排序代码

1.3.1一次只选一个最值

复制代码
void SelectSortUp1(int* a, int n){
	for(int j = 0; j < n - 1; ++j){
		int min = j;
		for (int i = j + 1; i < n; ++i){
			if (a[i] < a[min])
				min = i;
		}
		Swap(&a[j], &a[min]);
	}
}

以升序为例,一次遍历(对未排序部分的遍历)选出的最小值放在未排序部分的起始位置

内层循环进行的是特定的某一次遍历的筛选过程

外层循环控制了每次遍历时的起始位置以及交换过程

1.3.2一次筛选出两个最值

复制代码
void SelectSortUp2(int* a, int n){
	int left = 0, right = n - 1;
	while(left < right){
		int min = left, max = left;
		for (int i = left + 1; i <= right; ++i){
			if (a[i] < a[min])
				min = i;

			if (a[i] > a[max])
				max = i;
		}
		Swap(&a[min], &a[left]);
		if (left == max)
			max = min;
		Swap(&a[max], &a[right]);

		++left;
		--right;
	}
}

对思想进行精进,一次遍历,选出未排序部分的最大和最小值,按需将其放在对应位置

leftright控制的是未排序部分的区间大小

一次遍历选出最大最小值后,以升序为例,

最小值放在未排序部分的起始位置,最大值放在未排序部分的末尾

然后缩小区间

1.4直接选择排序的时间复杂度与空间复杂度

假设数组有N个元素,一次只选出一个最值,则最坏情况下,第一轮遍历需遍历N个元素,第二轮遍历序遍历N-1个元素.............第N-1轮遍历需遍历2个元素,遍历结束,遍历次数为((N+2)*(N-1))/2

所以时间复杂度为O(N^2)

一次筛选出两个最值,则最坏情况下,第一轮遍历大约需遍历N个元素,第二轮遍历需遍历N-2个元素.............一共约遍历N/2轮,遍历次数为 ((2+N)*N)/ 2,时间复杂度为O(N^2)
直接选择排序的变量个数固定,所有操作均在原数组上,所以空间复杂度为O(1)

2.堆排序

关于堆,可参考前两期博客 数据结构 之 【堆】堆的应用

2.1堆排序的思想

数组可以模拟完全二叉树,堆是一种完全二叉树,且具备选数的功能,那么

将数组持续变为堆,并对特定的堆选出最值,最终可实现数组有序

2.2堆排序的具体步骤

1.建堆:按需将所给数组变为大(小)堆 (建堆的具体步骤参考 堆的应用 这一期博客)

升序建大堆,降序建小堆

2.交换与调整:以升序为例,建成大堆后,将堆顶元素(为未排序部分的最大值)与末尾位置的元素进行交换,然后对末尾位置之前的所有元素进行向下调整操作,使其再次成为大堆,然后交换堆顶元素与未排序部分的末尾位置的元素,........循环进行调整与交换操作,直到未排序部分的元素个数为1为止

2.3堆排序图解

2.4完整排序代码

复制代码
void AdjustDown(HpDateType* a, int n, int parent){
	int child = parent * 2 + 1;
	//有左孩子才进行调整
	while (child < n)//n是节点个数{
		//左孩子一个逻辑,右孩子一个逻辑,直接假设
		//child是左右孩子中较大的一个孩子的下标
		//注意右孩子的有无(防止越界访问与无效数据)
		if (child + 1 < n && a[child] < a[child + 1]){
			++child;
		}
		//与左右孩子中较大的一个孩子进行比较
		if (a[parent] < a[child]){
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else    break;
	}
}
void HeapSortUp(int* a, int n){
	//从第一个非叶子节点开始建堆
	for (int i = (n - 2) / 2; i >= 0; --i){
		AdjustDwon1(a, n, i);
	}
	//堆顶元素与末尾交换,并向下调整
	//显然是循环
	int end = n - 1;
	while (end > 0){
		Swap(&a[0], &a[end]);
		AdjustDwon1(a, end, 0);
		--end;
	}
}

通过逐步缩小堆的范围来排序数组

当堆中只剩一个元素时(即 end = 0),排序过程已完成, 所以 end > 0作为循环结束条件

2.5堆排序的时间复杂度与空间复杂度

堆排序的时间复杂度O(N*logN),可用最坏情况下的交换次数进行衡量

空间复杂度为O(1)

3.冒泡排序

3.1冒泡排序的思想

进行多轮遍历 ,每轮遍历中,依次比较相邻元素 ,若不符合目标顺序(升序则前大后小,降序则前小后大),就交换它们的位置。如此,每轮遍历会将当前未排序部分的最大(小)值"冒泡"到数组一端。当某一轮遍历无元素交换时,排序完成

3.2冒泡排序图解

3.3单趟排序代码(一轮遍历进行的操作)

复制代码
for (int j = 0; j < n - 1; ++j){
	if (a[j] > a[j + 1]){
		Swap(&a[j], &a[j + 1]);
	}
}

上述代码展示的是第一次遍历进行的操作,数组元素两两进行比较,j + 1 < n,所以 j < n - 1

因为是升序,前一个数比后一个数大就交换

3.4完整排序代码

复制代码
void BubbleSortUp(int* a, int n){
	//一趟只正确放好一个数
	//n个数n - 1 趟
	for(int i = 0; i < n - 1; ++i){
		//经过一趟少一次比较
		for (int j = 0; j < n - 1 - i; ++j){
			if (a[j] > a[j + 1]){
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

冒泡排序遍历一轮可正确放好一个数组元素,则有n个数组元素的数组只需遍历n-1轮

冒泡排序遍历一轮可正确放好一个数组元素,那么遍历一轮可减少一次比较次数

小优化

内层循环未进行交换操作则证明数组有序,此时可终止循环,减少不必要的循环操作

复制代码
void BubbleSortUp(int* a, int n){
	//一趟只正确放好一个数
	//n个数n - 1 趟
	for(int i = 0; i < n - 1; ++i){
		int flag = 0;
		//经过一趟少一次比较
		for (int j = 0; j < n - 1 - i; ++j){
			if (a[j] > a[j + 1]){
				Swap(&a[j], &a[j + 1]);
				flag = 1;
			}
		}
	    if (!flag)
			break;
	}
}

3.5冒泡排序的时间复杂度与空间复杂度

假设数组有N个元素,则最坏情况下,第一轮遍历需交换N-1次,第二轮遍历需交换N-2次,

.............第N-1轮遍历需交换1次,交换总次数为 ((N-1)*N) / 2,时间复杂度为O(N^2)
变量个数固定,所有操作均在原数组上,空间复杂度为O(1)

相关推荐
闪电麦坤958 小时前
数据结构:字符串(Strings)
数据结构
宴之敖者、9 小时前
数组——初识数据结构
c语言·开发语言·数据结构·算法
青小莫9 小时前
c语言-数据结构-二叉树OJ
c语言·开发语言·数据结构·二叉树·力扣
典学长编程9 小时前
Java从入门到精通!第十一天(Java常见的数据结构)
java·开发语言·数据结构
胡斌附体9 小时前
oracle查询数据结构滤涉及的sql语句
数据结构·sql·oracle
不想学习\??!10 小时前
c练习-c基础
数据结构·算法
晨曦学习日记13 小时前
Leetcode题解:209长度最小的子数组,掌握滑动窗口从此开始!!!
数据结构·算法·leetcode
Xiaoxiaokm14 小时前
LinkedList与链表(单向)(Java实现)
java·数据结构·链表
Dubhehug14 小时前
104.二叉树的最大深度
java·数据结构·算法·leetcode