1. Simple Quick Sort
Quicksort is a simple, easy-to-understand sorting algorithm, let's take a look at an entry-level quicksort:
function QuickSort(nums){
if(nums.length <= 1){
return nums
}
let pivot = nums[random(0, nums.length - 1)]
let left = []
let right = []
let mid = []
for (let i = 0; i < nums.length; i++) {
if(nums[i] < pivot){
left.push(nums[i])
}else if(nums[i] == pivot){
mid.push(nums[i])
}else{
right.push(nums[i])
}
}
return QuickSort(left).concat(mid, QuickSort(right))
}
The advantage of this way of writing is that the logic is very clear and easy to understand; the disadvantage is that each recursion generates a new array, and finally merges the arrays, and the space complexity is slightly higher
2. In-situ sorting Partition algorithm
The Partition algorithm is an in-situ segmentation and sorting algorithm. It selects a reference value, swaps each value smaller than the reference value to the left, and swaps each value larger than the reference value to the right, and finally the reference value is centered. Algorithm for ground division, no extra space is used except for constant-level variables
2.1 Public functions
Let's first define a few commonly used public functions
//数组交换
function swap(nums, i ,j){
tmp = nums[j]
nums[j] = nums[i]
nums[i] = tmp
}
//范围内随机数
function random(minNum,maxNum){
return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10)
}
2.2 One traversal from left to right - Partition algorithm
function Partition(nums, l = 0, r = nums.length - 1) {
//随机选取一个作为基值值
let random_i = random(l, r)
let pivot = nums[random_i]
swap(nums, l, random_i)
let pos = l
for (let i = l + 1; i <= r; i++) {
if(nums[i] <= pivot){
pos++
if(i != pos){
swap(nums, i, pos)
}
}
}
swap(nums, l, pos)
return pos
}
pos is the dividing line, the right side of pos (including itself) is less than or equal to the reference value, and the right side is greater than the reference value
2.3 Double Pointer-Partition Algorithm
function Partition(nums, l = 0, r = nums.length - 1)
{
let pivot = nums[l]
while(l < r)
{
while(l < r && nums[r] >= pivot){
r--
}
nums[l] = nums[r]
while(l < r && nums[l] <= pivot){
l++
}
nums[r] = nums[l]
}
nums[begin] = pivot;
return begin;
}
The algorithm of double pointers is not easy to understand. The subtlety is that the exchange function is not used. The reference value is temporarily stored through pivot, and then the reference value and the relationship between the left and right endpoints are used to complete the segmentation. If you are interested, you can hit a breakpoint and run it.
3. Quicksort using Partition algorithm
function QuickSort(nums, l = 0, r = nums.length - 1){
if(r - l <= 0){
return nums
}
let pos = Partition(nums, l, r)
QuickSort(nums, l, pos - 1)
QuickSort(nums, pos + 1, r)
return nums
}
Using the quick sort of the Partition algorithm, no new array is created, the original array is swapped, and the sorting is completed
4. Three-Partition Algorithm
The Three-Partition algorithm is an extension of the Partition algorithm. It divides the unordered array into three parts, which are smaller than the reference value, equal to the reference value, and larger than the reference value. It is very suitable for array segmentation and sorting with a lot of repeated data.
4.1 Three-Partition Algorithm to Determine Left and Right Boundaries
function ThreePartition(nums, l = 0, r = nums.length - 1) {
//随机选取一个作为基准值
let mid = random(l, r)
let pivot = nums[mid]
let p = l//这里的p就是左边界,p(含p)左边都是小于基准值的
for (let i = l; i <= r; i++) {
while(i <= r && nums[i] > pivot){
swap(nums, i, r)//这里的r就是右边界,r右边都是大于基准值的
r--
}
if(nums[i] < pivot){
swap(nums, i, p)
p++
}
}
return [p,r]
}
I think this algorithm is easier to understand than 4.2确定左中边界的Three-Partition算法
. When encountering each larger than the reference value, it will be swapped to the right, and at the same time, the right border r--
; if it is smaller than the reference value, it will be Swap to the left, while the left border p++
is more in line with common thinking.
4.2 Three-Partition Algorithm for Determining the Left Middle Boundary
function ThreePartition(nums, l = 0, r = nums.length - 1) {
//随机选取一个作为基准值
let mid = random(l, r)
let pivot = nums[mid]
let p0 = p1 = l//p0 0的最右边界 //p1中间值的最右边界
for (let i = l; i <= r; i++) {
if(nums[i] < pivot){
swap(nums, i, p0)
//因为首先是连续的左值+连续的基准值+连续的右值
//如果p1 > p0则会把一个基准值交换到了i,这不是我们期望的
//这时候我们需要把i和基准值的右边界p1交换
if(p0 < p1){
swap(nums, i, p1)
}
p0++
p1++
}else if(nums[i] == pivot){
swap(nums, i, p1)
p1++
}
}
return [p0,p1-1]
}
This algorithm is based on determining the left-middle boundary. Each time, the left boundary is exchanged to the left boundary, and the reference value is exchanged to the right boundary of the reference value. However, when the right boundary of the reference value p1
grows too fast, If it exceeds p0
, then it is necessary to i
and the right boundary of the reference value p1
5 Quicksort using the Three-Partition algorithm
function QuickSort(nums, l = 0, r = nums.length - 1){
if(r - l <= 0){
return nums
}
let pos = ThreePartition(nums, l, r)
QuickSort(nums, l, pos - 1)
QuickSort(nums, pos + 1, r)
return nums
}
If there are no duplicate values in the unordered array, it is completely equivalent to the Partition algorithm. If there are duplicate values in the array, the more repetitions, the more concentrated the distribution, and the higher the efficiency of this algorithm.
6. Use the Partition algorithm to solve the problem TopK
function findKthNumber(nums, k){
let l = 0;
let r = nums.length - 1;
while (l <= r){
let pos = Partition(nums, l ,r)
let r_len = r - pos
let l_len = pos
if(k == r_len + 1){
return nums[pos]
}else if(k <= r_len){
l = pos + 1
}else{
r = pos - 1
k -= (r_len + 1)
}
}
return 0
}
Efficiency is still very efficient
7. Solve the problem using the Three-Partition algorithm TopK
function findKthNumber(nums, k){
let l = 0;
let r = nums.length - 1;
while (l <= r){
let pos = Partition(nums, l ,r)
let r_len = r - pos
let l_len = pos
if(k == r_len + 1){
return nums[pos]
}else if(k <= r_len){
l = pos + 1
}else{
r = pos - 1
k -= (r_len + 1)
}
}
return 0
}
The efficiency of the Three-Partition algorithm is also very efficient. The more convergent and concentrated the distribution, the better the effect.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。