Jianzhi ofrece 40. El menor número de k

Jianzhi ofrece 40. El menor número de k

Descripción del Título

Inserte la descripción de la imagen aquí

Ideas para resolver problemas

Esta pregunta es una pregunta clásica de Top K, un visitante frecuente en las entrevistas. Hay dos soluciones diferentes para el problema de Top K. Una solución usa un montón (cola de prioridad) y la otra usa un método de dividir y conquistar similar a la clasificación rápida.

Aunque la complejidad de tiempo y espacio del algoritmo de clasificación rápida es mejor que el método de uso del montón, debe prestar atención a las limitaciones del algoritmo de selección rápida:

  • Primero, el algoritmo necesita modificar la matriz original . Si la matriz original no se puede modificar, se requiere una copia de la matriz, lo que aumenta la complejidad del espacio.

  • En segundo lugar, el algoritmo necesita guardar todos los datos . Si los datos se consideran un flujo de entrada, el método de usar montones es procesar uno por uno, sin guardar los datos, solo se necesita guardar el montón máximo de k elementos. El método de selección rápida necesita guardar todos los datos antes de ejecutar el algoritmo. Cuando la cantidad de datos es muy grande, incluso cuando la memoria no cabe, es problemático. Entonces, cuando la cantidad de datos es grande, es mejor usar el método basado en montones .


1. Contar y ordenar

Si la clasificación está involucrada y el rango de datos es limitado , considere contar la clasificación .

Tiempo o (n), espacio o (maxNum), maxNum se refiere al número más grande en el rango de datos.

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. Montón

En términos generales, cuando hablamos del problema de topK, podemos usar un montón superior grande o un montón superior pequeño para implementarlo.

  • La K más grande: pila superior pequeña
  • La K más pequeña: pila superior grande

Idea : utilizamos un montón de raíz grande para mantener los primeros k elementos pequeños de la matriz en tiempo real. Primero inserte los primeros k números en el montón raíz grande, y luego recorra desde el número k + 1. Si el número actualmente atravesado es menor que el número superior del montón raíz grande, saque el número superior del montón e inserte el actual Después de atravesar el número, lo que queda es el número k más pequeño.

PriorityQueue de Java tiene como valor predeterminado un montón superior pequeño, pero puede pasar una función comparadora personalizada o usar expresiones lambda para lograr un montón superior grande.

La redacción de esta pregunta al construir una gran pila superior:

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

Aquí (x, y) -> (y - x)están las nuevas características de Java lamda expresión 8 que se puede entender como una forma simplificada de la función de entrada, una función de los parámetros xey, la salida devuelve y - x.

Esta forma de escribir es equivalente a la siguiente:

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

Tiempo o (nlogk), espacio 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. Pensamientos de clasificación rápida

Ordene rápidamente una plantilla dividida:

    //快排划分的模板
    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;
    }

Análisis de complejidad:

Inserte la descripción de la imagen aquí

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;
    }
}

Supongo que te gusta

Origin blog.csdn.net/cys975900334/article/details/115254971
Recomendado
Clasificación