[python] Quick selection algorithm + minimum K number of leetcode

Part of the content is reproduced: https://blog.csdn.net/dacixie/article/details/79477421
Thanks to the blogger for finishing

quick selection:

Quick select (English: Quickselect) is a selection algorithm that finds the k-th smallest element from an unordered list. It is related to quick sort in principle. Like quick sort, it was proposed by Tony Hall, so it is also called the Hall selection algorithm. Similarly, it is an efficient algorithm in practical applications, with a good average time complexity, but the worst time complexity is not ideal. Quick selection and its variants are the most commonly used efficient selection algorithms in practical applications.

The general idea of ​​quick selection is the same as that of quick sorting. Select an element as the benchmark to partition the elements, and divide the elements smaller and larger than the benchmark into two areas on the left and right of the benchmark. The difference is that quick selection does not recursively visit the two sides, but only recursively enter the elements on one side to continue searching. This reduces the average time complexity from O(n log n) to O(n), but the worst case is still O(n2).

Like quick sort , quick selection is generally implemented in an in-situ algorithm. In addition to selecting the k-th smallest element, the data is also partially sorted.

If you don’t understand the process of quick selection, because the process of quick selection and quick sort can be said to be very similar, I suggest you watch a short 10-minute video explaining the quick sort algorithm by Mr. Chen Bin of Peking University :

Quick sort algorithm explanation
Insert picture description here

The general idea of ​​quick selection is the same as that of quick sorting. Select an element as the benchmark to partition the elements, and divide the elements smaller and larger than the benchmark into two areas on the left and right of the benchmark. The difference is that quick selection does not recursively visit the two sides, but only recursively enter the elements on one side to continue searching. This reduces the average time complexity from O(n log n) to O(n), but the worst case is still O(n2).

Like quick sort, quick selection is generally implemented in an in-situ algorithm. In addition to selecting the k-th smallest element, the data is also partially sorted.

The goal of Quick Select is to find the k-th largest element, so

If the length of the split left sub-array> k, the k-th largest element must appear in the left sub-array;
if the length of the split left sub-array = k-1, then the k-th largest element is pivot;
if If the above two conditions are not met, the k-th largest element must appear in the right sub-array.

The code function is as follows:

def qselect(A,k):    
    if len(A)<=k:return A    #若数组长度小于k,则输出前k个最小数构成的数组
    pivot = A[0]    #中位数
    left = [pivot] + [x for x in A[1:] if x<=pivot]  #中位数左边的数组
    llen = len(left)    #中位数左边数组的长度,全是比中位数小的数
    if llen==k:    #如果刚好为k个,则第k个是第k个最小数
        return left    #输出数组
    if llen > k:    #如果比k大,则需要对这个数组进一步切分
        return qselect(left, k)    #递归继续划分
    else:    
        right = [x for x in A[1:] if x > pivot] #如果长度小于k,那肯定在中位数右边的数组,也就是比中位数大
        return left + [pivot] + qselect(right, k-llen-1) #返回左边+中位数+右边划分
#所以划分一共有三种情况,恰好k个,比k个少,比k个多,分别对应左、中、右;
# 划分函数需要两个参数:k/k-llen,以及被划分的数组。

It just so happens that Leetcode has a problem:
design an algorithm to find the smallest k number in the array. These k numbers can be returned in any order.

Example:
Input: arr = [1,3,5,7,2,4,6,8], k = 4
Output: [1,2,3,4]

class Solution:
    def smallestK(self, arr: List[int], k: int) -> List[int]:
        def helper(arr, k):
        	if k == 0:#个人感觉很有必要
        		return []
            if len(arr) <= k:
                return arr
            # 快速选择
            midnum = arr[0]  # 中位数取数组第一位
            left = [e for e in arr[1:] if e <= midnum]
            len_left = len(left)  # 左数组的长度
            if len_left == k:
                return left
            elif len_left > k:
                return helper(left, k)  # 继续分
            else:  # 长度比k还小,只能把中位数右边的也加进来了
                right = [e for e in arr[1:] if e > midnum]
                return left + [midnum] + helper(right, k - len_left-1)  # left已经被算进里面了,所以只需要在right里面找k-lenleft-1就好
        return helper(arr,k)

What if you want to find the largest K number? Magically change the code

def helper(arr, k):
    if k == 0:#个人感觉很有必要
        return []
    if len(arr) <= k:
        return arr
    # 快速选择
    midnum = arr[0]  # 中位数取数组第一位
    right = [e for e in arr[1:] if e >= midnum]
    len_right = len(right)  # 右数组的长度
    if len_right == k:
        return right
    elif len_right > k:
        return helper(right, k)  # 继续分
    else:  # 长度比k还小,只能把中位数左边的也加进来了
        left = [e for e in arr[1:] if e < midnum]
        return right + [midnum] + helper(left, k-len_right-1)  # left已经被算进里面了,所以只需要在right里面找k-lenleft-1就好

Guess you like

Origin blog.csdn.net/Sgmple/article/details/110928514