[LeetCode] -Lista vinculada-2

prefacio

Grabación de preguntas relacionadas con listas vinculadas encontradas en los cuestionarios de LeetCode, Parte 2

142. Lista circular enlazada II

Diagrama de ejemplo de lista enlazada circular 2

Usamos punteros duales rápidos y lentos para completar esta pregunta.
Supongamos que la lista vinculada tiene un anillo, como se muestra en la figura anterior, la parte verde es el anillo y la distancia desde el encabezado de la lista vinculada hasta la entrada del anillo es a. Los punteros de rápido y lento comienzan desde la cabeza, el rápido retrocede dos nodos a la vez y el lento retrocede un nodo a la vez. Entonces, lo rápido y lo lento definitivamente se encontrarán, suponiendo que la distancia entre el nodo encontrado y el nodo de entrada del anillo es b, y la distancia al último nodo del anillo es c. Entonces la longitud del anillo es b + c

Luego, cuando se encuentran, la distancia recorrida por fast es a + n(b + c) + b . n representa cuántos anillos completos ha viajado fast. En cuanto a qué tan grande es n, depende de a y de la longitud del anillo b. + c. Relacionado, esto no afecta nuestra resolución de problemas, y la distancia recorrida por lento es a + b , es decir, cuando lento no ha completado un círculo completo, se encontrará con rápido . Por supuesto, si lo lento y lo rápido continúan sin cesar, se encontrarán muchas veces, pero solo necesitamos los datos de su primer encuentro.

Entonces de acuerdo a que la "velocidad" de rápido es el doble que la de lento, tenemos a + n(b + c) + b = 2(a + b) , simplificando se puede obtener

a = ( n − 1 ) ( b + c ) + ca = (n - 1)(b + c) + ca=( n.1 ) ( segundo+c )+C

El lado izquierdo de la ecuación representa la distancia desde la cabeza hasta la entrada del anillo. El lado derecho de la ecuación puede verse como lento. Después de caminar la distancia restante c del anillo actual, camina n - 1 anillos más. Puedes ver que Has llegado al final. Lento estará en la entrada del ring.

Entonces, deje que un nuevo puntero apunte a la cabeza, y luego, cuando la cabeza y la velocidad lenta no se encuentran, los dos punteros avanzan paso a paso hasta que los dos punteros son iguales, y el nodo donde están es el nodo de entrada del anillo.

public ListNode detectCycle(ListNode head) {
    
    
    if(head == null || head.next == null || head.next.next == null) return null;
    ListNode fast = head.next.next,slow = head.next;
    while(fast != slow){
    
    
        if(fast.next == null || fast.next.next == null) return null;
        fast = fast.next.next;
        slow = slow.next;
    }
    fast = head;
    while(fast != slow){
    
    
        fast = fast.next;
        slow = slow.next;
    }
    return fast;
}

234. Lista enlazada de palíndromo

Puede utilizar la operación de invertir la lista vinculada para encontrar el punto medio de la lista vinculada utilizando los punteros rápido y lento, y luego invertir la primera mitad o la segunda mitad. Si invierte la primera mitad, compare la primera mitad invertida y la segunda mitad original para ver si son iguales. Eso es todo.

El código recursivo se proporciona a continuación: primero busque el nodo inicial de la segunda mitad y luego recorra la segunda mitad de forma recursiva. El efecto de la recursividad es que se accederá a los nodos de la segunda mitad en orden inverso sin invertir directamente la lista vinculada.

class Solution {
    
    
    ListNode prv; //prv指向前半部分链表
    boolean rec(ListNode n){
    
    
        if(n.next != null){
    
    
            if(!rec(n.next)) return false; //如果后面的节点已经出现了不相等的情况则直接返回false
        }
        if(n.val == prv.val){
    
     //否则就比较当前节点跟对应的前半部分中的节点是否相等,同时更新prv
            prv = prv.next;
            return true;
        }
        return false;
    }
    public boolean isPalindrome(ListNode head) {
    
    
        if(head == null || head.next == null) return true;
        ListNode fast = head,slow = head;
        while(fast != null && fast.next != null){
    
    
            fast = fast.next.next;
            slow = slow.next;
        }
        //找到后半部分的起始节点,模拟几个例子可以发现,当fast不能继续往前走两步时
        //如果fast此时就是null,那么slow的位置就是后半部分的起始节点
        //不然的话就是fast的next是null,那么slow应该再走一步才是后半部分的起始节点
        if(fast != null) slow = slow.next;
        prv = head;
        return rec(slow);
    }
}

