[Notas de redacción de preguntas de LeetCode] Punteros dobles

Puntos de espada para ofrecer 21. Ajuste el orden de la matriz para que los números impares estén delante de los pares.

Ideas para resolver problemas:
  • Punteros de colisión , siga buscando el primer número par desde la izquierda, siga buscando el primer número impar desde la derecha e intercambie las posiciones de los dos después de encontrarlo.

Esta pregunta es casi la misma que [905. Ordenar matrices por pares e impares].

La espada apunta a la Oferta 57. La suma son dos números s

Esta pregunta es la misma que [ 167. Suma de dos números II - matriz ordenada de entrada ]

Ideas para resolver problemas:
  • 1) Choque de punteros , calcule la suma de   la suma (L + R)  y determine la relación con   el objetivo  para decidir si mover el puntero izquierdo o el puntero derecho.
  • 2) Búsqueda binaria , primero corrija un   nums[i] y luego busque binariamente el valor de target = s - nums[i]  en la matriz restante [ i+1...end ]  
  • Nota: La matriz de preguntas está ordenada en orden ascendente , por lo que se pueden utilizar los dos métodos anteriores y se puede devolver siempre que se encuentre una solución.

 

26. Eliminar duplicados en una matriz ordenada

Ideas para resolver problemas:
  • 1) Punteros rápidos y lentos , los puntos lentos apuntan a la última posición del área procesada , los puntos rápidos apuntan a la primera posición del área no procesada , inicialmente lento = 0, rápido = 1 , moviéndose continuamente hacia atrás rápidamente
  • Si el valor de fast no es igual al valor de slow , deje que slow++ coloque el valor de fast en la posición slow y finalmente devuelva slow + 1  como longitud de la matriz
  • Nota: La razón por la que se puede hacer esta pregunta es porque la matriz de la pregunta está organizada en orden ascendente .

 

Este algoritmo compara continuamente el valor del área no procesada con el último valor del área procesada. Dado que la matriz está organizada en orden ascendente, solo necesita omitir aquellos valores que son iguales al último valor del área procesada. Luego simplemente coloque los diferentes valores al frente. 

 

Ideas para resolver problemas: 
  • 2) Punteros rápidos y lentos , lento apunta a la siguiente posición del área procesada , rápido apunta a la primera posición del área no procesada , inicialmente lento = 1, rápido = 1 y continúa retrocediendo rápidamente
  • Si el valor de fast es diferente de fast-1 , coloque el valor de fast en la posición slow , luego slow++ y finalmente devuelva slow como longitud de la matriz
  • Nota: La razón por la que se puede hacer esta pregunta es porque la matriz de la pregunta está organizada en orden ascendente .

 

Este algoritmo compara continuamente el valor actual del área no procesada con el valor anterior. Dado que la matriz está dispuesta en orden ascendente, solo necesita omitir aquellos valores que son iguales al anterior. Luego simplemente coloque los diferentes valores al frente. 

Pregunta: ¿Por qué el lento y el rápido comienzan desde la posición 1 ?

Esto se debe a que el significado de lento es la siguiente posición del área procesada . Inicialmente, la posición 0 previamente reservada indica que la posición predeterminada es el área que ha sido procesada, es decir, el número 1 en sí no se repite, por lo que comienza lentamente . desde  la posición 0. Comience en la siguiente posición. La posición 1 representa la primera posición sin procesar, por lo que  comienza rápidamente desde la posición 1 . En el método 1, el significado de comenzar lentamente desde  es el mismo, lo que significa que la posición 0 es la última posición que se ha procesado (y también la primera posición que se ha procesado).

80. Eliminar duplicados en la matriz ordenada II

 

Ideas para resolver problemas:
  • Similar a 26, lento apunta a la siguiente posición   del intervalo procesado . Se reservan dos vacantes delante de lento . Lento y rápido comienzan desde el tercer elemento, es decir, inicial lento = 2, rápido = 2 y continúa retrocediendo. ayunar .     
  • Si los elementos en las posiciones fast y slow - 2 son diferentes, coloque el valor fast en la posición slow , luego slow++ y finalmente devuelva slow como longitud de la matriz.
  • Nota: La razón por la que se puede hacer esta pregunta es porque la matriz de la pregunta está organizada en orden .

 

 

La razón por la que aquí se reservan dos espacios al principio es porque la pregunta requiere que los elementos repetidos aparezcan solo dos veces, por lo que no importa si los dos primeros elementos se repiten, y no hay problema si los dos primeros elementos son diferentes.

Otra pregunta es ¿por qué  el número en la posición lenta-2 se compara con el número rápido ?

