快速排序的本质是从数组中选一个参考值ref,比该参考值的大的,将其放在ref的右边,比ref小的放在左边,然后不断的对两边重复执行该动作
我们先列出来快速排序的步骤:
1.从数组中选一个参考值ref,比该参考值的大的,将其放在ref的右边,
上面的动作将数组划分为两部分:
A ref B
A是比ref小的数组元素集合,它仍然是数组,B是比ref大的元素集合,它也仍然是数组
2.在对ref左右两边的元素重复上述动作,直到A和B都只剩下一个元素,那么排序就算完成了。
重点是如何分别选出来两个集合A和B。算法导论里面把这个步骤叫做partition动作。
先把算法导论里面的伪代码贴出来,大家先看一下:
先看第一种ref的选择方法,即ref = a[r]
partition(a[], p, r) { i = p j = p-1 ref = a[r] for(; i<r; i++) { if(a[i]<ref) { j++ exchange(a[i], a[j]) } } exchange(a[r], a[j+1]) return j+1; }
首先找一个参考值,ref = a[r],为了简单起见,这里取最后一个作为参考值,实际上可以去任意一个值作为参考值。这个我们一会再说。
然后找定义两个游标,分别是i 和j。i=p, j=p-1。为什么要这么定义呢?原因是我们既然选的是第一个,也就是a[p],同时表示是从数组的第一个元素开始遍历的。
选取j的目的是,我们要时刻知道当前最近一次比ref小的值的位置。由于我们选取的是a[r],作为参考值,且从第一个元素开始遍历,为了跟踪最近一次比ref小的数的游标,暂时j=p-1。大家可以仔细体会一下这个做的意义。
观察上述代码可以看到,j总是记录着最近一次比ref小的数的游标,因此最后return j+1,所有比ref小的数的游标均小于j+1,所有比ref大的数的游标均大于j+2。
总之我们执行partition的目的就是为了得到A,B,以及中间数的游标,这样我们就可以分别对A和B重复执行上述动作。
接下来我们考虑另外两种选取ref的方法。从上面选取最后一个值a[r],作为参考值,并且在最后,将a[r]和a[j+1]交换的动作可以知道,我们总是希望知道我们选取参考值在partition过程中的位置,以便我们可以在最后一步,将a[refId] 和 a[j+1]的值交换。这里的refId表示选取ref值在a[]中的游标。
如果我们选取ref为最后一个值,那么在所有的partition过程中,这个值的位置是固定的。但是,假如我们选取的ref的refId是p到r范围内的一个随机数呢?
显然,假如我们随机选取ref的值,那么在partition过程中,refId对于的ref就有可能和其他值交换。这时候我们就需要更新ref对应的游标。
这样一来,思路就很清晰了。
先给出partition的伪代码:
partition(a[], p, r){ refId = random(p,r) i = p j = p-1 for(; i<=r; i++) { if(a[i]<ref) { j++ if(j == refId)//此时j刚好等refId,并且要和a[i]交换,则更新refId { refId = i } exchange(a[i], a[j]) } } exchange(a[j+1], a[refId]) return j+1 }
从三种选择ref的方法可以看到本质上都是一样的,都为用一个游标j记录最近一次遍历到的比ref小的数据的游标,然后将ref和a[j+1]交换,返回j+1。
下面给出C++的代码实现
#include <iostream> #include <stack> #include"stdlib.h" #include <time.h> using namespace std; template<class T> class SORT { public: static void myQsort(T a[], int p, int r); static void myQsortNoRecur(T a[], int p, int r); private: static int partition(T a[], int p, int r); static void exchange(T a[], int i, int j); }; template<class T> void SORT<T>::exchange(T a[], int i, int j) { T temp = a[i]; a[i] = a[j]; a[j] = temp; return; } template<class T> int SORT<T>::partition(T a[],int p,int r) { int i = p; int j = p-1; T ref = a[p]; int refId = p; srand((unsigned)time(NULL)); refId = (rand() % (r-p+1))+ p; //cout<<refId<<endl; ref = a[refId]; for(; i<=r; i++) { if(a[i] < ref) { j++; exchange(a, i, j); if(j == refId) { refId = i; } } } exchange(a, j+1, refId); return j+1; } template<class T> void SORT<T>::myQsort(T a[],int p,int r) { int q = 0; if(p<r) { q = partition(a, p, r); myQsort(a, p, q-1); myQsort(a, p+1, r); } return; } template<class T> void SORT<T>::myQsortNoRecur(T a[], int p, int r) { int start = p; int end = r; int mid = 0; std::stack<int> sortStk; sortStk.push(p); sortStk.push(r); while(!sortStk.empty()) { end = sortStk.top(); sortStk.pop(); start = sortStk.top(); sortStk.pop(); if(start < end) { mid = partition(a, start, end); sortStk.push(start); sortStk.push(mid -1); sortStk.push(mid + 1); sortStk.push(end); } } } int main(int argc, char** argv) { int a[10]; int b[10]; srand((unsigned)time(NULL)); for(int i=0; i<9; i++) { a[i] = rand(); } srand((unsigned)time(NULL)); for(int i=0; i<9; i++) { b[i] = rand(); } SORT<int>::myQsort(a,0, 9); SORT<int>::myQsortNoRecur(b,0, 9); for(int i=0; i<10; i++) { cout<<a[i]<<" "; } cout<<endl; for(int i=0; i<10; i++) { cout<<b[i]<<" "; } return 0; }
上面的代码我直接给出了快速排序的递归和非递归实现。
递归的实现方式很好理解,但是加入别人正在面试你快速排序算法的时候,多半会让你用非递归的方式实现给他看。下面我们就讨论一下。
先观察一下递归算法的运行过程,即每次都去对一段更小的范围去调用partition函数。所以我们需要知道每一次调用partition函数的start和end游标,同时,每一次partition调用都会产生新的start和end游标。
template<class T> void SORT<T>::myQsort(T a[],int p,int r) { int q = 0; if(p<r) { q = partition(a, p, r); myQsort(a, p, q-1); myQsort(a, p+1, r); } return; }
这样的话,我们就可以用一个通用容器去存放每次调用partition生成的start和end游标。知道虽有的合法的start和end游标都作为参数调用了partition函数。所谓合法的start和end就是start < end。。。。。
关于递归算法的非递归实现,第一个想到的数据结构肯定是栈。其实别的数据结构,例如队列,也是可以实现。这里给出基于stl内的stack的实现方法。代码如下
template<class T> void SORT<T>::myQsortNoRecur(T a[], int p, int r) { int start = p; int end = r; int mid = 0; std::stack<int> sortStk; sortStk.push(p); sortStk.push(r); while(!sortStk.empty()) { end = sortStk.top(); sortStk.pop(); start = sortStk.top(); sortStk.pop(); if(start < end) { mid = partition(a, start, end); sortStk.push(start); sortStk.push(mid -1); sortStk.push(mid + 1); sortStk.push(end); } } }
上面代码的运行过程就是每次循环,从容器内拿一个start和end,如果是合法的,就依次将他们再次放入容易,知道这个容器为空未知。
给个运行实例吧,我在代码里面实现的是实现随机数排序,ref采用随机选取的方式。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]