86. Lista enlazada dividida

Punteros dobles rápidos y lentos: asegúrese de que los nodos delante del puntero lento (incluido el puntero lento en sí) sean nodos menores que x, y que el siguiente nodo detrás del puntero lento sea un nodo mayor o igual a x; el puntero rápido encuentra el nodo más pequeño que Detrás del puntero lento. Hasta que el puntero rápido apunte a nulo, el algoritmo finaliza

punto importante:

  1. Para evitar la situación de insertar antes del nodo principal, configure un nodo ficticio como nodo principal temporal
  2. Dado que el nodo del puntero rápido debe insertarse después del puntero lento, el nodo predecesor del puntero rápido insertado en él debe estar conectado a su nodo sucesor. Dado que es una lista enlazada unidireccional, el nodo predecesor fastPrv del Se debe mantener el puntero rápido.
public ListNode partition(ListNode head, int x) {
    
    
    ListNode dummy = new ListNode(-1,head);
    ListNode slow = dummy;
    //让 slow 指向第一个大于等于 x 的节点的之前一个节点
    while(slow.next != null && slow.next.val < x) slow = slow.next;
    ListNode fast = head,fastPrv = dummy;
    //让 fast 指向第一个大于等于 x 的节点
    while(fast != null && fast.val < x){
    
    
        fast = fast.next;
        fastPrv = fastPrv.next;
    }
    while(true){
    
    
    	//快指针向后查找小于等于 x 的节点
        while(fast != null && fast.val >= x){
    
    
            fast = fast.next;
            fastPrv = fastPrv.next;
        }
        if(fast == null) break; //注意及时判断是否遇到 null 及时 break
        //下面就是将fast所指节点插入到slow的后面,同时移动fast 跟 slow
        fastPrv.next = fast.next;
        fast.next = slow.next;
        slow.next = fast;
        slow = slow.next;
        fast = fastPrv.next;
    }
    return dummy.next;
}

61. Lista enlazada giratoria

Conecte la cola de la lista vinculada al encabezado de la lista vinculada para formar una lista vinculada circular y luego, de acuerdo con el tamaño de k, busque el último nodo después de la rotación, desconéctelo del siguiente nodo y registre el siguiente nodo de el último nodo antes de desconectarse, es decir, el nodo principal de la lista vinculada después de la rotación, y regresar al nuevo nodo principal después de la desconexión

public ListNode rotateRight(ListNode head, int k) {
    
    
    if(head == null || head.next == null || k == 0) return head;
    ListNode tmp = head;
    int len = 1;
    //计算链表长度
    while(tmp.next != null){
    
    
        tmp = tmp.next;
        len++;
    }
    //k可能比原链表长度大,实际需要旋转的只有k%len个单位
    //如果k恰好为链表长度的倍数那就不用旋转 (旋转后跟原来链表是一样的)
    int m = k % len;
    if(m == 0) return head;
    //连成循环链表
    tmp.next = head;
    //查找旋转后链表最后一个节点,让tmp指向它
    m = len - m;
    while(m-- > 0){
    
    
        tmp = tmp.next;
    }
    //断开链接
    ListNode res = tmp.next;
    tmp.next = null;
    return res;
}

2. Suma dos números

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    
    
    ListNode p1 = l1;
    ListNode p2 = l2;
    ListNode dummy = new ListNode();  //哑节点
    ListNode tmp = dummy;
    int extra = 0;
    while(p1 != null && p2 != null){
    
    
        tmp.next = new ListNode();
        tmp = tmp.next;
        int val = p1.val + p2.val + extra;
        extra = val / 10;
        tmp.val = val % 10;
        p1 = p1.next;
        p2 = p2.next;
    }
    while(p1 != null){
    
    
        tmp.next = new ListNode();
        tmp = tmp.next;
        int val = p1.val + extra;
        extra = val / 10;
        tmp.val = val % 10;
        p1 = p1.next;
    }
    while(p2 != null){
    
    
        tmp.next = new ListNode();
        tmp = tmp.next;
        int val = p2.val + extra;
        extra = val / 10;
        tmp.val = val % 10;
        p2 = p2.next;
    }
    if(extra != 0){
    
    
        tmp.next = new ListNode(extra);
        tmp.next.next = null;
    }else {
    
    
        tmp.next = null;
    }
    return dummy.next;
}

