Problema de algoritmo cómico: suma de dos números y suma de tres números

Hace algún tiempo, Xiao Hui explicó dos problemas de algoritmos clásicos en leecode:

Cómic: ¿Cómo encontrar dos números cuya suma sea un "valor específico" en una matriz?

Cómic: ¿Cómo encontrar tres números cuya suma sea un "valor específico" en una matriz?

Hoy, Xiao Hui integró estas dos preguntas y revisó los detalles de ellas. Gracias por sus correcciones.

----- el día siguiente -----

Qué significa eso? Tomemos un ejemplo, dada la siguiente matriz de enteros (asumiendo que no hay elementos duplicados en la matriz):

Elegimos aleatoriamente un valor específico, como 13, y pedimos encontrar todas las combinaciones donde la suma de dos números es igual a 13 .

Dado que 12 + 1 = 13, 6 + 7 = 13, el resultado de salida final (la salida es un subíndice) es el siguiente:

【dieciséis】

【2 , 7】

La idea que Xiao Hui quiere expresar es atravesar toda la matriz directamente y agregar otros elementos cada vez que se atraviesa un elemento para ver si la suma es igual a ese valor específico.

En la primera ronda , agregue el elemento 5 y otros elementos:

No se encontraron dos elementos que cumplan con los requisitos.

En la segunda ronda , agregue el elemento 12 y otros elementos:

Se encuentra que el resultado de sumar 12 y 1 es 13, lo que cumple con los requisitos.

Según esta idea, se ha atravesado todo el conjunto.


————————————



Demostremos en detalle:

En la primera ronda , visite el elemento 5 y calcule 13-5 = 8. Busque 8 en la tabla hash y descubra que no se puede encontrar:

En la segunda ronda , visite el elemento 12 y calcule 13-12 = 1. Busque 1 en la tabla hash y encuentre que el subíndice del elemento 1 es 6, por lo que el elemento 12 (el subíndice es 1) y el elemento 1 (el subíndice es 6) son un par de resultados:

En la tercera ronda , visite el elemento 6 y calcule 13-6 = 7. Busque 7 en la tabla hash y encuentre que el subíndice del elemento 7 es 7, entonces el elemento 6 (el subíndice es 2) y el elemento 7 (el subíndice es 7) son un par de resultados:

De acuerdo con esta idea, simplemente recorra toda la matriz todo el tiempo.

public class FindSumNumbers {

    public static List<List<Integer>> twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        List<List<Integer>> resultList = new ArrayList<>();
        for (int i = 1; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        for (int i = 0; i < nums.length; i++) {
            int other = target - nums[i];
            if (map.containsKey(other) && map.get(other) != i) {
                resultList.add(Arrays.asList(i,map.get(other)));
                //为防止找到重复的元素对,匹配后从哈希表删除对应元素
                map.remove(nums[i]);
            }
        }
        return resultList;
    }

    public static void main(String[] args) {
        int[] nums = {5,12,6,3,9,2,1,7};
        List<List<Integer>> resultList = twoSum(nums, 13);
        for(List<Integer> list : resultList){
            System.out.println(Arrays.toString(list.toArray()));
        }
    }

}

    public static List<List<Integer>> twoSumV2(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        List<List<Integer>> resultList = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            int other = target - nums[i];
            if (map.containsKey(other)) {
                resultList.add(Arrays.asList(map.get(other),i));
            }
            map.put(nums[i], i);
        }
        return resultList;
    }

Por ejemplo, dada la siguiente matriz de enteros (asumiendo que no hay elementos duplicados en la matriz):

Elegimos arbitrariamente un valor específico, como 13, y pedimos encontrar todas las combinaciones donde la suma de tres números es igual a 13 .

Dado que 5 + 6 + 2 = 13, 5 + 1 + 7 = 13, 3 + 9 + 1 = 13, el resultado de salida final es el siguiente (solo envíe el valor del elemento directamente):

【5 , 6,2】

【5 , 1,7】

【3 , 9,1】


La idea de Xiao Hui es transformar el "problema de suma de tres números" original en un "problema de suma de dos números" n veces.

Tomemos la matriz anterior como ejemplo, seleccione un valor específico de 13 y demostremos las ideas específicas de Xiao Hui:

En la primera ronda , visite el primer elemento 5 de la matriz y convierta el problema en encontrar dos números que sumen 8 (13-5) de los siguientes elementos:

¿Cómo encontrar dos números cuya suma sea 8? Según lo que dijimos la última vez, podemos usar una tabla hash para resolver de manera eficiente:

En la segunda ronda , visite el segundo elemento 12 de la matriz y convierta el problema en encontrar dos números que sumen 1 (13-12) de los siguientes elementos:

En la tercera ronda , visite el tercer elemento 6 de la matriz y convierta el problema en encontrar dos números que sumen 7 (13-6) de los siguientes elementos:

Por analogía, atravesar toda la matriz todo el tiempo equivale a resolver el problema de la suma de dos números n veces.

    public static List<List<Integer>> threeSum(int[] nums, int target) {
        List<List<Integer>> resultList = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            Map<Integer, Integer> map = new HashMap<>();
            int d1 = target - nums[i];
            //寻找两数之和等于d1的组合
            for (int j = i+1; j < nums.length; j++) {
                int d2 = d1 - nums[j];
                if (map.containsKey(d2)) {
                    resultList.add(Arrays.asList(nums[i], d2, nums[j]));
                }
                map.put(nums[j], j);
            }
        }
        return resultList;
    }

