【C语言】【数据结构初阶】 选择排序 、快速排序(重点)- 详解

一.选择排序

1.思想

选择排序是一种简单直观的排序算法。 它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。 以此类推,直到全部待排序的数据元素的个数为零。

我们可以对其做一个优化:我们一趟选择,同时选出最小的和最大的,而非只选出最小/最大的。

但是选择排序的时间复杂度为O(n^2),最好情况下,也为O(n^2)

2.代码

void Swap(int* p, int* q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;
}
//选择排序
void SelectSort(int* a, int n)
{
	int begin = 0; 
	int end = n - 1;
	while (begin < end)
	{
		int maxi = begin;
		int mini = begin;
		for (int i = begin; i <= end; i++)
		{
			if (a[maxi] < a[i])
			{
				maxi = i;
			}
			if (a[mini] > a[i])
			{
				mini = i;
			}
		}
		Swap(&a[begin], &a[mini]);//将最小值交换到begin处
		//begin == maxi 时,最大的数被换到了mini的标记处,需要修正一下maxi的位置
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);//将最大值交换到end处
		begin++;
		end--;
	}
}

3.运行结果

二. 快速排序

1.hoare版本

(1)实现思路:

标记一个数为key,一般为最开始的值或者最末尾的值。然后定义L、R两个下标,L为最左边数的下标,R为最右边数的下标。通过L++和R--,根据需求(交换完成后,key左边的数都小于key,key右边的数都大于key)来交换这两个下标指向的值,如下图所示:

 然后6成为新的key的值。

注意:选最左边的值做key,让右边先走。

选最右边的值做key,让左边先走。

(2)代码(递归思想)

一趟排完后,key的左边都比key小,key的右边都比key大。如果key的左边变成有序的,key的右边也变成有序的,那么整体就有序了。

比如先使得左边变成有序,那么我们将左边进行一趟排序,这一趟排序的区间就比第一次变小了。

再排序再变小,最后区间变为1或者空。我们使用递归来解决这个问题。

//hoare版本单趟循环
int Partion(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);
	return left;
}

//快速排序
void QuickSort(int* a, int left,int right)
{
//递归跳出条件
	if (left >= right)
	{
		return;
	}
	int keyi = Partion(a, left, right); 
//对左边区间进行快排
	QuickSort(a, left, keyi - 1);
//对右边区间进行快排
	QuickSort(a, keyi + 1, right);
}

(3)运行结果

 2.改进:

(1)缺陷:

当原数列是有序数列时,使用递归会很糟糕(可能会导致栈溢出等)

(2)代码

所以,我们将其进行改进。

//解决快排面对有序的选keyi问题:
//  1.随机选key(但是由于是随机,也不大好)
//  2.三数取中 (更好一些)   左边、中间、右边取不是最大也不是最小的那个做key

int GetMidIndex(int* a, int left, int right)
{
	int mid = left + (right - left) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}

}

int Partion(int* a, int left, int right)
{
//找出三数中 中间的数,将其与left的位置的值交换
	int mini = GetMidIndex(a, left, right);
	Swap(&a[mini], &a[left]);
//其它不变
	int keyi = left;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);
	return left;
}

3.快速排序的另外一种方法:挖坑法

挖坑法实质是上面那种方法的变形。

(1)代码

//挖坑法
int Partion(int* a, int left, int right)
{
	//三数取中
	int mini = GetMidIndex(a, left, right);
	Swap(&a[mini], &a[left]);

	int key = a[left];
	int pivot = left;
	while (left < right)
	{
		//右边找比key的值小的数,放入左边的坑里
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[pivot] = a[right];
		pivot = right;

		//左边找比key 的值大的数,放入右边的坑里
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[pivot] = a[left];
		pivot = left;

	}
	a[pivot] = key;
	return pivot;

}

(2)运行结果

 

  • 26
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杯酒问苍天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值