Estructuras de datos y algoritmos (2) - Matrices y algoritmos relacionados

Conocimientos básicos de arreglos.

Introducción

Array (Array) es una estructura de datos de tabla lineal. Utiliza un conjunto de espacios de memoria contiguos para almacenar un conjunto de datos del mismo tipo.

Una tabla lineal es una estructura en la que los datos se organizan como una línea. Los datos de cada tabla lineal tienen como máximo dos direcciones, adelante y atrás. De hecho, además de las matrices, las listas enlazadas, las colas, las pilas, etc. también son estructuras de tablas lineales. El concepto opuesto es una tabla no lineal, como un árbol binario, un montón, un gráfico, etc. La razón por la que se llama no lineal es porque, en una tabla no lineal, no existe un contexto simple entre los datos.

operación común

Tome la siguiente matriz de imágenes como ejemplo:

inserte la descripción de la imagen aquí

leer

Las matrices admiten lecturas aleatorias y la complejidad temporal de las lecturas aleatorias es O(1)

buscar

Encontrar un valor específico en una matriz (sin ordenar) requiere atravesar la matriz, y la complejidad del tiempo es O (n)

insertar

Como se muestra en la figura, inserte 10 en la posición de k = 2.

inserte la descripción de la imagen aquí

Si inserta elementos al final de la matriz, no necesita mover los datos y la complejidad del tiempo es O(1). Pero si inserta un elemento al comienzo de la matriz, todos los datos deben retroceder un bit, por lo que
la peor complejidad de tiempo es O (n). Debido a que tenemos la misma probabilidad de insertar un elemento en cada posición, la complejidad de tiempo de caso promedio es (1+2+...n)/n=O(n)

borrar

Similar a la inserción, la complejidad del tiempo es O(n)

Algoritmos comunes relacionados con matrices

Las siguientes preguntas de algoritmo son de Leetcode, las preguntas de la entrevista de varias empresas en Niuke.com y las preguntas de muestra sobre la "Oferta de Jianzhi". Se recomienda hacer más preguntas, y hacer más preguntas es la forma real.

doble puntero (o doble índice)

Ejemplo 1: suma de dos números II de Leetcode - matriz ordenada de entrada

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

Método 1: use un recorrido bidireccional, pero la complejidad del tiempo es O (n ^ 2)

Método 2: Usando punteros dobles, la complejidad del tiempo es O(n)

inserte la descripción de la imagen aquí

Úselo startpara señalar la posición inicial y endpara señalar la posición final. Determine la relación nums[start] + nums[end]entre la suma de y el número objetivo target. Dado que la matriz es una matriz ordenada , es posible nums[start] + nums[end] > targetrequerir when ; de manera similar, when , requiereend--nums[start] + nums[end] == targetnums[start] + nums[end] < targetstart++

inserte la descripción de la imagen aquí
porque nums[start] + nums[end] = 17 > target = 9, entonces end -- , consigueend = 2

inserte la descripción de la imagen aquí

nums[start] + nums[end] = 13 > target = 9, por lo tanto end -- , obtenga end = 1el cálculo final
nums[0] + nums[1] == 9, es decir, obtenga el resultado.

el código se muestra a continuación:

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        if(numbers == null || numbers.length <= 1)return new int[2];//判断输入数组是否合法
        int start = 0;
        int end = numbers.length - 1;
        while(start < end){
            int sum = numbers[start]+numbers[end];
            if(sum == target){
                break;
            }else if(sum > target){
                end--;
            }else{
                start++;
            }
        }
        int[] res = new int[2];
        res[0] = start+1;
        res[1] = end+1;
        return res;
    }
}

Ejemplo 2: el subarreglo con la longitud más pequeña

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 
的长度最小的连续子数组,并返回其长度。如果不存在符合条件的连续子数组,返回 0。

示例: 
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

inserte la descripción de la imagen aquí

como muestra la imagen:

Defina dos punteros i y j para que apunten a 0 y 1 respectivamente; su suma = 2 + 3 < s = 7, así que j++ hasta que su suma >= s; como se muestra en la tercera tabla, su suma = 8 > s = 7 , registre res = 4 en este momento y deje i++; como se muestra en la figura, y así sucesivamente,
puede obtener res = 2.

el código se muestra a continuación

class Solution {
    
    
    public int minSubArrayLen(int s, int[] nums) {
    
    
        if(nums == null || nums.length <= 0)return 0;
        int start = 0;
        int end = 1;
        int min = 1;
        int sum = nums[0];
        int res = Integer.MAX_VALUE;
        while(true){
    
    
           if(sum >= s){
    
    
               res = Math.min(min,res);
           } 
           if(sum > s){
    
    
               sum -= nums[start];
               min--;
               start++;
           }else{
    
    
               if(end >= nums.length)break;
               sum += nums[end];
               end++;
               min++;
           }
        }
        if(res == Integer.MAX_VALUE)return 0;
        return res;
    }
}

