下面我们来看快速排序,快速排序有什么要说的,首先要知道快速排序是冒泡排序的改进版,他们都属于交换排序,他被认为是现在
最好的一种排序,他这里还涉及到两个概念,一个是分治,分而治之,把一个大的数列分成两个小的数列,小的分成两个小的,是可以
这么来分的,主要是分治和递归
冒泡排序他有什么缺点,他会不停的比较,它会频繁的进行比较,但关键字每次进行比较完,满足条件它会有更频繁的移动,交换,
他不仅有频繁的比较,而且他有频繁的交换,所以他的性能是比较低的
那我们来看一下快速排序是怎么回事啊,1962年推出的算法,不同的语言给出他的实现,基本思想有三步:
1. 第一步,先从数列中取出一个数作为基准数,别的数比他大比他小,它是个基准,简单起见,你只要去数列的第一个数就可以了,
比如说我们下面有一个数列,从0到9一共有10个数,那大家来看,我要取一个数为基准数,72做基准数吧,看下面这些数是比他大
还是比他小的,这是一个
2. 分区过程,干什么,我们要做一系列的操作,这是他最核心的部分,经过这个分区操作之后呢,这个数列里面,72在这儿,前面
都是比他小的,后面都是比他大的,这个过程叫分区,初始状态在这儿呢,一共有10个数,那我们第一步已经做了,拿着第一个数72
为基准数,我们把72赋给一个变量x,相当于在这里挖了一个坑,这个72实际上还是在,因为当你把72赋给另外一个变量的话,这个
值是不是还在呢,值在不要紧,我们就当无视他就可以了,这是一个,我们下面就要做操作了,经过这么一系列的操作,最后能够
达到这样的一个效果,哪个效果啊,就是这个效果,你看这个效果是什么,72左边的全部都是小于他的,右边全部是大于72的,
排序到此就结束了吗,早着呢,为什么呢,左边这些还是无序的,右边这些还是无序的,下边怎么办,两个字,递归,这就是我们
的第三步
3. 再对左右区间重复第一步,第二步,直到各区间只剩下一个数,这就是在递归,把左边这部分看作是一个完整的数组,
首先挖个坑,把这个48拿出来,经过一系列的操作,保证48前面的都是小于48的,后边都是大于48的,绿色这一部分呢,
也可以挖坑,先把83拿出来,挖个坑,通过一系列的变换之后,保证这4个数,83前边小于83,后面大于83,一直往下进行,
直到这个数组只剩下一个数,一个数就不用理了,是不是他啊,这样就可以了,这就是我们所说的一个主要的思路
从一个数列里面取一个数作为基准数,拿出来就可以了,然后再对前两步进行重复,递归是非常简单的,所以最难的地方
在分区,就是我们从这个一步,到最终这一步,这中间到底发生了什么,这就是快速排序的核心,因为再往下,左边右边
再重复,是不是也就重复刚才的算法,是重复这个算法,我们来看它是怎么来动的
1. 初始状态,一共有10个数,我们为了说明白,用下标直接标记0到9,从72开始,这个数是没有任何顺序的,我们从第一步
开始,我们要指定两个指针,一个叫i,i等于0,i指向这个数组的首元素,j指向数组的最后的一个位置,i数组等于0,j都等于
9,然后我们把这个坑给挖出来,怎么挖出来,把这个72赋给另外一个变量,72让他先在一边先歇一会,下边我们要干什么,
这边挖了一个坑,目的是为了填坑,这个数据是不是空出来了,那你觉得这里要放一个大于72的还是小于72的,小于72的吧,
我们从哪里找小于72的数啊,从右边去找吧,所以下面我们要动j的指针,这个指针要从右向左来动,动什么啊,第一个是85,
和72比,再动下一个,48,找到这个48了吧,48是不是要放到前面去,从右向左移动这个j,找到第一个小于72的值,
j等于8,怎么来表示这个48呢,他的索引不就是j嘛,拿到48,把这个坑填了他,填了之后i就可以往前动一下了,i到这儿就
可以了,这么一做,填了一个坑,又造了一个坑,是不是又造了一个坑,你觉得下一步是干什么了,下一个步这个坑又要放
一个数,那应该放一个什么样的数啊,大于72的数,从哪儿去找一个大于72的数,从左边去找,我们要把左边大于72的数
移到右边,把右边小于72的数全部移到左边,这72不就是在中间了吗,这j等于8是个坑了,下面我们怎么办,我们现在
移动i这个指针了,移动这个i了,6怎么办啊,都和72比,不动,57不动,88,从左向右移动i,找到第一个大于等于72的值,
在这儿呢,88到这儿了,i等于3,下一步就要填坑了,我要把88填到这儿,你告诉我这个语句该怎么实现,我怎么就能把88
填到这儿呢,88的索引不是3吗,这坑的索引不是8吗,所以是特别好填的,如果数组是arr的话,实际上是把谁给谁,
arr[j]=arr[i],前边填后边的坑,arr[i]给后面的arr[j],这是一个啊,88这个坑填了,告诉我把这个坑填了干什么,
指针j要往前动一下,两个指针是不是都要往中间动啊,88把这个坑填了,j动一下,前边又造了一个坑,
前面造一个坑干什么,必须动这个j往前动,有人说能往后动吗,后边全部是大于72的,不用动的,再动这个j,
73大于72,83大于72,当j等于5的时候,72大于42,拿着42把这个坑填了,是不是又造了一个坑,同时i要往前移动一个,
可是我这里又多了一个坑,这个怎么办了,这个该怎么办了,现在不能放72了,凭什么又放72了,万一放个85呢,
是不是还得动这个i,拿着这个i,60,我们和62比,他是不是小于72的,那就要再动这个i,那就动动,i和j到一起了,
是不是到一起了,左边的都小于72,我们把一轮的分区操作就搞定了,我们再把这个简单回忆一下,基本思路是什么,
设两个指针,一个指向首,一个指向尾,把第一个数据挖坑,挖出来,然后就开始动右边的指针,找到小于的数填左边的
坑,然后就开始动左边的指针,找出大的数填右边的坑,填完坑再重复右边的步骤,移动右边的指针往里边动,
依次进行,知道i等于j,两个指针都向中间的动,i++,j--,最终到i等于j的时候,这个位置就是72最终要放的位置,
这就一个分区操作完成了,请问下面我们该怎么办了,我们就要递归重复之前的操作了,如果我们对蓝色的进行
递归的操作的话,应该设一个i在左边,设置一个j在这个小数组的右边,48这个坑挖了,60就往左边动,填了坑之后
48往右边动,什么时候i等于j的时候,48就放在中间的位置,绿色的部分找个基准的元素,83先拿出来,i指向谁,
指向第一个,j指向谁,指向最后一个,i往右边动,j往左边动,是不是就可以了,最终i等于j的时候就是83该放的
位置,一直递归下去,一直就等到只放一个数了,那就是他该放的位置,就是他了,做来做去,就重复刚才的两个
动作,最终会实现这么一个结果,完全有序的,这就是我们快速排序的算法,那就说到这儿呢,不再重复,已经说明白了
快速排序,有冒泡,它是在冒泡基础上实现的,有分治,分而治之,把一个数组分区,分两块,两块再分,再分别分
递归
快速排序我们可以用一句话来概括,叫东拆西补,西拆东补,一边拆一边补,一边挖坑一边填坑
算法说道这儿,下面我们就用代码来实现了
快速排序算法的分析:
1. 我们直接记住结论就可以了,在最坏的情况下,他的时间复杂度是O(n2),冒泡排序就是一样的
2. 好的情况下是O(nlog2 n),已经不可能比这个更好了,这是最好的情况,最高效率
3. 快速排序的空间复杂度为什么是O(log2 n),因为它用到了递归,递归的log2 n次
4. 当待排元素中[6,1,3,7,3],是不是有两个3,最终可能会生成[1,3,3,6,7]这个样子,什么意思,前面跑到后面了,
这种情况是会出现的,同样说明我们这种快速排序是一种不稳定的排序,排序我就给大家说到这,后面还有排序更多的分析
可以把它作为阅读资料来看一下
快速排序是冒泡排序的改进版,也是最好的一种内排序,还涉及到分治和递归,把这个记住