Si rápido es lo mismo que lento - 2  , y lento - 2 puede ser lo mismo que lento - 1  , entonces puede haber tres números idénticos: nums[rápido] == ​​nums[slow - 2] == nums[slow - 1 ] , en este momento el número de apariciones de elementos repetidos es > 2 , por lo que  rápido debe ser diferente de lento - 2 , para que los elementos repetidos puedan aparecer hasta 2 veces.

Entonces, si  rápido y lento - 1  se comparan y asignan cuando son diferentes, ¿está bien? Como se muestra en la imagen a continuación, esto omitirá perfectamente todas las respuestas a los números repetidos (solo se retendrá una de las repetidas 2 y 3)

 

287. Encuentra números duplicados

Ideas para resolver problemas:
  • 1) Punteros rápidos y lentos. Las matrices con números repetidos formarán un anillo , similar a una lista enlazada circular 141. La entrada al anillo es el elemento repetido .
  • Trate la matriz dada en la pregunta como una lista vinculada en forma de matriz . Los subíndices de la matriz son punteros a elementos, y los elementos de la matriz también se consideran punteros.
  • Por ejemplo, 0 es un puntero que apunta a nums[0] y nums[0] también es un puntero que apunta a nums[nums[0]] .
  • Lento y rápido son inicialmente 0 , luego lento toma un paso y rápido toma dos pasos. Si lento alcanza a rápido , sal del ciclo. En este momento, deja que lento = 0 y regresa al principio, y luego se mueve lento y rápido . sincrónicamente Si lo lento alcanza a lo rápido , entonces el punto de encuentro es el número repetido. 

Echemos un vistazo a por qué la matriz con números repetidos dada en la pregunta forma un anillo: 

Como se muestra en la figura anterior: usamos la función f(x) = nums[x] para crear una secuencia: x, nums[x], nums[nums[x]], nums[nums[nums[x]]] ... ...  Es decir, la posición de cada número es el valor del número anterior, si partimos de x=nums[0], y debido a que hay un número repetido en nums, esto inevitablemente formará una secuencia con un ciclo.

Ejemplo más complejo/círculo más grande:

No es difícil ver que el punto que entra al bucle es el número repetido que estamos buscando (es decir, 1 y 9 en los dos ejemplos anteriores), la pregunta es ¿cómo encontrarlo? La respuesta es utilizar punteros rápidos y lentos .

Ideas para resolver problemas: 
  • 2) Búsqueda binaria , bisección en el intervalo numérico [1, n], para  mid = ( L + R) / 2 , los números repetidos caen en [L, mid]  o [mid + 1, R] ,
  • Después de cada división, cuente el número de elementos ≤ mid  en la matriz y luego decida qué lado dividir a continuación según la relación entre count mid :  
  • ① Si cuenta ≤ mid , significa que no hay repetición en el lado izquierdo, reduce el límite izquierdo L = mid + 1 y busca en el medio intervalo derecho [mid + 1, R] ;
  • ② Si cuenta   >   mid , significa que hay elementos repetidos en [L, mid] , reduce R a mid - 1 y continúa buscando en la mitad izquierda del intervalo [L, mid - 1] , pero en este momento Es necesario utilizar la variable res para registrar el valor medio de los elementos eliminados , ya que puede ser un posible elemento duplicado. Finalmente regrese a res .

¿Por qué necesitamos contar el número de elementos ≤ mid al dividir en dos partes ? 

En pocas palabras, hay un hueso para cada zanahoria. Hay como máximo  huesos medianos  en el medio  y en el lado izquierdo (si contiene 1 ~ n elementos consecutivos que no se repiten). Si hay elementos repetidos, puede exceder el medio.  pozos. 

Consideramos una matriz cnt, cnt [i] representa la cantidad de elementos en nums que son menores o iguales que i, como se muestra a continuación:

Suponiendo que no hay duplicación en la matriz nums, es obvio que cnt[i] ≤ nums[i] , es decir, el número de ≤ mid no excederá mid , por ejemplo, en la figura anterior, el número de ≤ 3 es como máximo 3, pero si hay En el caso de 1 elemento repetido, esta condición podrá no cumplirse: 

Aparentemente esto se debe a números duplicados. 

Aunque la matriz de preguntas está desordenada, la matriz cnt aumenta gradualmente y es monótona. La idea de dicotomía de esta pregunta es utilizar las características de la matriz cnt para determinar la división del intervalo.