Usa la idea del algoritmo de clasificación rápida

Ejemplo: el K-ésimo elemento más grande en una matriz (importante, pregunta frecuente en entrevistas)

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 
个最大的元素,而不是第 k 个不同的元素。

示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

Solución 1: ordene la matriz primero, y luego podemos obtener los elementos que necesitamos a través de nums[nums.length - k]. Dado que se usa el algoritmo de ordenación, la complejidad temporal del algoritmo es O(nlogn)

Solución 2: Usando el principio de clasificación rápida, el k-ésimo elemento más grande puede obtenerse con solo O(n) complejidad de tiempo, que también es un algoritmo que necesitamos entender en entrevistas generales.

Idea de algoritmo: encuentre aleatoriamente un número p en la matriz, coloque los elementos de la matriz que son más grandes que este número p a la derecha y los que son más pequeños que p a la izquierda, como se muestra en la figura
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Entonces, la posición donde se encuentra p es índice = 4, es decir, p = 4 es el cuarto número más pequeño de la matriz, de lo contrario, p = 4 es el tercer número más grande de la matriz. Dado que p = 4 en sí mismo y el número anterior no pueden ser los elementos que necesitamos encontrar, los elementos de [0,3] se truncan y los pasos iniciales se repiten desde el rango de la matriz [4,5] para encontrar el k-ésimo elemento más grande.

inserte la descripción de la imagen aquí

Como se muestra en la figura, cuando p = 5, es el segundo elemento más grande, es decir, se devuelve el resultado.

La implementación del código del autor liweiwei1419 es la siguiente

public class Solution {
    
    

    public int findKthLargest(int[] nums, int k) {
    
    
        int len = nums.length;
        int left = 0;
        int right = len - 1;

        // 转换一下,第 k 大元素的索引是 len - k
        int target = len - k;

        while (true) {
    
    
            int index = partition(nums, left, right);
            if (index == target) {
    
    
                return nums[index];
            } else if (index < target) {
    
    
                left = index + 1;
            } else {
    
    
                right = index - 1;
            }
        }
    }

    /**
     * 在数组 nums 的子区间 [left, right] 执行 partition 操作,返回 nums[left] 排序以后应该在的位置
     * 在遍历过程中保持循环不变量的语义
     * 1、[left + 1, j] < nums[left]
     * 2、(j, i] >= nums[left]
     *
     * @param nums
     * @param left
     * @param right
     * @return
     */
    public int partition(int[] nums, int left, int right) {
    
    
        int pivot = nums[left];
        int j = left;
        for (int i = left + 1; i <= right; i++) {
    
    
            if (nums[i] < pivot) {
    
    
                // 小于 pivot 的元素都被交换到前面
                j++;
                swap(nums, j, i);
            }
        }
        // 在之前遍历的过程中,满足 [left + 1, j] < pivot,并且 (j, i] >= pivot
        swap(nums, j, left);
        // 交换以后 [left, j - 1] < pivot, nums[j] = pivot, [j + 1, right] >= pivot
        return j;
    }

    private void swap(int[] nums, int index1, int index2) {
    
    
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

conocimiento de operaciones con bits

Ejemplo:

一个整型数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字

Conocimientos preliminares: Operación XOR: Si los dos valores de a y b no son iguales, el resultado XOR es 1. Si los dos valores de a y b son iguales, el resultado de XOR es 0.

El código se implementa de la siguiente manera:

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    
    
    public int FindNumsAppearOnce(int [] array) {
    
    
        int sum = array[0];
        for(int i = 1;i < array.length;i++){
    
    
            sum ^= array[i];
        }
        return sum;
    }
}

espacio para el tiempo

Reducir la complejidad del tiempo aumentando la complejidad del espacio es una forma común de reducir la complejidad del tiempo. Las preguntas relevantes del algoritmo son las siguientes:

Correlación de matriz bidimensional

Debe dominar la implementación del código

  • Implemente una matriz que admita la expansión dinámica
  • Realice una matriz ordenada con un tamaño fijo y admita operaciones dinámicas de adición, eliminación y modificación
  • Combinar dos matrices ordenadas en una matriz ordenada

Preguntas frecuentes sobre algoritmos

referencia

  • codigo lelet
  • "Espada apuntando a ofrecer"
  • Columna "La belleza de la estructura de datos y el algoritmo" de Geek Time

Supongo que te gusta

Origin blog.csdn.net/lichukuan/article/details/127019605
Recomendado
Clasificación