Actualización continua...
1. Dos Números que Suman un Valor Objetivo
- Inicializar una tabla hash para almacenar los elementos del arreglo y sus índices.
- Recorrer el arrreglo; para cada elemento:
- Calcular la diferencia entre el valor objetivo y el elemento actual.
- Verificar si esta diferencia existe en la tabla hash.
- Si existe, se encontraron los dos números que suman el objetivo, devolver sus índices.
- Si no existe, almacenar el elemento actual y su índice en la tabla hash.
- Si se recorrió todo el arreglo sin encontrar solución, devolver un resultado especial como {-1, -1}.
class SolucionDosNumeros {
public int[] encontrarPares(int[] numeros, int objetivo) {
Map<integer integer=""> indices = new HashMap<>();
for (int i = 0; i < numeros.length; i++) {
int complemento = objetivo - numeros[i];
if (indices.containsKey(complemento)) {
return new int[]{indices.get(complemento), i};
}
indices.put(numeros[i], i);
}
return new int[]{-1, -1};
}
}</integer>
49. Agrupar Anagramas
- Método de ordenación de caracteres:
- Para cada cadena, convertirla a un arreglo de caracteres y ordenarla.
- Después de ordenar, los anagramas se convertirán en la misma secuencia de caracteres. Por ejemplo, "eat", "tea" y "ate" se convierten en "aet" después de ordenar.
- Usar la cadena ordenada como clave y agregar la cadena original a la lista correspondiente.
- Usar tabla hash:
- Utilizar una tabla hash para almacenar el mapeo entre cadenas ordenadas y listas de cadenas originales.
- Si la clave ya existe en la tabla hash, agregar la cadena original a la lista correspondiente.
- Si no existe, crear una nueva lista, agregar la cadena original y luego insertar el par clave-valor en la tabla hash.
- Resultado final:
- Recolectar todos los valores de la tabla hash (todas las listas de cadenas) y devolverlos como resultado.
class SolucionAnagramas {
public List<list>> agruparAnagramas(String[] cadenas) {
Map<string list="">> agrupador = new HashMap<>();
for (String cadena : cadenas) {
char[] caracteres = cadena.toCharArray();
Arrays.sort(caracteres);
String claveOrdenada = String.valueOf(caracteres);
agrupador.computeIfAbsent(claveOrdenada, k -> new ArrayList<>()).add(cadena);
}
return new ArrayList<>(agrupador.values());
}
}</string></list>
128. Secuencia Consecutiva Más Larga
- Inicializar un HashSet y agregar todos los elementos del arreglo para eliminar duplicados.
- Recorrer cada elemento del HashSet:
- Para cada elemento, verificar si su elemento anterior existe. Si no existe, el elemento podría ser el inicio de una nueva secuencia consecutiva.
- Desde ese inicio, verificar sucesivamente si los elementos siguientes existen, hasta que ya no existan, y registrar la longitud de esta secuencia.
- Actualizar la longitud máxima de secuencia consecutivas registrada.
class SolucionSecuencia {
public int longitudMaxima(int[] numeros) {
Set<integer> conjunto = new HashSet<>();
for (int numero : numeros) {
conjunto.add(numero);
}
int maximaLongitud = 0;
for (int numero : numeros) {
if (!conjunto.contains(numero - 1)) {
int actual = numero;
int longitudActual = 1;
while (conjunto.contains(actual + 1)) {
actual++;
longitudActual++;
}
maximaLongitud = Math.max(maximaLongitud, longitudActual);
}
}
return maximaLongitud;
}
}</integer>
283. Mover Ceros
- Usar dos punteros (puntero frontal y puntero trasero).
- Primera iteración: mover todos los elementos no-cero hacia adelante.
- Segunda iteración: llenar todos los elementos restantes con cero.
class SolucionMoverCeros {
public void moverCeros(int[] arreglo) {
int posicion = 0;
for (int i = 0; i < arreglo.length; i++) {
if (arreglo[i] != 0) {
arreglo[posicion++] = arreglo[i];
}
}
for (int i = posicion; i < arreglo.length; i++) {
arreglo[i] = 0;
}
}
}
11. Contenedor con Más Agua
- Inicializar dos punteros i y j al inicio y final del arreglo respectivamente.
- Inicializar una variable para almacenar el volumen máximo.
- Usar un ciclo while mientras i < j.
- En cada iteración, calcular el volumen actual y actualizar el resultado.
- Comparar height[i] y height[j], mover el puntero correspondiente a la línea más corta.
- Devolver el resultado, que representa el volumen máximo.
class SolucionAgua {
public int volumenMaximo(int[] alturas) {
int izquierdo = 0;
int derecho = alturas.length - 1;
int maximoVolumen = 0;
while (izquierdo < derecho) {
int ancho = derecho - izquierdo;
int altura = Math.min(alturas[izquierdo], alturas[derecho]);
maximoVolumen = Math.max(maximoVolumen, ancho * altura);
if (alturas[izquierdo] < alturas[derecho]) {
izquierdo++;
} else {
derecho--;
}
}
return maximoVolumen;
}
}
15. Tres Números que Suman Cero
- Ordenar el arreglo.
- Recorrer el arreglo:
- Para cada nums[i], si nums[i] es mayor que cero, romper el ciclo porque los números siguientes son mayores que cero y no pueden formar una trio que sume cero.
- Saltar elementos duplicados de nums[i].
- Usar dos punteros:
- Inicializar el puntero izquierdo en i + 1 y el puntero derecho al final del arreglo.
- Calcular la suma de los tres números y ajustar la posición de los punteros según el resultado, saltando elementos duplicados.
class SolucionTresSumas {
public List<list>> encontrarTriples(int[] numeros) {
List<list>> resultado = new ArrayList<>();
if (numeros == null || numeros.length < 3) {
return resultado;
}
Arrays.sort(numeros);
for (int i = 0; i < numeros.length; i++) {
if (numeros[i] > 0) {
break;
}
if (i > 0 && numeros[i] == numeros[i - 1]) {
continue;
}
int izquierda = i + 1;
int derecha = numeros.length - 1;
while (izquierda < derecha) {
int suma = numeros[i] + numeros[izquierda] + numeros[derecha];
if (suma == 0) {
resultado.add(Arrays.asList(numeros[i], numeros[izquierda], numeros[derecha]));
while (izquierda < derecha && numeros[izquierda] == numeros[izquierda + 1]) {
izquierda++;
}
while (izquierda < derecha && numeros[derecha] == numeros[derecha - 1]) {
derecha--;
}
izquierda++;
derecha--;
} else if (suma < 0) {
izquierda++;
} else {
derecha--;
}
}
}
return resultado;
}
}</list></list>
42. atrapar Lluvia
- Inicialización de dos punteros: punteros izquierdo y derecho al inicio y final del arreglo; leftMax y rightMax inicializados con las alturas en las posiciones iniciales de los punteros.
- Recorrer el arreglo:
- Actualizar leftMax y rightMax para asegurar que representen las alturas máximas a la izquierda y derecha de la posición actual del puntero.
- Comparando leftMax y rightMax, decidir qué puntero mover y calcular el agua que se puede almacenar en la posición actual.
- Como el agua está determinada por la barrera más baja, cuando leftMax es menor que rightMax, el agua está determinada por la izquierda; y viceversa.
- Acumulación del resultado: acumular el agua almacenada en cada movimiento del puntero.
class SolucionLluvia {
public int capturarAgua(int[] alturas) {
int n = alturas.length;
int izquierdo = 0;
int derecho = n - 1;
int aguaTotal = 0;
int maxIzquierdo = alturas[izquierdo];
int maxDerecho = alturas[derecho];
izquierdo++;
derecho--;
while (izquierdo <= derecho) {
maxIzquierdo = Math.max(maxIzquierdo, alturas[izquierdo]);
maxDerecho = Math.max(maxDerecho, alturas[derecho]);
if (maxIzquierdo < maxDerecho) {
aguaTotal += maxIzquierdo - alturas[izquierdo];
izquierdo++;
} else {
aguaTotal += maxDerecho - alturas[derecho];
derecho--;
}
}
return aguaTotal;
}
}
3. Subcadena Sin Caracteres Repetidos
- Inicialización:
- Punteros left y right inicializados en 0, representando los límites izquierdo y derecho de la ventana actual.
- Variable resultado para almacenar la longitud de la subcadena más larga sin caracteres repetidos.
- Conjunto hash para almacenar los caracteres dentro de la ventana actual.
- Lógica de ventana deslizante:
- En cada iteración, si el carácter en la posición right está en el conjunto, significa que hay caracteres repetidos. Contraer la ventana removiendo el carácter en la posición left y moviendo el puntero left hacia la derecha.
- Si el carácter en la posición right no está en el conjunto, expandir la ventana agregando el carácter en la posición right al conjunto y moviendo el puntero right hacia la derecha.
- Después de cada expansión de ventana, actualizar resultado para asegurar que guarde el tamaño máximo de la ventana.
- Devolver resultado al terminar.
class SolucionSubcadena {
public int longitudMasLarga(String cadena) {
if (cadena.length() <= 1) {
return cadena.length();
}
int inicio = 0;
int fin = 0;
int maximaLongitud = 0;
Set<character> caracteres = new HashSet<>();
while (fin < cadena.length() && inicio < cadena.length()) {
if (caracteres.contains(cadena.charAt(fin))) {
caracteres.remove(cadena.charAt(inicio));
inicio++;
} else {
caracteres.add(cadena.charAt(fin));
fin++;
}
maximaLongitud = Math.max(maximaLongitud, caracteres.size());
}
return maximaLongitud;
}
}</character>
438. Encontrar Todos los Anagramas
- Inicializar diccionarios:
- Crear dos tablas hash: necesidad y ventana.
- Necesidad registra cada carácter de la cadena p y su cantidad de apariciones.
- Ventana registra la cantidad de cada carácter en la ventana deslizante actual.
- Llenar el diccionario necesidad:
- Recorrer la cadena p y almacenar cada carácter y su cantidad en necesidad.
- Definir ventana deslizante:
- Usar dos punteros left y right para representar los límites de la ventana, ambos apuntando al inicio de la cadena s.
- Variable válidas para registrar la cantidad de caracteres en la ventana que cumplen las condiciones de necesidad.
- Mover el límite derecho:
- En un ciclo, mover el puntero derecho continuamente, agregar caracteres de s a la ventana y actualizar el diccionario ventana.
- Si el carácter agregado está en necesidad, actualizar el conteo en ventana y si cumple con la cantidad requerida, incrementas válidas.
- Condición de contracción de ventana:
- Cuando el tamaño de la ventana es mayor o igual a la longitud de p, verificar si la ventana actual es un anagrama válido.
- Si válidas es igual al número de claves en necesidad, significa que los caracteres de la ventana coinciden con p. Registrar la posición left.
- Mover el puntero left para contraer la ventana y actualizar el conteo en ventana.
- Si el carácter removido está en necesidad y después de reducir el conteo ya no cumple con la necesidad, decrementar válidas.
- Devolver resultado:
class SolucionAnagramasCadena {
public List<integer> encontrarAnagramas(String origen, String objetivo) {
Map<character integer=""> necesidad = new HashMap<>();
Map<character integer=""> ventana = new HashMap<>();
for (char c : objetivo.toCharArray()) {
necesidad.put(c, necesidad.getOrDefault(c, 0) + 1);
}
int izquierda = 0;
int derecha = 0;
int cumple = 0;
List<integer> resultado = new ArrayList<>();
while (derecha < origen.length()) {
char caracter = origen.charAt(derecha);
derecha++;
if (necesidad.containsKey(caracter)) {
ventana.put(caracter, ventana.getOrDefault(caracter, 0) + 1);
if (ventana.get(caracter).equals(necesidad.get(caracter))) {
cumple++;
}
}
while (derecha - izquierda >= objetivo.length()) {
if (cumple == necesidad.size()) {
resultado.add(izquierda);
}
char salida = origen.charAt(izquierda);
izquierda++;
if (necesidad.containsKey(salida)) {
if (ventana.get(salida).equals(necesidad.get(salida))) {
cumple--;
}
ventana.put(salida, ventana.get(salida) - 1);
}
}
}
return resultado;
}
}</integer></character></character></integer>
560. Subarreglos que Suman K
- Usar arreglo de prefijos.
- Usar doble ciclo for para calcular la suma de un intervalo continuo.
class SolucionSumaK {
public int subarreglosSumaK(int[] numeros, int objetivo) {
int n = numeros.length;
int[] prefijos = new int[n + 1];
prefijos[0] = 0;
for (int i = 0; i < n; i++) {
prefijos[i + 1] = prefijos[i] + numeros[i];
}
int conteo = 0;
for (int inicio = 0; inicio < n; inicio++) {
for (int fin = inicio; fin < n; fin++) {
if (prefijos[fin + 1] - prefijos[inicio] == objetivo) {
conteo++;
}
}
}
return conteo;
}
}
239. Máximo Ventana Deslizante
- Implementar estructura de máxima ventana usando cola doblemente terminada, que almacena índices del arreglo.
- La cola doblemente terminada debe mantener siempre los elementos ordenados de mayor a menor según los valores correspondientes a los índices.
- Mover la ventana continuamente; si el número que entra rompe el orden decreciente, la cola尾部 seguirá sacando elementos hasta satisfacer el requerimiento decreciente, luego insertar el número actual.
class SolucionVentanaMaxima {
public int[] maximaVentana(int[] numeros, int k) {
if (numeros == null || numeros.length == 0 || k <= 0) {
return new int[0];
}
int n = numeros.length;
List<integer> respuesta = new ArrayList<>();
Deque<integer> colaIndices = new LinkedList<>();
for (int i = 0; i < n; i++) {
while (!colaIndices.isEmpty() && numeros[colaIndices.peekLast()] <= numeros[i]) {
colaIndices.pollLast();
}
colaIndices.offerLast(i);
if (colaIndices.peekFirst() == i - k) {
colaIndices.pollFirst();
}
if (i >= k - 1) {
respuesta.add(numeros[colaIndices.peekFirst()]);
}
}
return respuesta.stream().mapToInt(Integer::intValue).toArray();
}
}</integer></integer>
76. Subcadena Mínima de Cobertura
- Ventana deslizante.
- Definición de variables:
- necesidad: tabla hash que registra la cantidad necesaria de cada carácter en la cadena t.
- ventana: tabla hash que registra la cantidad de cada carácter en la ventana deslizante actual.
- left y right: punteros de los límites izquierdo y derecho de la ventana deslizante, inicializados en 0.
- validas: registra la cantidad de tipos de caracteres en la ventana actual que cumplen las condiciones de necesidad.
- respuesta: arreglo para almacenar la longitud de la subcadena mínima y las posiciones de inicio y fin, inicializado como {-1, 0, 0}.
- requeridos: registra el total de caracteres diferentes en t.
- Expandir límite derecho:
- A medida que el puntero right se mueve a la derecha, cada vez que encuentra un carácter c, agregarlo a ventana.
- Verificar si este carácter c existe en necesidad; si existe y la cantidad en ventana alcanza la cantidad correspondiente en necesidad, incrementar validas.
- Contraer límite izquierdo:
- Cuando el valor de validas es igual a requeridos, la ventana actual contiene todos los caracteres de t.
- Intentar optimizar la longitud de la subcadena encontrada moviendo el puntero left.
- Durante la contracción, registrar la posible ventana mínima. Si la longitud de la ventana actual es menor que la longitud mínima registrada (o es el valor inicial -1), actualizar el arreglo respuesta.
- Reducir la cantidad del carácter en posición left en ventana; si esto hace que el carácter ya no cumpla con la necesidad, decrementar validas.
class SolucionSubcadenaMinima {
public String subcadenaMinima(String origen, String objetivo) {
if (origen == null || objetivo == null || origen.length() == 0 || objetivo.length() == 0) {
return "";
}
Map<character integer=""> necesidad = new HashMap<>();
Map<character integer=""> ventana = new HashMap<>();
for (char c : objetivo.toCharArray()) {
necesidad.put(c, necesidad.getOrDefault(c, 0) + 1);
}
int izquierda = 0;
int derecha = 0;
int validas = 0;
int[] respuesta = {-1, 0, 0};
int requeridos = necesidad.size();
while (derecha < origen.length()) {
char c = origen.charAt(derecha);
ventana.put(c, ventana.getOrDefault(c, 0) + 1);
if (necesidad.containsKey(c) && ventana.get(c).intValue() == necesidad.get(c).intValue()) {
validas++;
}
while (izquierda <= derecha && validas == requeridos) {
if (respuesta[0] == -1 || derecha - izquierda + 1 < respuesta[0]) {
respuesta[0] = derecha - izquierda + 1;
respuesta[1] = izquierda;
respuesta[2] = derecha;
}
char salir = origen.charAt(izquierda);
ventana.put(salir, ventana.get(salir) - 1);
if (necesidad.containsKey(salir) && ventana.get(salir).intValue() < necesidad.get(salir).intValue()) {
validas--;
}
izquierda++;
}
derecha++;
}
return respuesta[0] == -1 ? "" : origen.substring(respuesta[1], respuesta[2] + 1);
}
}</character></character>
53. suma de Subarreglo Máximo
- Programación dinámica.
- dp[i] representa la suma máxima del subarreglo desde nums[0] hasta nums[i].
class SolucionMaxSubarreglo {
public int sumaMaxima(int[] numeros) {
int[] dp = new int[numeros.length];
dp[0] = numeros[0];
int maximo = numeros[0];
for (int i = 1; i < numeros.length; i++) {
dp[i] = Math.max(dp[i - 1] + numeros[i], numeros[i]);
maximo = Math.max(maximo, dp[i]);
}
return maximo;
}
}
56. Fusionar Intervalos
- Ordenar:
- Primero, ordenar todos los intervalos según su posición inicial. Esto facilita la operación de fusión, ya que los intervalos adyacentes son más fáciles de verificar para ver si se superponen.
- Inicialización:
- Si el arreglo de entrada está vacío, devolver un arreglo bidimensional vacío.
- Recorrer y fusionar:
- Usar un ciclo para recorrer el arreglo de intervalos ordenados y verificar uno por uno si el intervalo se superpone con el intervalo actual. Si se superpone, fusionar los intervalos actualizando la posición final. Si no se superpone, agregar el intervalo actual a la lista de resultados.
- Convertir resultado:
- Finalmente, convertir la lista de resultados a un arreglo bidimensional y devolverlo.
class SolucionFusionar {
public int[][] fusionarIntervalos(int[][] intervalos) {
if (intervalos.length == 0) {
return new int[0][0];
}
Arrays.sort(intervalos, Comparator.comparingInt(a -> a[0]));
List<int> resultado = new ArrayList<>();
for (int i = 0; i < intervalos.length; i++) {
int inicio = intervalos[i][0];
int fin = intervalos[i][1];
int j = i + 1;
while (j < intervalos.length && intervalos[j][0] <= fin) {
fin = Math.max(fin, intervalos[j][1]);
j++;
}
resultado.add(new int[]{inicio, fin});
i = j - 1;
}
return resultado.toArray(new int[0][]);
}
}</int>
189. Rotar Arreglo
- Inversión completa: primero, invertir todo el arreglo. Esto invertirá las posiciones de todos los elementos.
- Encontrar punto de división: según el valor de k, determinar el punto de división. Como el arreglo es cíclico, solo necesitamos considerar k % longitud del arreglo, eliminando así los ciclos completos多余的.
- Inversión parcial: dividir el arreglo invertido en dos partes: los primeros k elementos y los elementos restantes. Invertir cada parte por separado para obtener el resultado final.
class SolucionRotar {
public void rotar(int[] arreglo, int k) {
k = k % arreglo.length;
invertir(arreglo, 0, arreglo.length - 1);
invertir(arreglo, 0, k - 1);
invertir(arreglo, k, arreglo.length - 1);
}
private void invertir(int[] arreglo, int inicio, int fin) {
while (inicio < fin) {
int temp = arreglo[inicio];
arreglo[inicio] = arreglo[fin];
arreglo[fin] = temp;
inicio++;
fin--;
}
}
}
238. Producto del Arreglo Excepto Él Mismo
- Primero inicializar arreglos de producto prefijo y sufijo, calculando el producto de cada posición de izquierda a derecha y de derecha a izquierda.
- Luego, usar los resultados de productos prefijos y sufijos para calcular el arreglo de resultados final.
- Finalmente, devolver el arreglo de resultados.
class SolucionProducto {
public int[] productoExceptoSelf(int[] numeros) {
int n = numeros.length;
int[] pre = new int[n];
pre[0] = 1;
for (int i = 1; i < n; i++) {
pre[i] = pre[i - 1] * numeros[i - 1];
}
int[] suf = new int[n];
suf[n - 1] = 1;
for (int i = n - 2; i >= 0; i--) {
suf[i] = suf[i + 1] * numeros[i + 1];
}
int[] resultado = new int[n];
for (int i = 0; i < n; i++) {
resultado[i] = pre[i] * suf[i];
}
return resultado;
}
}
41. Primer Número Positivo Faltante
class SolucionPrimerFaltante {
public int primerPositivoFaltante(int[] numeros) {
int n = numeros.length;
for (int i = 0; i < n; i++) {
while (numeros[i] > 0 && numeros[i] <= n && numeros[numeros[i] - 1] != numeros[i]) {
int temp = numeros[numeros[i] - 1];
numeros[numeros[i] - 1] = numeros[i];
numeros[i] = temp;
}
}
for (int i = 0; i < n; i++) {
if (numeros[i] != i + 1) {
return i + 1;
}
}
return n + 1;
}
}
73. Establecer Ceros en Matriz
- Primero escanear la primera fila y primera columna para ver si necesitan establecerse en 0.
- Al escanear otros elementos de filas y columnas, usar el espacio de la primera fila y primera columna como posición de marca para indicar que el elemento es 0.
class SolucionCerosMatriz {
public void establecerCeros(int[][] matriz) {
int filas = matriz.length;
int columnas = matriz[0].length;
boolean primeraFilaCero = false;
boolean primeraColumnaCero = false;
for (int j = 0; j < columnas; j++) {
if (matriz[0][j] == 0) {
primeraFilaCero = true;
break;
}
}
for (int i = 0; i < filas; i++) {
if (matriz[i][0] == 0) {
primeraColumnaCero = true;
break;
}
}
for (int i = 1; i < filas; i++) {
for (int j = 1; j < columnas; j++) {
if (matriz[i][j] == 0) {
matriz[i][0] = matriz[0][j] = 0;
}
}
}
for (int i = 1; i < filas; i++) {
for (int j = 1; j < columnas; j++) {
if (matriz[i][0] == 0 || matriz[0][j] == 0) {
matriz[i][j] = 0;
}
}
}
if (primeraFilaCero) {
for (int j = 0; j < columnas; j++) {
matriz[0][j] = 0;
}
}
if (primeraColumnaCero) {
for (int i = 0; i < filas; i++) {
matriz[i][0] = 0;
}
}
}
}
54. Matriz Espiral
class SolucionEspiral {
public List<integer> ordenEspiral(int[][] matriz) {
if (matriz.length == 0) {
return new ArrayList<>();
}
int izquierda = 0;
int derecha = matriz[0].length - 1;
int arriba = 0;
int abajo = matriz.length - 1;
int indice = 0;
Integer[] resultado = new Integer[(derecha + 1) * (abajo + 1)];
while (true) {
for (int i = izquierda; i <= derecha; i++) {
resultado[indice++] = matriz[arriba][i];
}
if (++arriba > abajo) break;
for (int i = arriba; i <= abajo; i++) {
resultado[indice++] = matriz[i][derecha];
}
if (izquierda > --derecha) break;
for (int i = derecha; i >= izquierda; i--) {
resultado[indice++] = matriz[abajo][i];
}
if (--abajo < arriba) break;
for (int i = abajo; i >= arriba; i--) {
resultado[indice++] = matriz[i][izquierda];
}
if (++izquierda > derecha) break;
}
return Arrays.asList(resultado);
}
}</integer>
48. Rotar Imagen
- Intercambiar diagonal principle.
- Intercambiar arriba y abajo.
class SolucionRotarImagen {
public void rotar(int[][] matriz) {
if (matriz.length == 0 || matriz.length != matriz[0].length) {
return;
}
int n = matriz.length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i; j++) {
int temp = matriz[i][j];
matriz[i][j] = matriz[n - 1 - j][n - 1 - i];
matriz[n - 1 - j][n - 1 - i] = temp;
}
}
for (int i = 0; i < n / 2; i++) {
for (int j = 0; j < n; j++) {
int temp = matriz[i][j];
matriz[i][j] = matriz[n - i - 1][j];
matriz[n - i - 1][j] = temp;
}
}
}
}
240. Buscar Matriz Ordenada II
- Para cada fila, realizar búsqueda binaria.
class SolucionBuscarMatriz {
public boolean buscarMatriz(int[][] matriz, int objetivo) {
int m = matriz.length;
int n = matriz[0].length;
if (m == 0 || n == 0) {
return false;
}
for (int i = 0; i < m; i++) {
int izquierda = 0;
int derecha = n - 1;
while (izquierda <= derecha) {
int medio = (izquierda + derecha + 1) >>> 1;
if (matriz[i][medio] < objetivo) {
izquierda = medio + 1;
} else if (matriz[i][medio] > objetivo) {
derecha = medio - 1;
} else {
return true;
}
}
}
return false;
}
}
160. Listas Enlazadas que se Intersecan
class SolucionInterseccion {
public ListNode obtenerInterseccion(ListNode cabezaA, ListNode cabezaB) {
ListNode p = cabezaA;
ListNode q = cabezaB;
int longitudA = 0;
int longitudB = 0;
while (p != null) {
longitudA++;
p = p.next;
}
while (q != null) {
longitudB++;
q = q.next;
}
p = cabezaA;
q = cabezaB;
if (longitudA > longitudB) {
int diferencia = longitudA - longitudB;
while (diferencia-- > 0) {
p = p.next;
}
while (p != null && q != null) {
if (p == q) {
return p;
}
p = p.next;
q = q.next;
}
} else {
int diferencia = longitudB - longitudA;
while (diferencia-- > 0) {
q = q.next;
}
while (q != null && p != null) {
if (p == q) {
return p;
}
p = p.next;
q = q.next;
}
}
return null;
}
}
206. Invertir Lista Enlazada
- Punteros anterior y posterior.
class SolucionInvertirLista {
public ListNode inverter(ListNode cabeza) {
ListNode actual = cabeza;
ListNode anterior = null;
while (actual != null) {
ListNode siguiente = actual.next;
actual.next = anterior;
anterior = actual;
actual = siguiente;
}
return anterior;
}
}
234. Lista Palindrómica
- Invertir la segunda mitad de la lista.
- Comparar las dos mitades de la lista.
class SolucionPalindromo {
public boolean esPalindromo(ListNode cabeza) {
if (cabeza == null) {
return false;
}
if (cabeza.next == null) {
return true;
}
ListNode lento = cabeza;
ListNode rapido = cabeza;
while (rapido != null && rapido.next != null) {
lento = lento.next;
rapido = rapido.next.next;
}
ListNode anterior = null;
while (siguiente != null) {
ListNode siguiente = lento.next;
lento.next = anterior;
anterior = lento;
lento = siguiente;
}
ListNode izquierda = cabeza;
ListNode derecha = anterior;
while (derecha != null) {
if (izquierda.val != derecha.val) {
return false;
}
izquierda = izquierda.next;
derecha = derecha.next;
}
return true;
}
}
141. Lista Enlazada Circular
- El puntero rápido se mueve dos pasos, el puntero lento se mueve un paso.
- Si existe un ciclo, definitivamente se encontrarán.
class SolucionCiclo {
public boolean tieneCiclo(ListNode cabeza) {
ListNode rapido, lento;
rapido = lento = cabeza;
while (rapido != null && rapido.next != null) {
rapido = rapido.next.next;
lento = lento.next;
if (rapido == lento) {
return true;
}
}
return false;
}
}
142. Lista Enlazada Circular II
- Detectar ciclo:
- Usar un ciclo while, mover el puntero rápido dos nodos cada vez y el puntero lento un nodo cada vez.
- Si rápido y lento se encuentran, hay un ciclo en la lista, salir del ciclo.
- Si rápido llega al final de la lista (rápido == null o rápido.next == null), no hay ciclo en la lista, devolver null.
- Encontrar nodo de inicio del ciclo:
- Si se detecta un ciclo, mover el puntero lento de nuevo a la cabeza de la lista.
- Luego, lento y rápido se mueven un nodo cada vez.
- Cuando lento y rápido se encuentran de nuevo, el punto de encuentro es el nodo de inicio del ciclo, devolver ese nodo.
class SolucionCicloInicio {
public ListNode detectarCiclo(ListNode cabeza) {
ListNode rapido, lento;
rapido = lento = cabeza;
while (rapido != null && rapido.next != null) {
rapido = rapido.next.next;
lento = lento.next;
if (rapido == lento) {
break;
}
}
if (rapido == null || rapido.next == null) {
return null;
}
lento = cabeza;
while (lento != rapido) {
rapido = rapido.next;
lento = lento.next;
}
return rapido;
}
}
21. Fusionar Dos Listas Ordenadas
- Ordenamiento por fusión.
class SolucionFusionarListas {
public ListNode fusionarListas(ListNode lista1, ListNode lista2) {
ListNode dummy = new ListNode(-1);
ListNode actual = dummy;
ListNode p = lista1;
ListNode q = lista2;
while (p != null && q != null) {
if (p.val < q.val) {
actual.next = p;
p = p.next;
} else {
actual.next = q;
q = q.next;
}
actual = actual.next;
}
if (p != null) {
actual.next = p;
}
if (q != null) {
actual.next = q;
}
return dummy.next;
}
}
2. Sumar Dos Números
- Plantilla de suma de alta precisión.
class SolucionSumar {
public ListNode sumarDos(ListNode lista1, ListNode lista2) {
ListNode dummy = new ListNode(-1);
ListNode actual = dummy;
ListNode p = lista1;
ListNode q = lista2;
int acarreo = 0;
while (p != null || q != null) {
if (p != null) {
acarreo += p.val;
p = p.next;
}
if (q != null) {
acarreo += q.val;
q = q.next;
}
ListNode temp = new ListNode(acarreo % 10);
actual.next = temp;
actual = actual.next;
acarreo = acarreo / 10;
}
if (acarreo != 0) {
actual.next = new ListNode(acarreo);
}
return dummy.next;
}
}
19. Eliminar el N-ésimo Nodo desde el Final
- Encontrar el nodo precedente del n-ésimo desde el final y luego eliminarlo.
class SolucionEliminarNodo {
public ListNode eliminarDesdeFin(ListNode cabeza, int n) {
if (cabeza == null) {
return null;
}
ListNode dummy = new ListNode(0);
dummy.next = cabeza;
ListNode primero = dummy;
ListNode segundo = dummy;
for (int i = 0; i <= n; i++) {
primero = primero.next;
}
while (primero != null) {
primero = primero.next;
segundo = segundo.next;
}
segundo.next = segundo.next.next;
return dummy.next;
}
}
24. Intercambiar Nodos en Pares
- Recorrer la lista e intercambiar nodos en pares.
class SolucionIntercambiarPares {
public ListNode intercambiarPares(ListNode cabeza) {
ListNode dummy = new ListNode(0);
dummy.next = cabeza;
ListNode temp = dummy;
while (temp.next != null && temp.next.next != null) {
ListNode primero = temp.next;
ListNode segundo = temp.next.next;
temp.next = segundo;
primero.next = segundo.next;
segundo.next = primero;
temp = primero;
}
return dummy.next;
}
}
25. Invertir Lista en Grupos de K
- Método reverseKGroup:
- Primero verificar si la lista está vacía; si está vacía, devolver directamente null.
- Usar dos punteros a y b inicializados en la cabeza de la lista.
- Verificar si la cantidad de nodos restantes en la lista es al menos k; si es menor que k, devolver la cabeza actual.
- Llamar al método reverse para invertir los primeros k nodos.
- Llamar recursivamente a reverseKGroup para procesar los nodos restantes y conectar la parte invertida.
- Devolver el nuevo nodo cabeza.
- Método reverse:
- Invertir nodos entre a y b (sin incluir b).
- Usar dos punteros prev y cur para realizar la inversión, prev apunta al nodo anterior y cur al nodo actual.
- Recorrer e invertir cada nodo hasta que el nodo actual sea igual a b.
- Devolver el nuevo nodo cabeza después de la inversión.
class SolucionInvertirGrupos {
public ListNode invertirGrupos(ListNode cabeza, int k) {
if (cabeza == null) {
return null;
}
ListNode a = cabeza;
ListNode b = cabeza;
for (int i = 0; i < k; i++) {
if (b == null) {
return cabeza;
}
b = b.next;
}
ListNode nuevaCabeza = inverter(a, b);
a.next = inverterGrupos(b, k);
return nuevaCabeza;
}
ListNode inverter(ListNode inicio, ListNode fin) {
ListNode anterior = null;
ListNode actual = inicio;
while (actual != fin) {
ListNode siguiente = actual.next;
actual.next = anterior;
anterior = actual;
actual = siguiente;
}
return anterior;
}
}
138. Copiar Lista con Puntero Aleatorio
- Tabla hash.
class SolucionCopiarLista {
public Node copiarLista(Node cabeza) {
Map<node node=""> mapeo = new HashMap<>();
Node actual = cabeza;
while (actual != null) {
Node temp = new Node(actual.val);
mapeo.put(actual, temp);
actual = actual.next;
}
actual = cabeza;
while (actual != null) {
Node nuevoNodo = mapeo.get(actual);
nuevoNodo.next = mapeo.get(actual.next);
nuevoNodo.random = mapeo.get(actual.random);
actual = actual.next;
}
return mapeo.get(cabeza);
}
}</node>
148. Ordenar Lista Enlazada
- Ordenamiento por fusión.
class SolucionOrdenarLista {
public ListNode ordenarLista(ListNode cabeza) {
return ordenar(cabeza, null);
}
public ListNode ordenar(ListNode inicio, ListNode fin) {
if (inicio == null) {
return inicio;
}
if (inicio.next == fin) {
inicio.next = null;
return inicio;
}
ListNode lento = inicio;
ListNode rapido = inicio;
while (rapido != fin) {
lento = lento.next;
rapido = rapido.next;
if (rapido != fin) {
rapido = rapido.next;
}
}
ListNode medio = lento;
ListNode parte1 = ordenar(inicio, medio);
ListNode parte2 = ordenar(medio, fin);
return fusionar(parte1, parte2);
}
ListNode fusionar(ListNode a, ListNode b) {
ListNode dummy = new ListNode(0);
ListNode temp = dummy;
ListNode p1 = a;
ListNode p2 = b;
while (p1 != null && p2 != null) {
if (p1.val < p2.val) {
temp.next = p1;
p1 = p1.next;
} else {
temp.next = p2;
p2 = p2.next;
}
temp = temp.next;
}
if (p1 != null) {
temp.next = p1;
}
if (p2 != null) {
temp.next = p2;
}
return dummy.next;
}
}
23. Fusionar K Listas Ordenadas
- Usando cola de prioridad (mínimo heap) se puede completar la fusión eficientemente.
class SolucionFusionarKListas {
public ListNode fusionarListas(ListNode[] listas) {
if (listas.length == 0) {
return null;
}
ListNode dummy = new ListNode(-1);
ListNode actual = dummy;
PriorityQueue<listnode> cola = new PriorityQueue<>(listas.length, (a, b) -> a.val - b.val);
for (ListNode nodo : listas) {
if (nodo != null) {
cola.add(nodo);
}
}
while (!cola.isEmpty()) {
ListNode temp = cola.poll();
actual.next = temp;
if (temp.next != null) {
cola.add(temp.next);
}
actual = actual.next;
}
return dummy.next;
}
}</listnode>
146. Caché LRU
menos usado recientemente
class LRUCache {
private int capacidad;
private LinkedHashMap<integer integer=""> cache = new LinkedHashMap<>();
public LRUCache(int capacidad) {
this.capacidad = capacidad;
}
public int obtener(int clave) {
if (!cache.containsKey(clave)) {
return -1;
}
hacerReciente(clave);
return cache.get(clave);
}
public void.put(int clave, int valor) {
if (cache.containsKey(clave)) {
cache.put(clave, valor);
hacerReciente(clave);
return;
}
if (cache.size() >= this.capacidad) {
int claveMasAntigua = cache.keySet().iterator().next();
cache.remove(claveMasAntigua);
}
cache.put(clave, valor);
}
private void hacerReciente(int clave) {
int valor = cache.get(clave);
cache.remove(clave);
cache.put(clave, valor);
}
}</integer>