Jianzhi offer 40. The smallest number of k

Jianzhi offer 40. The smallest number of k

Title description

Insert picture description here

Problem-solving ideas

This question is a classic Top K question, a frequent visitor in interviews. There are two different solutions to the Top K problem. One solution uses a heap (priority queue), and the other uses a divide-and-conquer method similar to quick sort.

Although the time and space complexity of the quick sort algorithm are better than the method using the heap, but you should pay attention to the limitations of the quick selection algorithm:

  • First, the algorithm needs to modify the original array . If the original array cannot be modified, a copy of the array is required, which increases the space complexity.

  • Second, the algorithm needs to save all the data . If the data is regarded as an input stream, the method of using heaps is to process one by one, without saving the data, only the maximum heap of k elements needs to be saved. The quick selection method needs to save all the data before running the algorithm. When the amount of data is very large, even when the memory can't fit, it's troublesome. So when the amount of data is large, it is better to use the heap-based method .


1. Count and sort

If sorting is involved and the data range is limited , consider counting sorting .

Time o (n), space o (maxNum), maxNum refers to the largest number in the data range.

class Solution {
    
    
    public int[] getLeastNumbers(int[] arr, int k) {
    
    
        //边界处理
        if (arr == null || arr.length == 0 || arr.length < k) return new int[0];
        
        int[] res = new int[k];   //保存结果
        int index = 0;   //结果数组的索引
        int[] count = new int[10001];   //计数数组
        //计数排序
        for (int i = 0; i < arr.length; i++) count[arr[i]]++;

        for (int i = 0; i < arr.length; i++) {
    
    
            while (index < k && count[i]-- > 0) {
    
    
                res[index++] = i;
            }
            if(index == k) break;
        }

        return res;
    }
}

2. Heap

Generally speaking, when we talk about the topK problem, we can use a large top heap or a small top heap to implement it.

  • The largest K: small top pile
  • The smallest K: large top pile

Idea : We use a large root heap to maintain the first k small elements of the array in real time. First insert the first k numbers into the big root heap, and then traverse from the k+1th number. If the number currently traversed is smaller than the top number of the big root heap, pop the top number of the heap and insert the current After traversing to the number, the smallest k number is left at the end.

Java's PriorityQueue defaults to a small top heap, but you can pass in a custom Comparator function or use lambda expressions to achieve a large top heap.

The wording of this question when constructing a large top pile:

maxHeap = new PriorityQueue<Integer>((x, y) -> (y - x));

Here (x, y) -> (y - x)is the new features of Java lamda expression 8 can be understood as a simplified form of the input function, a function of parameters x and y, the output return y - x.

This way of writing is equivalent to the following:

maxHeap = new PriorityQueue<Integer>(new Comparator<Integer>(){
    
    
            public int compare(Integer num1, Integer num2) {
    
    
                return num2 - num1;
            }
        });

Time o (nlogk), space o (k)

class Solution {
    
    
    public int[] getLeastNumbers(int[] arr, int k) {
    
    
        //边界处理
        if (arr == null || arr.length == 0 || k == 0 || arr.length < k) return new int[0];
        int[] res = new int[k];   //保存结果
        //构造最大堆
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>((x, y) -> (y - x));
        //将前k个元素添加到大顶堆中
        for (int i = 0; i < k; i++) {
    
    
            maxHeap.offer(arr[i]);
        }
        //从下标k开始,依次入堆,不断将堆中的最大元素去除,最后留下的就是最小的k个数
        for (int i = k; i < arr.length; i++) {
    
    
            if (maxHeap.peek() > arr[i]) {
    
    
                maxHeap.poll();
                maxHeap.offer(arr[i]);
            }
        }
        for (int i = 0; i < k; i++) {
    
    
            res[i] = maxHeap.poll();
        }
        return res;
    }
}

3. Fast sorting thoughts

Quickly sort a divided template:

    //快排划分的模板
    public void quickPartition(int[] arr, int left, int right) {
    
    
        int temp = arr[left];
        int leftIndex = left, rightIndex = right;
        while(leftIndex < rightIndex) {
    
    
            while(leftIndex < rightIndex && arr[rightIndex] >= temp) rightIndex--;
            arr[leftIndex] = arr[rightIndex];
            while(leftIndex < rightIndex && arr[leftIndex] <= temp) leftIndex++;
            arr[rightIndex] = arr[leftIndex];
        }
        arr[leftIndex] = temp;
    }

Complexity analysis:

Insert picture description here

class Solution {
    
    

    public int[] getLeastNumbers(int[] arr, int k) {
    
    
        //边界处理
        if (arr == null || arr.length == 0 || k == 0 || arr.length < k) return new int[0];
        int[] res = new int[k];
        quickSearch(arr, 0, arr.length - 1, k);
        for (int i = 0; i < k; i++) res[i] = arr[i];
        return res;
    }


    public void quickSearch(int[] arr, int left, int right, int k) {
    
    
        if (left >= right) return;
        //每次用快排思想切分后,返回切分点的下标
        int partitionIndex = quickPartition(arr, left, right);
        //如果切分点正好是k,则arr中的前k个元素就是最小的k个数,直接返回
        if (partitionIndex == k) return;
        //继续去左边或右边寻找
        if (partitionIndex < k) {
    
    
            quickSearch(arr, partitionIndex + 1, right, k);
        } else {
    
    
            quickSearch(arr, left, partitionIndex -  1, k);
        }
    }

    //快排划分的模板,返回切分点的下标
    public int quickPartition(int[] arr, int left, int right) {
    
    
        int temp = arr[left];
        int leftIndex = left, rightIndex = right;
        while(leftIndex < rightIndex) {
    
    
            while(leftIndex < rightIndex && arr[rightIndex] >= temp) rightIndex--;
            arr[leftIndex] = arr[rightIndex];
            while(leftIndex < rightIndex && arr[leftIndex] <= temp) leftIndex++;
            arr[rightIndex] = arr[leftIndex];
        }
        arr[leftIndex] = temp;
        return leftIndex;
    }
}

Guess you like

Origin blog.csdn.net/cys975900334/article/details/115254971