数据结构进阶篇 之 【交换排序】(冒泡排序,快速排序递归、非递归实现)详细讲解

在这里插入图片描述
当你觉的自己不行时,你就走到斑马线上,这样你就会成为一个行人

一、交换排序

1.冒泡排序 BubbleSort

1.1 基本思想

1.2 实现原理

1.3 代码实现

1.4 冒泡排序的特性总结

2.快速排序 QuickSort

2.1 基本思想

2.2 递归实现

2.2.1 hoare版
2.2.2 前后指针版本

2.3 快速排序优化

2.3.1 随机数选key
2.3.2 三数取中选key
2.3.3 递归到小的子区间使用插入排序

2.4 非递归实现

2.5 快速排序的特性总结

二、完结撒❀

前言:

所谓交换排序,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是(以升序为例):将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

一、冒泡排序

对于冒泡这个初学者必学的排序,我想大家应该都不陌生,现在我们以排升序为例再简单学习回顾一下,为下面快速排序做铺垫。

1.1 基本思想

冒泡排序是从头开始让两个相邻位置的数进行大小比较,不符合升序的将两者进行交换,再继续向后比较两个数据大小,反复执行一轮上述操作,便可以将数组中最大数据排到队尾,再从头进行一轮上述操作便可以将数组中第二大的数据排到数组中倒数第二个位置,反复执行n-1次即可完成排序(n为数据总个数)

1.2 实现原理

在数组arr[n]中,开始将数组arr[0]与arr[1]进行大小比较,若arr[1]<arr[0]就将两者数值进行交换后继续进行arr[1]与arr[2]的大小比较,若arr[1]>arr[0]就继续向后进行arr[1]与arr[2]的大小比较,直到比较到arr[n-1]为止,这时arr[n-1]便是数组中最大的值,再执行一轮上述操作便可以将数组中第二大的数值存放到arr[n-2]当中,反复执行n-1次便完成数组的总排序

动态图解:
在这里插入图片描述

1.3 代码实现

//冒泡排序
void BubbleSort(int* a, int n)
{
   
   
	assert(a);

	for (int t = 1; t < n; t++)
	{
   
   
		int exchang = 0;//优化
		for (int i = 0; i < n - t; i++)//优化
		{
   
   
			if (a[i] > a[i + 1])
			{
   
   
				int tmp = a[i];
				a[i] = a[i + 1];
				a[i + 1] = tmp;
				exchang = 1;
			}
		}
		if (exchang == 0)
		{
   
   
			break;
		}
	}
}

代码中对冒泡排序进行了两处优化,大家认真品味,加深理解。

1.4 冒泡排序的特性总结

1. 冒泡排序是一种非常容易理解的排序

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

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

4. 稳定性:稳定

2.快速排序

2.1 基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码(数组下标)将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

2.2 递归实现

2.2.1 Hoare版

Hoare版就是Hoare大佬自己创作的最原始的快速排序。

根据上述基本思想我们可以发现快速排序递归实现与二叉树的前序遍历规则非常像,在代码的实现中我们就可参照二叉树前序递归实现快速排序代码的递归框架。

我们先认真观察下面快速排序的动态图解:
在这里插入图片描述上面1动态图解表示的是快速排序的一次单趟过程:

以数组左边第一个值为key,R先向左走找小于key的数就停下来(5),接着L向右走找大于key的值就停下来(7),再将L和R对应位置数值进行交换,接着R继续向左走找小于key的值(4),找到后停下L向右走找大于key的值(9),之后停下再将两数值进行交换,继续重复上述操作直到L与R相遇,L与R相遇之后再将相遇下标对应的值与key下标对应的值进行交换,这样就完成了一次单趟快速排序,此时key的右边都为小于key的值,右边都是大于key的值。

快速排序单趟实现代码

void Swap(int* p, int* q)
{
   
   
	int tmp = *p;
	*p = *q;
	*q = tmp;
}

//快速排序(单趟)
void PartSort(int* a, int left, int right)
{
   
   
	assert(a);

	int keyi = left;

	while (left < right)
	{
   
   
		while (left<right && a[right] >= a[keyi])
		{
   
   
			--right;
		}

		while (left<right && a[left] <= a[keyi])
		{
   
   
			++left;
		}

		Swap(&a[right], &a[left]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = left;
}

这里强调一下,一趟快速排序开始必须先让R往左边走,保证在最后L与R相遇时下标对应的值小于key下标对应的值

那么我们单趟排序便实现完成,下面就要开始展开研究递归问题。

递归就分为:1.递归子问题 2.最终子问题

我们对一串数组进行一次单趟的快速排序之后,数组并没有完全实现有序,还要“分割”key左右两边对其再分别进行快速排序,再将“分割”的一段再按照快排后key的位置在进行分割再进行单趟排序,直到分割为一个数据的时候就可以返回,也就是L = R,最后也可能为空

如图所示:
在这里插入图片描述
递归顺序我们就可以按照二叉树前序进行设计,所以代码实现如下:

//快速排序(霍尔递归)
void QuickSort1(int* a, int left, int right)
{
   
   
	
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值