快速排序算法
1.1 算法分析
快速排序是一种不稳定的,时间复杂度为O(nlogn)的排序算法.
基本思想:1.选定基准值key。
2.通过双指针不断地移动,将比key大的值移动到key的右边,比key小的值移动到key的左边,直到双指针相遇(即代表该过程已完成)
3.上述过程完成后,对于以原key区分形成的两个子数组,分别重复1和2操作。
4.直到所有的子过程完成后,我们就得到了最后的有序数组
快速排序采用了分治递归的思想,通过问题的分解,简化为子问题求解。
1.2算法描述
- 在一个无序数组中,选定一个基准值(一般为第一个元素),设该值为Key。(升序)
- 使用low和high分别指向数组的第一个以及最后一个元素,为保留数据,我们使用i,j两个变量来存储low与high的值。
- 我们从数组的尾指针(j)开始,通过移动j,来寻找比key小的元素,也就是我们常用的while循环,要注意的是,如果与前方指针i相遇,则说明过程已经完成,此时也应该结束寻找,否则将会出现越出。
- 在寻找到比key小的元素后,我们将该元素放在i(初始情况下指向key)位置,并且让i指向下一个位置。
- 此时我们再从数组的头指针(i)开始,从前往后地寻找比key大的元素,过程同前文第二点类似,并在寻找到该元素后,将该元素放在j位置,并且使得j往前移动一位。
- 不断重复第2,3,4部,直到两指针i和j相遇,则相遇的位置,就是我们基准值所在的位置,这时,我们将key赋值到该位置,则此部分完成
- 这时候我们得到了以low到i-1与i+1到high两个新的子数组,于是我们递归调用自身,进行同样的操作,直到最后结果完成。
- 注意:递归收拢条件是low始终小于high。
1.3 思路分析
接下来我们来模拟这一算法思路:
假设有数组:
78 21 98 55 64 33 82
则在第一步,我们以78作为基准值,数据变化如下:
key:78 从后往前,33比78小,得到:
(33) 21 98 55 64 ? 82
从前往后,98比78大
(33) 21 ? 55 64 (98) 82
之后:
(33) 21 (64) 55 ? (98) 82
则此时我们得到:
(33) 21 (64) 55 78 (98) 82
则有33 21 64 55与98 82为子数组
对于33 21 64 55:
? 21 64 55=>21 ? 64 55
得到新的子数组:21与64 55
则有数组:21 33 55 64
另外一部分同理,则得到最后结果:
21 33 55 64 78 82 98
2.1代码:
void quickSort(int arrs[], int low, int high)
{
//递归出口:
if (low < high)
{
//通过i,j存储low与high,并以第一个数据作为基准值key
int i = low;
int j = high;
int key = arrs[low];
//每一次操作都是以i,j是否相遇作为结束条件,相遇则代表两边都已经完成操作
while (i < j)
{
//从后往前扫描,若是遇到了比key小的,则循环会退出,并且当前j会指向该元素的位置
while (i < j && arrs[j] >= key)
j--;
//将找到的元素放到i指向的位置,并让i指向下一个位置
if (i < j)
arrs[i++] = arrs[j];
//重复与上面类似的操作
while (i < j && arrs[i] < key)
{
i++;
}
if (i < j)
{
arrs[j--] = arrs[i];
}
}
//这是指针已经相遇,则此位置即为key所在的位置
arrs[i] = key;
//我们得到的新数组,其位置下标就是[low,i-1]与[i+1,high],进行递归调用
quickSort(arrs, low, i - 1);
quickSort(arrs, i + 1, high);
}
}
2.2总结:
快速排序的代码比较符合正常逻辑,虽然是递归调用,但是仍然比较容易理解,分治递归求解子问题
快速排序的主要特点:比较脆弱,内循环比较短小,实际中性能较优越,只需要使用一个很小的辅助栈。