2.3.1 解题思路
快速排序的核心思想是找到一个目标值 key ,使用数组 nums 中的元素和 key 进行比较,大于 key 的元素放到数组的一边,小于 key 的元素放到数组的另一边,这样 key 的位置就确定了,依次使用这种方式对两边的数组进行排序,最终得到排好序的数组。
示例:
输入 nums = {4,8,1,5,9,2} 输出 nums = {1,2,4,5,8,9}
快速排序执行步骤:
初始化:nums = {4,8,1,5,9,2}
第一步:nums = {4,8,1,5,9,2}
选定最左边的元素 4 作为目标值。先从左边开始比较,8 大于 4 ,所以 leftIndex 指针停留在指向 8 的位置,值为 1。
第二步:nums = {4,2,1,5,9,8}
从右边比较,2 小于 4 ,所以 rightIndex 指针停留在指向 2 的位置,值为 5。交换 下标 0 和 5 的值。
第三步:nums = {4,2,1,5,9,8}
继续从左边开始比较,1 小于 4,leftIndex--,指针指向 5,5 大于 4,所以 leftIndex 停留在 5 的位置,leftIndex = 3 。
第四步:nums = {4,2,1,5,9,8}
继续从右边比较,8,9都大于 4 ,rightIndex最后也指向 5 。
第五步:nums = {1,2,4,5,9,8}
判断 5 大于 4 ,所以交换 leftIndex-1 位置 和 key 位置 的元素。
第六步:nums1 = {1,2}
按照第一步到第五步排序左侧数组。
第七步:nums2 = {5,8,9}
按照第一步到第五步排序右侧数组。
最终得到排序好的数组: nums = {1,2,4,5,8,9}
2.3.2 编码实现
public static void fastSort1(int[] nums,int left,int right){
if(left<right){
int mark = sort1(nums,0,nums.length-1);
fastSort(nums,left,mark-1);
fastSort(nums,mark+1,right);
}
}
public static int sort1(int[] nums,int left,int right){
int leftIndex = left+1;
int rightIndex = right;
int mark = -1;
int key = nums[left];
while(leftIndex<rightIndex){
while(nums[leftIndex]<key && leftIndex<rightIndex){
leftIndex++;
}
while(nums[rightIndex]>key && leftIndex<rightIndex){
rightIndex--;
}
if(left<right){
int tmp = nums[leftIndex];
nums[leftIndex] = nums[rightIndex];
nums[rightIndex] = tmp;
}
}
if(nums[leftIndex]<key){
mark = leftIndex;
} else {
mark = leftIndex-1;
}
nums[left] = nums[mark];
nums[mark] = key;
return mark;
}
2.3.3 时间复杂度和空间复杂度
快速排是采用分治法进行遍历的,相当于遍历一个二叉树,理想的二叉树结构如下图所示:
这种情况二叉树的深度是3,根据完全二叉树的定义,深度计算公式 log(N+1),每一层需要比较N次,所以时间复杂度是 T(n) = O(N*logN)。因为是使用的递归,所以每一层递归都需要数组存储,理想情况下空间复杂度 S(n) = O(logN)。
上面讲的是最理想的情况下,空间复杂度和时间复杂度,二叉树有可能并不是上面的样子,二叉树的深度有可能是N ,这种情况下 时间复杂度 T(n) = O(n^2) ,空间复杂度 S(n) = O(n)。
快速排序的平均时间复杂度 T(n) = O(N*logN) ,空间复杂度 S(n) = O(NlogN) 。