En el código anterior, la complejidad temporal de resolver el "Problema de la suma de dos números" en cada ronda es O (n), un total de n iteraciones, por lo que la complejidad temporal total de la solución es O (n²) .

En cuanto a la complejidad del espacio, la misma tabla hash se crea repetidamente, y hay como máximo n-1 pares clave-valor en la tabla hash, por lo que la complejidad espacial de la solución es O (n) .

Todavía usamos la matriz anterior como ejemplo para ordenar la matriz en orden ascendente:

Esto es un poco abstracto, demostremos en detalle:

En la primera ronda , visite el primer elemento 1 de la matriz y convierta el problema en encontrar dos números que sumen 12 (13-1) de los siguientes elementos.

¿Cómo encontrar dos números cuya suma es 12? Establecemos dos punteros, el puntero j apunta al elemento 2 más a la izquierda de los elementos restantes, y el puntero k apunta al elemento 12 más a la derecha:

Calcula la suma de los elementos correspondientes de los dos punteros, 2 + 12 = 14> 12, el resultado es demasiado grande.

Dado que la matriz está organizada en orden ascendente, el elemento a la izquierda de k debe ser menor que k, por lo que movemos el puntero k hacia la izquierda:

Calcule la suma de los elementos correspondientes de los dos punteros, 2 + 9 = 11 <12. Esta vez el resultado es demasiado pequeño.

El elemento a la derecha de j debe ser mayor que j, por lo que movemos el puntero j un lugar a la derecha:

Calcula la suma de los elementos correspondientes de los dos punteros, 3 + 9 = 12, ¡que cumple con los requisitos!

Así que encontramos con éxito un conjunto de combinaciones coincidentes: 1, 3, 9

Pero este no es el final, tenemos que seguir buscando otras combinaciones, deja que el puntero k siga moviéndose hacia la izquierda:

 Calcule la suma de los elementos correspondientes de los dos punteros, 3 + 7 = 10 <12, el resultado es demasiado pequeño.

Entonces movemos el puntero j hacia la derecha:

Calcule la suma de los elementos correspondientes de los dos punteros, 5 + 7 = 12, y encuentre un grupo que cumpla con los requisitos:

1, 5, 7

Seguimos buscando y movemos el puntero k hacia la izquierda:

 Calcule la suma de los elementos correspondientes de los dos punteros, 5 + 6 = 11 <12, el resultado es demasiado pequeño.

Entonces movemos el puntero j hacia la derecha:

En este punto, los dos punteros son coincidentes, si continúas moviéndote puede que se repita la combinación encontrada antes, por lo que directamente terminamos el ciclo.

En la segunda ronda , visite el segundo elemento 2 de la matriz y convierta el problema en encontrar dos números que sumen 11 (13-2) de los siguientes elementos.

Aún establecemos dos punteros, el puntero j apunta al elemento 3 más a la izquierda de los elementos restantes, y el puntero k apunta al elemento 12 más a la derecha:

 Calcula la suma de los elementos correspondientes de los dos punteros, 3 + 12 = 15> 11. El resultado es demasiado grande.

Movemos el puntero k hacia la izquierda:

 Calcula la suma de los elementos correspondientes de los dos punteros, 3 + 9 = 12> 11. El resultado sigue siendo demasiado grande.

Dejamos que el puntero k continúe moviéndose hacia la izquierda:

 Calcule la suma de los elementos correspondientes de los dos punteros, 3 + 7 = 10 <11, el resultado es demasiado pequeño.

Movemos el puntero j hacia la derecha:

 Calcula la suma de los elementos correspondientes de los dos punteros, 5 + 7 = 12> 11. El resultado es demasiado grande.

Movemos el puntero k hacia la izquierda:

 Calcula la suma de los elementos correspondientes de los dos punteros, 5 + 6 = 11, por lo que encontramos un grupo que cumple con los requisitos:

2, 5, 6

Seguimos buscando y movemos el puntero k hacia la izquierda:

En este punto, los punteros dobles se superponen una vez más y terminamos el ciclo.

De acuerdo con esta idea, hemos estado atravesando toda la matriz.

De esta manera, se utilizan dos punteros para señalar los dos extremos de la matriz y se mueven constantemente más cerca del centro para encontrar una combinación coincidente.Este es el método de puntero doble, también conocido como el "método de sujeción".

    public static List<List<Integer>> threeSumv2(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        //大循环
        for (int i = 0; i < nums.length; i++) {
            int d = target - nums[i];
            // j和k双指针循环定位,j在左端,k在右端
            for (int j=i+1,k=nums.length-1; j<nums.length; j++) {
                // k指针向左移动
                while (j<k && (nums[j]+nums[k])>d) {
                    k--;
                }
                //双指针重合,跳出本次循环
                if (j == k) {
                    break;
                }
                if (nums[j] + nums[k] == d) {
                    List<Integer> list = Arrays.asList(nums[i], nums[j], nums[k]);
                    resultList.add(list);
                }
            }
        }
        return resultList;
    }

En la superficie del código anterior, hay tres bucles, pero cada ronda del puntero j y k se mueve como máximo n-1 veces, por lo que la complejidad de tiempo total de la solución es O (n²) .

Lo más importante es que esta solución no utiliza conjuntos adicionales (la clasificación se realiza directamente en la matriz de entrada), por lo que la complejidad del espacio es solo O (1) .

-----FIN-----

Amigos a los que les gusta este artículo, bienvenidos a seguir al programador de cuentas oficial  Xiaohui y ver contenido más emocionante.

点个[在看],是对小灰最大的支持!

Supongo que te gusta

Origin blog.csdn.net/bjweimengshu/article/details/109063928
Recomendado
Clasificación