【数据结构】冒泡排序&&快速排序

1.交换排序

  • 冒泡排序
  • 快速排序

2.冒泡排序

1)基本思想

一次确定一个最大值或者最小值,两两比较,将最大值或者最小值交换到最右边或者最左边,N个元素需要N-1次排序
【该图片转载博主一像素】
在这里插入图片描述

2)分析

冒泡排序最好情况的时间复杂度为:O(n)最坏情况为O(n2)
空间复杂度为O(1)
稳定

3)与插入排序的比较

如果差一点点有序,插入排序会更好;因为冒泡是更严格的排序,冒泡在有序后,还要在冒一次,才知道他已经有序,才停止冒泡。但插入排序只需要走一遍,加挪动一次就可以搞定。

4)代码

void BubbleSort(int* num, int len)
{
	if (num == NULL || len <= 0)
		return;
	//确定循环躺数
	for (int i = 0; i < len - 1; i++)
	{
		//确定比较次数
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (num[j]>num[j + 1])
				Swap(&num[j], &num[j + 1]);
		}
	}
}


2.快速排序(冒泡排序的改进)

1)基本思想

任取待排序元素序列中某元素作为基准值,按照基准值待排序集合分割为两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程
【该图转载博主一像素】
在这里插入图片描述

2)分析

快排的最坏场景:待排序列是有序的,左右子序列数有一个为空,二叉树深度为n,则时间复杂度为O(nn),空间复杂度为O(n)。
最好场景:每次选的数都是序列最中间的数字,二叉树深度为logN,时间复杂度为O(N
logN ).空间复杂度为O(logN)
不稳定的

3)代码

void QuickSort(int* num, int left, int right)
{
	if (num == NULL)
		return;
	//递归出口
	if (left >= right)
		return;
	//按照基准值将待排序区间划分为两个子区间
	int div = PartSort1(num, left, right);
	//子问题排序左子区间
	QuickSort(num, left, div - 1);
	//子问题排序右子区间
	QuickSort(num, div + 1, right);
}

将区间按照基准值划分为左右两半部分的常见方式有:三种实现
左右指针法
int PartSort3(int* num, int begin, int end)
{
	 int key = num[end];
	 int last = end;
	 while (begin < end)
	 {
	  //1.左边找到大于基准值的元素
	  while ((begin < end) && (num[begin] <= key))
	   begin++;
	  //2.右边找到小于基准值的元素
	  while ((begin < end) && (num[end] >= key))
	   end--;
	  //3.交换两个值
	  Swap(&num[begin], &num[end]);
	 }
	 //两个下标走到一块的时候,把基准值交换过来
	 Swap(&num[begin], &num[last]);
	 //返回基准值的位置
	 return begin;
}
挖坑法
int PartSort2(int* num, int begin, int end)
{
	int key = num[end];//把基准值拿出来(挖一个坑)
	while (begin < end)
	{
		//1.左边找到大于基准值的元素,并放入坑里
		while ((begin < end) && (num[begin] <= key))
			begin++;
		num[end] = num[begin];
		//2.右边找到小于基准值的元素,并放入坑里
		while ((begin < end) && (num[end] >= key))
			end--;
		num[begin] = num[end];
	}
	//3.把拿出来基准值放入坑里
	num[begin] = key;
	//返回基准值的位置
	return begin;
}
前后指针
int PartSort3(int* num, int begin, int end)
{
	int prev = begin - 1;
	int cur = begin;
	int key = num[end];
	while (cur < end)
	{
		//++prev=cur说明这个元素时第一个元素或者这是一段连续的小于基准的序列
		if ((num[cur] < key) && (++prev != cur))
			Swap(&num[cur], &num[prev]);
		cur++;
	}
	//把基准元素放在所有小于基准元素的紧邻后边,可以达到基准的左边小于基准,右边大于基准
	Swap(&num[++prev], &num[end]);
	return prev;
}

4)快排的优化

三数取中法优化
int GetMidKey(int* num, int begin, int end)
{
	assert(num);
	int mid = begin + (end - begin) / 2;
	if (num[begin] < num[mid])
	{
		if (num[mid] < num[end])
			return mid;
		else
		{
			if (num[begin]>num[end])
				return begin;
			else
				return end;
		}
	}
	else
	{
		if (num[begin] < num[end])
			return begin;
		else
		{
			if (num[mid]>num[end])
				return mid;
			else
				return end;
		}
	}
}

int PartSort3(int* num, int begin, int end)
{
	 //三数取中优化
	 int index = GetMidKey(num, begin, end);
	 //和要选的基准交换
	 Swap(&num[index], &num[end]);
	 int key = num[end];
	 int last = end;
	 while (begin < end)
	 {
	  //1.左边找到大于基准值的元素
	  while ((begin < end) && (num[begin] <= key))
	   begin++;
	  //2.右边找到小于基准值的元素
	  while ((begin < end) && (num[end] >= key))
	   end--;
	  //3.交换两个值
	  Swap(&num[begin], &num[end]);
	 }
	 //两个下标走到一块的时候,把基准值交换过来
	 Swap(&num[begin], &num[last]);
	 //返回基准值的位置
	 return begin;
}
底层区间优化

void QuickSort1(int* num, int left, int right)
{
	if (num == NULL)
		return;
	//递归出口
	if (left >= right)
		return;
	//小区间优化(替换掉后边几层的递归)
	if (right - left + 1 < 10)
		InsertSort(num, right - left + 1);
	int div = PartSort1(num, left, right);
	QuickSort1(num, left, div - 1);
	QuickSort1(num, div + 1, right);
}

将递归快排改为循环快排

栈的相关函数:https://blog.csdn.net/weixin_41892460/article/details/82973851

void QuickSortNonR(int* num, int left, int right)
{
	if (num == NULL || right <= left)
		return;
	Stack st;
	StackInit(&st);
	//先将整个区间压栈
	StackPush(&st, left);
	StackPush(&st, right);
	while (StackEmpty(&st) != 0)
	{
		//取栈顶并且出栈
		int end = StackTop(&st);
		StackPop(&st);
		int begin = StackTop(&st);
		StackPop(&st);
		//先划分主区间,固定好一个基准
		int div = PartSort1(num, begin, end);
		//如果左子序列还有大于1个元素,继续压栈
		if (begin < div - 1)
		{
			StackPush(&st, begin);
			StackPush(&st, div - 1);
		}
		//如果右子序列还有大于1个元素,继续压栈
		if (div + 1 < end)
		{
			StackPush(&st, div + 1);
			StackPush(&st, end);
		}
	}
	StackDestroy(&st);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值