算法复杂度
时间复杂度:O(nlogn)
空间复杂度:T(n)
算法策略
分而治之
基本逻辑
取数组内任意一数,将数组内所有小于它的数移到左边,所有大于它的数移到右边,将左右两边分别视为新的数组。
对新的两个数组,分别重复上一步骤,直到新的数组只含1个元素,整个数组变得有序。
图解
取数组中第一个数为基准。
从右往左找到第一个小于基准的数(此处第一步找到的是6)。
从左往右找到第一个大于基准的数(9)。
交换6和9。
继续从刚才的两个位置,向中间找到第二组数,交换位置,直到i=j。
此时,i和j都会停在小于基准的数中,最右边的那一个数的位置。(如果在从两边向中间找数的这一步,先从左找大数、再从右找小数,则会停在大于基准的数中最左边的那一个数的位置,即19这个位置。具体由代码的写法决定。)
这时,只需要交换自始自终留在第一位的基准数,和停止位置的数(7和5),即可完成本轮划分。
之后,持续对左侧和右侧分别进行新的划分即可。
代码实现
void quick_sort(int arr[], int len) {
int beginAddress = 0;
int endAddress = len - 1;
devide_arr_quick(arr, beginAddress, endAddress);
for (int i = 0; i < 9; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
void devide_arr_quick(int arr[], int beginAddress, int endAddress) {
if (beginAddress >= endAddress) {
return;
}
int left = beginAddress;
int right = endAddress;
int middle = arr[beginAddress];
while (left < right) {
while (arr[right] >= middle && left < right) {
//从右到左,跳过大于/等于middle的数字,找到第一个小于middle的
//middle本身还留在第一位
//注意,要保证left小于right,一是防止内存溢出,二是减少运算量
right--;
}
//要先找右边再找左边,这样最后left和right都会停在小于middlle的最右边那个数上,
//与一直在最左边的middle数交换位置,即可结束本轮排序。
//如果先左再右,则会停在大于middle的最左边的数上,
//此时,则需要将middle与(left-1)交换
while (arr[left] <= middle && left < right) {
//从左到右,跳过小于/等于middle的数字,找到第一个大于middle的
left++;
}
//交换left和right位置上的数
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
//不需要手动加减left和right,直接进入下一轮循环
//left++; (X)
//right--; (X)
}
//左右划分完毕,交换放在最左边的middle和小于middle的数中位于最右边的那个
arr[beginAddress] = arr[left];
arr[left] = middle;
//对左右两边分别进行新的划分
devide_arr_quick(arr, beginAddress, left - 1);
devide_arr_quick(arr, left + 1, endAddress);
}