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.
点个[在看],是对小灰最大的支持!