Ideas para resolver problemas: 
  • 3) Operación de bits , cuente la suma del número de unos en el i - ésimo bit del binario de todos en números y regístrelo como x , cuente la suma del número de 1 en el i-ésimo bit del binario de todos en [ 1 - n] y regístrelo como  y , si x > y , significa que el número binario del número repetido es 1 en la i- ésima posición , por lo que habrá un  número más  . Sólo necesitamos establecer el dígito binario de respuesta del dígito con más  unos en los dígitos binarios de 32 bits en .

 

Si desea hacer este código más extremo, primero puede encontrar la posición del bit más alto 1 en el número máximo N. El bucle solo necesita procesar este bit, porque los bits más altos son todos 0. Código de referencia:

27. Eliminar elementos

 

Ideas para resolver problemas:
  • 1) Punteros rápido y lento , los punteros rápido y lento comienzan desde 0 , mueven constantemente el puntero rápido y mueven el valor que encuentra el puntero rápido que no es igual a val a la posición del puntero lento .

Se puede considerar que este enfoque utiliza la propia matriz original para recibir la respuesta como matriz de resultados. 

Ideas para resolver problemas: 

  • 2) Puntero de colisión , cuando  el puntero L encuentra el elemento val  que se va a eliminar   , use el último elemento de la matriz para sobrescribir este elemento y deje que R-- .
  •   El nuevo número en L  después de sobrescribirlo aún puede ser el elemento a eliminar. No importa. El siguiente  ciclo while continuará juzgándolo y procesándolo, pero al menos hemos eliminado un elemento del final de la matriz.
  •   Dejemos que L++ se use cuando el puntero L encuentre un elemento que no sea   val  .
  • La  condición del bucle while es L<=R , porque cada número debe procesarse y finalmente se devuelve como la longitud de la matriz.

283. Ceros en movimiento

Ideas para resolver problemas:
  • 1) Punteros rápidos y lentos . Los punteros rápidos y lentos comienzan desde 0 y siguen moviendo el puntero rápido. Cuando el puntero rápido encuentra un número distinto de 0 , simplemente intercambie los valores de los elementos de los punteros rápido y lento .
  • 2)   Punteros rápidos y lentos , los punteros rápidos y lentos comienzan desde 0 y mueven continuamente el puntero rápido. Cuando el puntero rápido encuentra un número distinto de 0 , sobrescribe directamente la posición lenta anterior y luego lento++ . Una vez que todo haya terminado, establezca las posiciones después de  la lentitud en 0 .
  • 3) Punteros rápidos y lentos , optimizados para el método 2): después de que rápido encuentre un valor distinto de cero y sobrescriba lento , si rápido no es igual a lento , establezca rápido directamente en 0 y luego lento++ .

 

 

202. Número feliz

Ideas para resolver problemas:
  • 1) Bucle de detección de hash , use HashSet para determinar la duplicación. Cada bucle while determina que siempre que  n! = 1  y no esté incluido en el conjunto n  se agrega al conjunto de conjuntos y luego n se actualiza a cada dígito de n . .suma de cuadrados.
  • Después de finalizar el ciclo, simplemente devuelva si n es igual a 1 (solo hay dos situaciones en las que se salta del ciclo, o n se convierte en 1 o hay una repetición en el conjunto set , lo que significa que hay un ciclo).
  • La técnica para encontrar  la suma de los cuadrados de  num  : cada vez que num % 10  saca los dígitos individuales, luego calcula el cuadrado y lo suma al resultado, num / 10  elimina los dígitos individuales hasta que  num  se convierte en  y se detiene. 

Aunque esta pregunta está marcada como dificultad [Fácil] en LeetCode, creo que hay una situación que es difícil de observar, es decir, además de obtener 1 al final, también puede convertirse en un bucle infinito al final . aquí El bucle puede eventualmente encontrar números repetidos , o puede volverse cada vez más grande hasta llegar al infinito . En otras palabras, puede haber dos situaciones, y lo que se explica en muchos cursos en línea es solo la solución predeterminada de configurar una tabla hash con números repetidos .

Echemos un vistazo a la explicación oficial de Likou:

Podemos comenzar con algunos ejemplos. Empecemos con 7. Entonces el siguiente número es 49 (porque 72 = 49), y luego el siguiente número es 97 (porque 42 + 92 = 97). Podemos seguir repitiendo este proceso hasta obtener 1. Como obtuvimos 1, sabemos que 7 es un número feliz y la función debería devolver verdadero .

 

Como otro ejemplo, comencemos con 116. Al calcular repetidamente el siguiente número mediante la suma de cuadrados, terminamos con 58 y, después de más cálculos, volvemos a 58. Como volvemos a un número que ya calculamos, sabemos que hay un bucle y, por lo tanto, es imposible llegar a 1. Entonces, para 116, la función debería devolver false

 

