初识与再探快速排序

初始快速排序

1、问题引入:

2、问题分析:

这本身是一个简单的 绝对值排序 问题,但是使用 选择排序 冒泡排序 均出现了超时问题,这是由于数的数量最大达到10^5这时候经过学习,了解了常见排序算法的 时间复杂度 ,这时候初始了都够更快解决问题的  快速排序  法。

3、初始快排:

快速排序是一种基于分治的快速排序,是对冒泡排序的一种改进,是非常重要且应用比较广泛的一种高效率排序算法。

大致步骤 :

  • 1.先从数列中取出一个数作为基准数。
  • 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  • 3.再对左右区间重复第二步,直到各区间只有一个数。

代码呈现:

//一种快速排序方式
void quickSort(int* arr, int low, int high) {
	int i = low;            //由i从左向右判断
	int j = high;           //由j从右向左判断
	int temp = arr[low];    //记录第一个基准值。(可以不为第一个)

	if (i >= j)             //判断边界条件。(判断结束排序递归)
		return;

	while (i != j) {        //判断每一次分块是否完成
		while (i < j && arr[j]>=temp)     //从右向左找到小于基准值的数后停止
			j--;

		while (i < j && arr[i] <= temp)  //从左向右找到大于基准值的数后停止
			i++;

		if (i < j)
			swap(&arr[i], &arr[j]);    //交换两个被找到的数,使大于基准值的都在右边,小于的在左
	}
//**关于循环中的( i<j )条件 与 常讨论的左右排序前后问题:由于需要完全分块,即将基准值调到左边,由于选择的是第一个数作为基准值,所以需要与它交换的是小于基准值的值,在哪里停止就尤为重要。

//**先判断从右向左的原因 :若从左向右判断,最后一趟交换会从 i++ 到达 i=j。而此时的j是刚交换完的大于基准值的值。导致最终完成完全分块发生问题。(交换数为大值)

	swap(&arr[low], &arr[i]);     //在前面的条件判断中,需要保证arr[i]<temp,交换基准值与arr[i],完成完全分块。(  <temp  ;    temp    ;    >temp   )

	quickSort(arr, i + 1, high);   //递归完成两块的后续分块
	quickSort(arr, low, i - 1);
}

重要部分!:

完成完全分块  (      <temp  ;    temp    ;    >temp   )需要将基准值调到中间,基准值在此代码中选择的是第一个数。
 

//** 关于循环中加入的( i<j )条件 与 常讨论的左右排序前后问题:由于需要完全分块,即将基准值调到左边,由于选择的是第一个数作为基准值,所以需要与它交换的是小于基准值的值,在哪里停止就尤为重要,(i<j)条件加入使(i=j)时跳出循环

//** 先判断从右向左的原因 :若从左向右判断,最后一趟交换会从 i++ 到达 i=j。而此时的j是刚交换完的大于基准值的值。导致最终完成完全分块发生问题。(交换数为大值)

//**若基准值选择的是最后一个数,则最后需要交换的为大于基准值的值,所以需要从左向右

在再探快速排序部分将深入解决这一问题。

4、问题解决:

//不同于前一种快排的优化版
# include<stdio.h>
# include<math.h>
int a[100005], n;
void Quicksort(int a[], int l, int r)
{
	//1. check boundary.
	if (l >= r)
		return;

	//2. partition
	int mid = l + (r - l) / 2, i = l - 1, j = r + 1, x = abs(a[mid]);
	while (i < j)
	{
		do i++; while (abs(a[i]) < x);
		do j--; while (abs(a[j]) > x);
		if (i < j)
		{
			int t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}//每次partition会执行多次while, 但一旦一次while结束, 就完成了一次划分. 左边所有数小于基准数 右边所有数大于基准数.

	//3. recurrence.
	Quicksort(a, l, j);
	Quicksort(a, j + 1, r);
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
	Quicksort(a, 0, n - 1);
	for (int i = 0; i < n; ++i)
		printf("%d ", a[i]);
	printf("\n");
	return 0;
}

再探快速排序

问题引申:

仔细观察问题解决的快速排序,与之前初识快排部分有较大的不同。它去除了最后的交换步骤。

没有完成完全分块。我们来深入看看优化版的代码。

  • 1、基准值的选择。基准值选择了数组的中间数,没有完成完全分块,循环判断最后实现的形式为 (  <  temp    ;      >   temp   )    temp的位置不确定
  • 2、【根本优化】循环结束条件判断。先看先前版本下因为需要分为三块,而使用( i = j )作为最后结束标志——诱发了判断时左右方向不同的不同结果;而优化版本下不使用 ( i = j )为结束标志,最后结束时 i 的位置为 右边分块的最左边j 的位置为左边分块的最右边,( i > j )顺利退出循环,由于最后位置确定,所以从左往右还是从右往左都无关紧要了。
  • 3、递归实现。分为两块后可以通过 i 或 j的位置来实现左右分块的递归.
  • 4、优化版中do-while运用与while效果类似,无特殊作用

问题的再拓展:

以上版本为 快速排序 的 hoare版本 和 其优化版。还有别的版本和优化后续将会更新;希望大家指出代码和注释的问题(我自己绕的也很晕……

  • 36
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值