快速排序详解及应用 :: labuladong的算法小抄 #1070
Replies: 15 comments 7 replies
-
不知道我的理解是否正确 对于数组 对于数组 试了一下,这里使用 对于数组 而 |
Beta Was this translation helpful? Give feedback.
-
@wy-luke 给你的思考点赞!关于你说的 这里的关键是控制 |
Beta Was this translation helpful? Give feedback.
-
@labuladong 谢谢东哥! |
Beta Was this translation helpful? Give feedback.
-
var sortArray = function(nums) {
// 为了避免出现耗时的极端情况 先随机打乱数组
randomShuffle(nums);
// 排序整个数组(原地修改)
sort(nums, 0, nums.length - 1);
return nums;
// 排序数组nums
function sort(nums, lo, hi) {
if (lo >= hi) {
return;
}
/****** 前序位置 ******/
// 对nums[lo...hi]进行切分
// 使得nums[lo...p-1] <= nums[p] < nums[p+1..hi]
let p = partition(nums, lo, hi);
/*********************/
sort(nums, lo, p - 1);
sort(nums, p + 1, hi);
}
// 对nums[lo...hi]进行切分
function partition(nums, lo, hi) {
// 取第一个位置的元素作为基准元素
let pivot = nums[lo];
// 这里把left, right定义为开区间
// 同时定义 [lo, left) <= pivot; (right, hi] > pivot
// 之后都要正确维护这个边界区间的定义
let left = lo + 1;
let right = hi;
// 当left > right时结束循环 以保证区间[lo, hi]都被覆盖
while(left <= right) {
while(left < hi && nums[left] <= pivot) {
left++;
// 此while循环结束时 恰好 nums[left] > pivot
}
while(right > lo && nums[right] > pivot) {
right--;
// 此while循环结束时 恰好 nums[right] <= pivot
}
// 此时[lo, left) <= pivot && (right, hi] > pivot
if (left >= right) {
break;
}
swap(nums, left, right)
}
// 将pivot放到合适的位置 即pivot左边元素较小 右边元素较大
swap(nums, lo, right);
return right;
}
// 随机打乱数组
function randomShuffle(nums) {
let len = nums.length;
for (let i; i < len; i++) {
// 随机生成[i, n - i]的随机数
let index = i + Math.floor(Math.random * (len - i));
swap(nums, i, index);
}
}
// 原地交换数组中的两个元素
function swap(nums, i, j) {
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
};
var findKthLargest = function(nums, k) {
// 转化成升序排名第targetIndex的元素
let targetIndex = nums.length - k;
let start = 0;
let end = nums.length - 1;
/** 在nums[start, end]中选择一个分界点 */
let index = partition(nums, start, end);
while (index != targetIndex) {
if (index > targetIndex) {
// 第k大的元素在nums[start, index - 1]中
end = index - 1;
} else {
// 第k大的元素在nums[index + 1, end]中
start = index + 1;
}
// 在nums[start, end]中选一个分界点
index = partition(nums, start, end);
}
// 找到第k大的元素
return nums[index];
}
/****** 对nums[start, end]进行切分 ******/
function partition(arr, startIndex, endIndex) {
// 取第一个位置的元素作为基准元素
let pivot = arr[startIndex];
// 设置一个mark指针指向数组的起始位置 最终mark指针代表小于基准元素的区域边界
let mark = startIndex;
for (let i = startIndex+1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++;
// 交换两个元素
[arr[mark], arr[i]] = [arr[i], arr[mark]];
}
}
// 更新分界点的值
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
} |
Beta Was this translation helpful? Give feedback.
-
东哥,算时间复杂度那,是等比数列,不是等差数列 |
Beta Was this translation helpful? Give feedback.
-
@wercyle 感谢指出,是我写错了,已修复~ |
Beta Was this translation helpful? Give feedback.
-
这个分区容易理解点,有点双指针技巧含义,选最后一个元素作为分区点,指针 i 表示比分区值小的元素应该放的位置,指针 j 只用来遍历。当 j 遍历到比分区值小的元素时,放到指针 i 的位置(通过交换实现)。当 j 遍历完时,[lo, i - 1] 都是比分区值小的元素,[i, hi - 1] 都是比分区值大的元素,最后交换一下分区值和 i 所指向的元素便实现了 pivot 左边都是比它小的元素,右边都是比它大的元素。 private static int partition(int[] arr, int lo, int hi) {
int pivot = arr[hi];
int i = lo, j = lo;
while (j < hi) {
if (arr[j] < pivot) {
swap(arr, i, j);
i++;
}
j++;
}
swap(arr, i, hi);
return i;
} |
Beta Was this translation helpful? Give feedback.
-
笔记: quick select里面还有一个 |
Beta Was this translation helpful? Give feedback.
-
东哥,现在912题快速排序算法会超时,应该是加入了输入是基本有序的数组case导致的 |
Beta Was this translation helpful? Give feedback.
-
超时会不会因为partition中判断太多,与lo和high以及i=j的case其实很少,btw,这块也没那么好理解, |
Beta Was this translation helpful? Give feedback.
-
ruby版 def sort_array nums
shuffle nums
quick_sort nums, 0, nums.length - 1
end
def quick_sort nums, lo, hi
return if lo >= hi
p = partition nums, lo, hi
quick_sort nums, lo, p - 1
quick_sort nums, p + 1, hi
end
def partition nums, lo, hi
pivot = nums[lo]
i, j = lo + 1, hi
while i <= j do
while i < hi && nums[i] <= pivot do
i += 1
end
while j > lo && nums[j] > pivot do
j -= 1
end
break if i >= j
nums[i], nums[j] = nums[j], nums[i]
end
nums[lo], nums[j] = nums[j], nums[lo]
j
end
def shuffle nums
ran_num = Random.new
n = nums.length
n.times do |i|
r = ran_num.rand i...n
nums[i], nums[r] = nums[r], nums[i]
end
end |
Beta Was this translation helpful? Give feedback.
-
如果纸质书上有归并和快排就好了 |
Beta Was this translation helpful? Give feedback.
-
20230203打卡!本篇的快排是两路快排(两个指针,切分成 <=和>两部分),其实存在局限:如果重复值过多,容易右侧递归深度大于左边导致时间复杂度退化。leetcode912, run time: 2262 ms void quicksort(int[] nums, int lo, int hi) {
if (lo >= hi) return;
int pivot = nums[lo];
int lt = lo; // [lo+1, lt] < pivot
int i = lo + 1; // (lt, i) == pivot
int gt = hi + 1; // [gt, hi] > pivot
while (i < gt) {
if (nums[i] < pivot) {
lt ++;
swap(nums, i, lt);
i ++;
} else if (nums[i] > pivot) {
gt --;
swap(nums, i, gt);
} else {
i ++;
}
}
swap(nums, lo, lt);
quicksort(nums, lo, lt - 1);
quicksort(nums, gt, hi);
} |
Beta Was this translation helpful? Give feedback.
-
python一直有个“222222……”的用例超时,改成三路快排。 class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
lo, hi = 0, len(nums)-1
random.shuffle(nums)
def qsort(nums, lo, hi):
if lo >= hi:
return nums
p, q = partition(nums, lo, hi)
qsort(nums, lo, p-1)
qsort(nums, q+1, hi)
# 划分
def partition(nums, lo, hi):
tar = nums[hi]
i, j, k = lo, lo, hi-1
# 三路快排
# [lo, i-1] < tar
# [k+1, hi-1] > tar
# [i, k] = tar
while j <= k:
if nums[j] < tar:
nums[j], nums[i] = nums[i], nums[j]
i += 1
j += 1
elif nums[j] > tar:
nums[j], nums[k] = nums[k], nums[j]
k -= 1
else:
j += 1
nums[hi], nums[i] = nums[i], nums[hi]
return i, k
qsort(nums, lo, hi)
return nums |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:快速排序详解及应用
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions