[LeetCode] -Prefijo suma y diferencia

suma de prefijo

Para matrices unidimensionales de números enteros, el algoritmo de suma de prefijos es la construcción(La construcción mencionada aquí no significa que se deba construir dicha matriz al resolver el problema. La explicación aquí es solo para comprender la idea de suma de prefijo. Después de comprender la idea, puede usar este algoritmo con sus propias ideas)Produzca un prefijo y una suma de matriz con la misma longitud que los números de matriz originales, y en el prefijo y la matriz, suma [i] representa la suma de los elementos de [0,i] en la matriz original. Puede usar la suma de
Prefijos y diagramas de ejemplo
prefijo Para resolver el problema, especialmente si realiza múltiples sumas de intervalo en la misma matriz unidimensional, solo necesita atravesar la matriz una vez con complejidad O (n) para generar la matriz de suma de prefijo, y luego solo necesita usar el intervalo suma cada vez Para la matriz de suma de prefijos, puede obtener la respuesta restando la suma de prefijos correspondiente al límite izquierdo del intervalo de la suma de prefijos correspondiente al límite derecho del intervalo. La complejidad es O (1)

303.Región y recuperación

class NumArray {
    
    
    private int[] sum;
    public NumArray(int[] nums) {
    
    
        int len = nums.length;
        if(len == 0) sum = null;
        else{
    
    
        	//开始生成前缀和数组
            sum = new int[len];
            sum[0] = nums[0];
            for(int i = 1;i < len;i++){
    
    
            	//生成前缀和数组的递推公式
                sum[i] = sum[i - 1] + nums[i];
            }
        }
    }
    public int sumRange(int left, int right) {
    
    
        if(sum == null) return 0;
        if(left == 0) return sum[right];
        return sum[right] - sum[left - 1];
    }
}

304. Región bidimensional y recuperación.

Esta pregunta es equivalente a un algoritmo para prefijar una matriz bidimensional, similar a una matriz unidimensional, y construye una matriz de suma de prefijo bidimensional cuyas filas y columnas son iguales a la matriz bidimensional original, donde suma [i ][j] significa
Entonces, cómo encontrar la suma de los elementos en el rectángulo en el rango de (fila1, col1) a (fila2, col2): Se
Diagrama de ejemplo de recuperación y área 2D
puede ver que el rectángulo en el rango de (fila1, col1) a (fila2, col2) es el rectángulo grande de (0,0) a (fila2,col2) menos el rectángulo pequeño en el área roja y el área azul en la figura más el área donde el área roja y el área azul se restan dos veces, es decir, suma [fila2] [col2] - suma [fila1 - 1] [col2] - suma [fila2] [col1 - 1] + suma [fila1 - 1] [col1 - 1]

class NumMatrix {
    
    
    private int[][] sum;
    public NumMatrix(int[][] matrix) {
    
    
        int m = matrix.length;
        int n = matrix[0].length;
        sum = new int[m][n];
        sum[0][0] = matrix[0][0];
        //矩形的第一行跟第一列的前缀和计算方法与其它位置的不同
        for(int i = 1;i < n;i++) sum[0][i] = sum[0][i - 1] + matrix[0][i];
        for(int i = 1;i < m;i++) sum[i][0] = sum[i - 1][0] + matrix[i][0];
        for(int i = 1;i < m;i++){
    
    
            for(int j = 1;j < n;j++){
    
    
                sum[i][j] = matrix[i][j] + sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];
            }
        } 
    }
    public int sumRegion(int row1, int col1, int row2, int col2) {
    
    
        if(row1 == 0 && col1 == 0) return sum[row2][col2];
        if(row1 == 0) return sum[row2][col2] - sum[row2][col1 - 1];
        if(col1 == 0) return sum[row2][col2] - sum[row1 - 1][col2];
        //下面这个计算公式是应对于普通情况的,可以发现不适用的情况是 col1 == 0 或者 row1 == 0,对应的就有三种输入情况
        //分别是row1跟col1都为0,row1为0col1不为0.row1不为0col1为0 
        return sum[row2][col2] - sum[row1 - 1][col2] - sum[row2][col1 - 1] + sum[row1 - 1][col1 - 1];
    }
}

560. Subarreglo cuya suma es K

