从快速排序开始

起步

打算重新学习下算法记录到博客,只为整理下自己的思路、备忘,所以内容难免存在很多主观看法和不严谨的地方。

如果有幸被检索到,内容好坏还请大家自己甄别,有问题的地方,也可以能够提出来大家共同学习。

快速排序

从快速排序和归并排序开始,因为对于算法学习来讲,这两个排序是理解递归的一个很好的切入点。

概要

快排思想:就是把数组首位数字v放入数组有序时它该处的位置,同时使其左右数据满足如下描述性质:

[|v|..................] ==> [....<v....|v|....>v.....]

这个步骤称之为partition.

可以发现经过一次partition,v的序列位定下来了,同时其他的数据虽然没有排好序,但是至少处在它该处的区间。然后对 <v 和 >v 左右区间再次进行同样的操作(递归),可以预见区间被划分得越来越多也越来越小,数据被分割得越来越接近排序的位置。最后完全停留在它的序列位上,此时整个数组有序。理解了大体流程以,就可以写出快排的伪代码:

int _partition(int a[], int lo, int hi);  
void _quicksort(int a[], int lo, int hi){  
    if (lo >= hi)  
        return;  
    int j = _partition(a, lo, hi);  
    _quicksort(a, lo, j - 1);  
    _quicksort(a, j + 1, hi);  
}

类似于二分法,此数组理想情况下被分割的次数是logN,每次分割后,遍历数据量为N,时间复杂度NlogN。

partition分割函数

快排的核心就是partition + 递归。

partition的过程

先入为主,partition流程,数组将一直处于以下状态:

[(v, lo) | .....<v..... | i......j | .....>v.....hi]

毕竟要用代码来实现算法,所以这里必须加入控制流的两个参数i、j,以下明确参数含义:

i:正在向右遍历的指针;

j:正在向左遍历的指针;

遇到i > v、j < v的时候,用swap(a[i], a[j])来使数组满足以上规律,之后继续遍历;

所以当刚开始遍历i、j时,这两个参数应该满足:[lo+1, i) < v、(j, hi] > v;

这两个区间在初始的时候长度都必须为0,由此也可以确定出i、j的初始值应该是i == lo+1,j==hi

partition的终点

[(v, lo) | .....<v..... j |i .....>v.....hi]

正是由于i、j两个指针不断的遍历和交换,将数组规划为左右两段,j刚好是最后一个<v的数字,当然要与lo进行交换,交换后就是:

[j | .....<v..... v|i .....>v.....hi]

其实就是之前概要提到的partition的最终执行结果:

 [....<v....|v|....>v.....]

partition的起点

最后忘记一个关键点,partition中i、j的初始值如何确定?(上面好像已经提及过),参考如下的数组

[(v, lo,i) | .............hi] j

这里i==lo,j==hi+1,为什么给这样的初值(不应该是i == lo+1,j==hi吗)其实这里partition循环代码中使用的是前置+,所以还未进入循环的时候,应该把初始值设置为初始位置的上一个位置。当然i == lo+1,j==hi的初始值也可以,不过partition循环代码中的前置+就要改为后置+,个人觉得在逻辑上会更繁杂一些,不容易调通。

代码

int _partition(int a[], int lo, int hi){
	int i = lo;				//各参数初始值
	int j = hi + 1;
	int v = a[lo];
	while(true){
		while(a[++i] < v){ 
			if (i >= hi) break;	//退出条件
		}
		while(a[--j] > v){
			if (j <= lo) break;	//退出条件

		}
		if (i >= j)			// ">=" 还是 ">" ?
			break;
		swap(a[i], a[j]);
	}
	swap(a[lo], a[j]);
	return j;
} 
void quicksort(int a[], int n){
	_quicksort(a, 0, n - 1);
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值