快速排序:看名字就知道,速度快,效率高,因此经常被采用。
排序思想:分而治之,通过一趟排序将待排序分割成两部分,其中一部分的元素的关键元素比另一部分元素的 关键元素要小,分别对这两部分元素继续进行排序,直到整个序列有序。
排序算法:
1.选择基准:在原始序列中,按照某种方式选出一个元素作为基准
2.将小于这个基准的元素全放在基准的左边,大于这个基准的元素全放在基准的右边
3.递归地对基准的左右两个序列进行快速排序,知道下一个基准的左右序列只有一个元素或者为空
排序算法的演示(以最左边数为基准数):
快速排序就像挖坑一样,
原始序列 15 8 23 4 7 18 2 34 56
1. 一排树,选一个基准数,把基准数15挖出来,也就是最左边那棵树,这时候这棵树要保存一下
00 8 23 4 7 18 2 34 56 基准数 15
2. 然后从右边开始找一个比基准数小的一个数,注意是从右向左,而基准数开始是从最左边的那个
发现2比15小,此时要把2挖出来,此时把2填在原来的坑中
2 8 23 4 7 18 00 34 56
3. 然后从左边原来那个坑开始继续从左往右遍历,发现大于基准数的23再将这个数挖出来,填到右边
的那个坑中
2 8 00 4 7 18 23 34 56
4. 下一步
2 8 7 4 00 18 23 34 56
5. 最后发现从右往左时,最后一个数4是不大于基准的,那么就到坑了,到坑则基准入坑,、
6. 上面的5步确定了一个15的位置,这个时候15左右两侧出现了都小于和都大于15的序列,此时就用到了 分而治之的思想了,左右两个序列分别按照以上的5步继续走下去,那样不断的分而治之,知道最后只剩 一个元素即可
这里看假如4是15,这个排序是不是就不稳定了,原来基准15和后面的15相对位置就发生了改变
代码演示
void
sort(
int
*a,
int
left,
int
right)
{
if
(left >= right)
/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return
;
}
int
i = left;
int
j = right;
int
key = a[left];
while
(i < j)
/*控制在当组内寻找一遍*/
{
while
(i < j && key <= a[j])
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
{
j--;
/*向前寻找*/
}
a[i] = a[j];
/*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
a[left],那么就是给key)*/
while
(i < j && key >= a[i])
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
{
i++;
}
a[j] = a[i];
}
a[i] = key;
/*当在当组内找完一遍以后就把中间数key回归*/
sort(a, left, i - 1);
/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
sort(a, i + 1, right);
/*用同样的方式对分出来的右边的小组进行同上的做法*/
/*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}
空间复杂度:快速排序需要栈空间来实现递归,如果数组按局等方式被分割时,则最大的递归深度为 log n, 需要的栈空间为 O(log n)。最坏的情况下在递归的每一级上,数组分割成长度为0的左子数组和长度为 n - 1 的右数组。这种情况下,递归的深度就成为 n,需要的栈空间为 O(n)。
稳定性:不稳定,分析如上面演示