Según nuestra exploración, suponemos que hay tres posibilidades:

    1. Terminarás con 1.

    2. Eventualmente entrará en un ciclo.

    3. El valor será cada vez mayor y finalmente se acercará al infinito.

La tercera situación es más difícil de detectar y abordar. ¿Cómo sabemos que seguirá creciendo en lugar de terminar en 1? Podemos pensar detenidamente en el siguiente dígito del número más grande para cada dígito.

 

Para un número de 3 dígitos, no puede ser mayor que 243. Esto significa que se queda atascado en el bucle debajo de 243 o cae a 1. Los números con 4 o más dígitos pierden un bit en cada paso hasta que baja a 3 dígitos. Entonces sabemos que, en el peor de los casos, el algoritmo podría recorrer todos los números por debajo de 243 y luego volver a un ciclo en el que ya estuvo o volver a 1. Pero no durará indefinidamente, por lo que descartamos la tercera opción.

Incluso si no necesita manejar el tercer caso en su código, aún necesita comprender por qué nunca sucede para poder justificar por qué no necesita manejarlo.

Ideas para resolver problemas: 
  • 2) Bucle de detección de puntero rápido y lento , los valores iniciales del puntero lento y del puntero rápido comienzan desde n . En cada ciclo, el puntero lento se calcula y actualiza una vez ( el lento se actualiza a su suma de cuadrados), mientras que el El puntero rápido se calcula y actualiza dos veces ( actualización rápida ) (suma de los cuadrados de sus bits) hasta que el puntero rápido se convierte en 1 , o el puntero rápido y el puntero lento se encuentran, el bucle sale.
  • Finalmente, simplemente devuelva si el puntero rápido es igual a 1 (si el bucle sale porque los punteros rápido y lento son iguales, significa que hay un bucle).
  • Nota: El puntero rápido encontrará 1 primero .

Tenga en cuenta que en el código anterior, es mejor utilizar un bucle do- while , de lo contrario, puede salir del bucle cuando aparezca, porque tanto el lento como el rápido se inicializan en n   . Por supuesto, como se ha analizado antes, eventualmente se convertirá en 1, o aparecerá un bucle (repetido), por lo que también puede escribir directamente un bucle infinito while (verdadero) para detectar y escribir las condiciones para salir en el cuerpo del bucle.

Los punteros rápidos y lentos son un método clásico para detectar problemas cíclicos.

881. Bote salvavidas

Ideas para resolver problemas:
  • 1) Clasificación + Puntero de colisión . Se requiere el número mínimo de barcos. Luego, tantas personas como sea posible deben estar en  un  barco, y el resto de personas deben estar en un barco. Por lo tanto, primero ordene y luego seleccione dos barcos de los dos. termina en el medio con el indicador de colisión. Si la suma de los pesos individuales <= el límite , puedes tomar un bote. Si el peso de dos personas excede el límite , solo la persona más pesada puede tomar un bote por sí misma .
  • Al final del ciclo, si  L == R , la persona restante tomará un bote sola. (Tenga en cuenta que salir del ciclo también puede deberse a que L > R , por lo que debe juzgar cuando finalmente regrese)
Ideas para resolver problemas: 
  • 2) Ordenar + expansión central, también debe ordenar primero y luego encontrar la primera posición <= límite / 2 de derecha a izquierda, y luego configurar los punteros dobles desde esta posición para expandirse hacia la izquierda y hacia la derecha al mismo tiempo , mirando Para un lugar donde dos personas pueden sentarse en el mismo barco. , las personas restantes que están solas solo pueden sentarse una persona y un barco.
  • Penalización especial 1: si el peso de todos excede la mitad del límite de peso ( > límite / 2 ), entonces solo una persona puede tomar un bote y la longitud de las personas se devuelve directamente sin continuar. Porque en este momento la suma de los pesos de dos personas cualesquiera excederá el límite .
  • Juicio especial 2: si el peso de la persona más pesada es <= límite / 2 , entonces la suma de los pesos de dos personas combinadas no excederá el límite , por lo que puede organizar directamente que 2 personas tomen un bote y regresen. directamente a (personas.longitud + 1)/2 .

 

El cálculo del método de expansión central es un poco problemático. En términos relativos, este problema sigue siendo el método 1) Los punteros de colisión son más fáciles de entender y el código es más simple.

 

Supongo que te gusta

Origin blog.csdn.net/lyabc123456/article/details/133566300
Recomendado
Clasificación