Para una matriz unidimensional, sus subarreglos consecutivos comienzan desde el elemento 0 (contiene el elemento 0) o no comienzan desde el elemento 0. Para el primero, usar prefijo
y solución es encontrar el valor en la suma del prefijo de la matriz. ¿Cuántos elementos hay para k?
Para este último, suponiendo que la suma de los elementos en el subarreglo [i,j] es k, 0 < i <= j, entonces, correspondientemente, la suma de los elementos del subarreglo [0 ,i) es suma[j] - k, que es suma[i] en el prefijo y la matriz. Específicamente para esta pregunta, puede recorrer toda la matriz, luego registrar la suma de los elementos actualmente atravesados ​​y usar el mapa para registrar el número de apariciones de cada suma, y ​​luego determinar el valor de la suma clave - k en el mapa. es decir, ¿cuántas de las sumas de prefijos que se han recorrido son suma - k?

public int subarraySum(int[] nums, int k) {
    
    
    int len = nums.length;
    Map<Integer,Integer> map = new HashMap<>();
    //map记录数组中,前缀和为某个值的前缀子数组的个数
    //初始设置一个前缀和为0时有一个子数组,这一个表示的是一个数组元素都不选时长度为0的子数组
    //这样后续遇到刚好有前缀和为k的时候,按照上面说的第二种情况来处理即可
    map.put(0,1);
    int sum = 0;
    int res = 0;
    for(int i = 0;i < len;i++){
    
    
    	//这里就不用真的去构造一个前缀和数组,只需遍历计算出每一个前缀和直接使用即可
        sum += nums[i];
        int key = sum - k;
        if(map.containsKey(key)){
    
    
            res += map.get(key);
        }
        map.put(sum,map.getOrDefault(sum,0) + 1);
    }
    return res;
}

1442. El número de ternas que forman dos conjuntos excluyentes o iguales (una pregunta por día)

La siguiente explicación proviene de la solución oficial de LeetCode,
Solución oficial
cabe señalar que s [0] representa 0. Para satisfacer a = b, se requiere s[i] = s[k + 1] (de acuerdo con la condición de 0 <= i < j <= k < arr.length, podemos obtener i < k), independientemente de j , entonces j Solo necesita estar dentro del rango de (i,k]. Es decir, cada vez que se encuentra un grupo de s[i] = s[k + 1], hay k - i tripletas correspondientes. De acuerdo con esto, podemos obtener el código a continuación.

public int countTriplets(int[] arr) {
    
    
    int n = arr.length;
    int[] s = new int [n + 1];
    for(int i = 1;i <= n;i++){
    
    
        s[i] = arr[i - 1] ^ s[i - 1];
    }
    int ans = 0;
    for(int i = 0;i < n;i++){
    
    
        for(int k = i + 1;k < n;k++){
    
    
            if(s[i] == s[k + 1]){
    
    
                ans += k - i; //k + 1 - i - 1
            }
        }
    }
    return ans;
}

Se puede decir que el código anterior es la solución óptima
. Comencemos desde la perspectiva de k: para cada k, en realidad estamos buscando cuántos s [i] = s [k + 1] hay delante de él. Cada vez que encontramos uno, ans Simplemente suma k y luego resta i. Es decir, para cada subíndice k, siempre que sepa cuántos subíndices i corresponden a s[i] = s[k + 1] delante de k y la suma de todos estos subíndices i, puede encontrar cada k contribución a la respuesta final ans. La idea de implementación es utilizar el registro hash para registrar el número de apariciones del mismo valor de s [i] que se ha atravesado y la suma de todos los subíndices i correspondientes, de modo que se pueda lograr la complejidad de O (n):

public int countTriplets(int[] arr) {
    
    
    int n = arr.length;
    int[] s = new int [n + 1];
    //count即记录每个前缀和及其出现的次数
    HashMap<Integer,Integer> count = new HashMap<>();
    //indexSum记录每个相等的前缀和s[i]的下标i的总和
    HashMap<Integer,Integer> indexSum = new HashMap<>();
    int ans = 0;
    //k从1开始,所以把s[0]对应的数据先放入哈希表
    count.put(0,1);
    indexSum.put(0,0);
    for(int k = 1;k <= n;k++){
    
    
        s[k] = arr[k - 1] ^ s[k - 1];
        if(count.containsKey(s[k])){
    
    
            ans += (k - 1) * count.get(s[k]) - indexSum.get(s[k]);
        }
        count.put(s[k],count.getOrDefault(s[k],0) + 1);
        indexSum.put(s[k],indexSum.getOrDefault(s[k],0) + k);
    }
    return ans;
}

Aunque la complejidad de los datos de prueba es O(n), considerando la búsqueda y configuración de la tabla hash, la eficiencia del tiempo real sigue siendo mayor que la del primer método anterior.

525. Matriz continua

La solución a la pregunta de referencia está organizada de la siguiente manera:
Defina la diferencia en el número de 01 como la diferencia obtenida restando el número de 1 del número de 0 en el subarreglo. menos [i] representa la diferencia en el número de 01 en el subarreglo [0,i]

Entonces, si podemos encontrar el subíndice j que satisface 0 <= j < i y menos [j] == menos [i], no significa que la diferencia en el número de 01 en el subarreglo [j + 1,i] sea 0, es decir, el número de 0. ¿Es un número igual a 1?

Esto requiere que calculemos menos [i] cuando atravesamos i, y al mismo tiempo usemos la tabla hash para registrar el j cuyo número de 01 en la parte atravesada es igual a menos [i], y luego podemos calcular [ j + 1,i] longitud. La longitud máxima de todos los subarreglos que satisfacen la condición es la respuesta final.

¿Cómo calcular menos [i]? No necesitamos calcular explícitamente menos [i], usamos un prefijo y un prefijo de matriz, el prefijo [i] representa el número de unos en [0,i], luego el número total de elementos en [0,i] es i + 1, el número de 1 es prefijo[i], el número de 0 es i + 1 - prefijo[i], entonces la diferencia en el número de 01 es i + 1 - 2 * prefijo[i]

¿Cómo grabar en la tabla hash? El valor clave representa menos [j] y el valor representa j. Al atravesar a i, el menos [i] de i se calcula como i + 1 - 2 * prefijo [i], y puede encontrar si la tabla hash tiene una clave.es i + 1 - 2 * prefijo [i], si lo hay, saque el valor j y calcule la longitud del subarreglo como i - j (no i - j + 1, el subarreglo calificado aquí es [ j + 1 ,i]); si no, ponga el par clave-valor k = i + 1 - 2 * prefijo[i], v = i

Simplemente mantenga la longitud máxima durante el proceso transversal.

Además, dado que el rango de diferencia en el número de 01 se puede conocer fácilmente como [-n,n], podemos usar una matriz de tamaño 2n + 1 como tabla hash en lugar de usar Map, ahorrando el tiempo requerido para la estructura del mapa

public int findMaxLength(int[] nums) {
    
    
    int n = nums.length, max = 0;
    //计算前缀和数组
    int[] prefix = new int[n];
    prefix[0] = nums[0] ;
    for (int i = 1; i < n; i++) 
        prefix[i] = prefix[i - 1] + nums[i];
    int[] ht = new int[2 * n + 1]; //哈希表
    //哈希表初始化,由于后续计算中ht中元素值可能为0,所以需要初始化为-1表示未被赋值过
    Arrays.fill(ht,-1); 
    int minus,index;
    for (int i = 0; i < n; i++) {
    
    
        minus = i + 1 - 2 * prefix[i]; //计算i对应的 01数量差
        index = minus + n;             //计算 01数量差 对应的哈希表中的槽
        //如果ht[index]不为-1说明该槽被覆盖过,可以计算[index + 1,i]的长度
        if(ht[index] != -1) max = Math.max(max,i - ht[index]);
        //如果01数量差为0说明[0,i]就符合条件,长度为i + 1
        else if(minus == 0) max = Math.max(max,i + 1);
        //如果ht[index]为-1才更新ht[index]的值,因为我们需要的是符合条件的子数组的最大长度
        //对于不同的j拥有同一个01数量差,我们记录的应该是最小的j,这样计算得到的子数组长度i - j才会是最大的
        else ht[index] = i;
    }
    return max;
}

diferencia