445. Sumar dos números II

La diferencia con el resumen de la lista vinculada anterior es que la lista vinculada aquí es de mayor a menor de principio a fin, por lo que el uso de la pila para invertir los valores en las dos listas vinculadas se puede agregar directamente de menor a mayor como la suma de la lista enlazada.

Entonces, cómo hacerlo o recorrerlo solo una vez de principio a fin para obtener el resultado:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    
     
    int count = 0; //用于计算长度
    ListNode head, last;
    for(head = l1; head != null; head = head.next) count++;
    for(head = l2; head != null; head = head.next) count--;
    //让l1指向较长的链。如果count小于0,说明l2更长,那就交换。相加后的值存于l1中
    if(count < 0) {
    
      
        ListNode t = l1;
        l1 = l2;
        l2 = t;
    } 
    //head用于记录相加后链表的头节点
    //last用于记录上一个值小于9的节点,这样当某一位相加后大于9,就让last节点值增1,
    //然后让last节点与当前节点之间的所有节点 (值都为9) 的值都置为0,完成进位操作
    //在最前面加一个值为0的节点作为初始的last节点,防止最高位也产生进位,如果最终该节点值仍为0则删除该节点
    last = head = new ListNode(0); 
    head.next = l1;  //相加后的值存于l1中
    for(int i = Math.abs(count); i != 0; i--){
    
     //取l1中后面的与l2等长的部分与l2相加
        if(l1.val != 9) last = l1;
        l1 = l1.next;
    }
    int tmp;
    while(l1 != null){
    
    
        tmp = l1.val + l2.val;
        //发生进位,则更新last到l1之间所有数位的值,同时l1会成为新的last
        if(tmp > 9){
    
                       
            tmp -= 10;
            last.val += 1;
            last = last.next;
            while(last != l1){
    
    
                last.val = 0;
                last = last.next;
            }
        }
        //没有发生进位且tmp小于9的话,last就应该更新,指向l1;如果tmp就等于9,那last就保持不变
        else if(tmp != 9) last = l1; 
        l1.val = tmp;
        l1 = l1.next;
        l2 = l2.next;
    }
    return head.val == 1 ? head : head.next; //head等于1说明原来两个链表的最高位发生了进位,返回head
}

El enfoque anterior debe considerarse como la solución óptima en términos de tiempo.

Oferta Sword Pointer II 023. El primer nodo coincidente de dos listas enlazadas

gráfico de muestra

Supongamos que hay una lista enlazada A y una lista enlazada B. La longitud de sus partes superpuestas es l. La longitud de la parte frontal de la lista enlazada A, excepto la parte superpuesta, es l1. La longitud de la parte frontal de la lista enlazada B, excepto la parte superpuesta la parte es l2.

Deje que los dos punteros p1 y p2 comiencen a atravesar A y B al mismo tiempo, y luego vayan a otra lista vinculada después de atravesar. Es decir, para p1, primero atraviesa A y luego atraviesa B. Cuando atraviesa B hasta el primer nodo coincidente, la distancia que ha recorrido es l1 + l + l2 ; de manera similar, para p2, cuando atraviesa Después de atravesar B y luego atraviesa A y atravesando hasta el primer nodo coincidente, la distancia que ha recorrido es l2 + l + l1 . Se puede encontrar que las distancias recorridas por los dos punteros son iguales, lo que significa que estarán en el primer nodo coincidente. El nodo se encuentra, así que cuando se encuentren, simplemente regrese al nodo donde se encuentran.

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
    if(headA == null || headB == null) return null;
    ListNode p1 = headA;
    ListNode p2 = headB;
    while(p1 != p2){
    
    
        p1 = p1 == null ? headB : p1.next;
        p2 = p2 == null ? headA : p2.next;
    }
    return p1;
}

Supongo que te gusta

Origin blog.csdn.net/Pacifica_/article/details/124881023
Recomendado
Clasificación