Hace algún tiempo, presentamos un problema de algoritmo clásico en LeetCode [el problema de la suma de dos números] .
Esta vez, ampliamos un poco el problema e intentamos encontrar tres números en la matriz que sumen un "valor específico" .
¿Cuáles son los requisitos específicos del tema? Dada la siguiente matriz de enteros:
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 final de salida es el siguiente:
【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:
Calcula 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 indicadores, 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, es posible 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 y se ajustan al 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.
点个[在看],是对小灰最大的支持!