Para los números de matriz unidimensional entera, construya una matriz de diferencias dif con la misma longitud, donde dif[i] representa la diferencia obtenida restando números[i - 1] de números[i] (i > 0), diff[0 ] = nums[0]
Diagrama de ejemplo diferencial
se puede inferir para obtener la matriz original en función de la matriz de diferencias. El significado de la matriz de diferencia es la diferencia entre los elementos adyacentes de la matriz.
Cuando queremos realizar la misma operación en todos los elementos de la matriz en una determinada sección de la matriz unidimensional, como sumar un número k (k puede sea ​​un número negativo), la operación se completa. La diferencia entre todos los números en el intervalo es la misma que antes de la operación, es decir, en la matriz de diferencias, el valor de este intervalo no cambia, solo el primer número en el intervalo es el mismo que su número anterior. La diferencia se incrementa en k, y la diferencia entre el último número en el intervalo (si lo hay) y se reduce en k, como para [4,2,2,-5 ], para el intervalo [1,2 ] realizamos la operación de sumar 3, es decir obtenemos [4,5,5,-5], y el arreglo de diferencias es [4,1,0,-10], eso es, después de la operación, dif[1] = dif[1] + 3, dif [3] = dif[3] - 3
Considerando que el último elemento en la matriz original no tiene un número posterior, si la operación de intervalo incluye el último elemento, solo necesita agregar k al primer número en el intervalo correspondiente al valor en la matriz de diferencias. Es necesario agregar k al intervalo. Borde derecho
Si no hay una matriz de diferencias, la complejidad de realizar operaciones de intervalo en la matriz original es O (n). Si hay m operaciones de intervalo, la complejidad total es O (n * m) para obtener la matriz final; con la matriz de diferencias , La complejidad de cada operación de intervalo es O (1). Solo necesita cambiar los valores de los elementos en las dos matrices de diferencia. Para obtener la matriz final después de m veces de operaciones de intervalo, solo necesita atravesar la diferencia. matriz e invertir para obtener la matriz original. Sí, la complejidad total es O (1 * m + n) = O (m + n)
Dado que la pregunta involucra operaciones de intervalo , primero debe considerar si puede usar la matriz de diferencias para resolver el problema

1094.Coche compartido

El arreglo original en esta pregunta tiene una longitud igual al número total de estaciones, cada elemento del arreglo representa el número máximo de personas en el autobús al llegar a la estación correspondiente al subíndice.

Tenga en cuenta que cada viaje [i] es una operación de intervalo , es decir, agregar num_passengers a todos los números en el intervalo de la matriz original [ start_location , end_location ). Tenga en cuenta que end_location no está incluido , porque end_location representa el num_passengers en este viaje [ i] La estación donde los pasajeros quieren bajarse significa que estas personas no deben contarse al llegar a esta estación, por lo que lo que debe ser operado por + num_passengers es dif [ start_location ] en la matriz de diferencia, y lo que debe ser operado por - num_passengers es dif[ end_location ] en lugar de dif[ end_location + 1]

public boolean carPooling(int[][] trips, int capacity) {
    
    
    int len = trips.length;
    //题目中说到0 <= trips[i][1] < trips[i][2] <= 1000,说明
    //车站的范围是[0,1000],所以数组最长为1001
    int[] dif = new int[1001];
    //记录区间操作过程中操作到的最右边的位置,在后面遍历差分数组时只需遍历到这个位置即可不用遍历到1000
    int maxR = -1;
    for(int i = 0;i < len;i++){
    
    
        dif[trips[i][1]] += trips[i][0];
        dif[trips[i][2]] -= trips[i][0];
        maxR = Math.max(maxR,trips[i][2]);
    }
    //逆推得到原数组
    for(int i = 1;i <= maxR;i++){
    
    
        dif[i] += dif[i - 1];
    }
    //判断原数组中是否有超过capacity的元素,即一个站出现的人数会不会超出capacity
    for(int i = 0;i <= maxR;i++){
    
    
        if(dif[i] > capacity){
    
    
            return false;
        }
    }
    return true;
}

1109.Estadísticas de reservas de vuelos

La matriz original de esta pregunta es la matriz num con longitud n, donde num[i] representa cuántos asientos están reservados para el vuelo número i + 1. Cada reserva[i] es una operación de intervalo, lo que significa agregar asientos a todos los elementos de la matriz en el intervalo especificado en la matriz numérica.

public int[] corpFlightBookings(int[][] bookings, int n) {
    
    
    int[] res = new int[n];
    for(int i = 0;i < bookings.length;i++){
    
    
    	//数组下标从0开始,但是航班编号从1开始,所以区间操作的对象其实是
    	//[bookings[i][0] - 1,bookings[i][1] - 1]
        res[bookings[i][0] - 1] += bookings[i][2];
        //区间操作涉及到右边界的话,不对差分数组处理
        if(bookings[i][1] == n) continue;
        res[bookings[i][1]] -= bookings[i][2];
    }
    for(int i = 1;i < n;i++){
    
    
        res[i] += res[i - 1];
    }
    return res;
}

Supongo que te gusta

Origin blog.csdn.net/Pacifica_/article/details/123034552
Recomendado
Clasificación