0513总结:关于链表

164 阅读3分钟

复杂链表的复制

  • 题目:就是一个链表,不仅指向自己后继节点,还有一个随机指针,随机指向一个节点,可能是null.
  • 解决思路:分三步走:
  1. 完成链表节点的复制,只是单纯的复制 A->B->C->D->E变成A->A'->B->B'->C->C'->D->D'->E->E' 所以每次都需要新建一个节点插入原链表中
  2. 完成刚刚复制新加节点中的随机指针的复制(需要判断原来的节点是不是有随机指针)
  3. 拆分节点,把节点一分为二,返回复制后的节点即可。
class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return head;
        Node cur = head;
        //简单复制节点,插入原链表中
        while (cur != null) {
            Node copy = new Node(cur.val);
            copy.next = cur.next;
            cur.next = copy;
            cur = cur.next.next;
        }
        //新增节点随机指针的复制,需要注意对原生节点的随机指针的判断
        cur = head;
        while (cur != null) {
            if (cur.random != null) {
                cur.next.random = cur.random.next;
            }
            cur = cur.next.next;
        }
        //对链表进行拆分
        cur = head;
        Node tmp = head.next;
        Node copyNode = head.next;
        while (cur != null) {
            cur.next = cur.next.next;
            cur = cur.next;
            if (tmp.next != null) {
                tmp.next = tmp.next.next;
                tmp = tmp.next;
            }
        }
        return copyNode;
    }
}

两个链表的第一个公共节点

  • 思路就是:两个链表从头开始一起往前走,不断后移不断后移,走完自己本身的链表之后,接另一个链表的头节点,继续走,这样一次两个链表如果有相同的节点就会相遇。如果没有,就设置循环条件判定,返回null。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        if (headA == null || headB == null) return null;
        int count = 0;
        while (curA != curB) {
            if (curA.next != null) {
                curA = curA.next;
            } else {
                curA = headB;
                count++;
            }
            if (curB.next != null) {
                curB = curB.next;
            } else {
                curB = headA;
                count++;
            }
            if (count > 2) return null;
        }
        
        return curA;
    }
}

反转链表

要求:输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

  • 采用原地替换的方法。一个往前走,一个记录当前节点,将二者连接起来。先记录当前节点的下一个节点tmp=cur.next;,然后将当前节点的下一个节点指向前一个节点cur.next=pre,然后原本代表前一个节点的pre此时已经和cur建立连接,所以可以将其后移pre=cur;,当前节点自然也要后移cur=tmp;.
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode cur = head;
        ListNode pre = null;
        ListNode tmp = null;
        while (cur != null) {
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

链表中倒数第k个节点

  • 自己的做法是用栈压,然后弹出,也还行。
  • 推荐做法是双指针,都从头结点开始,一个先走,一个后走,两个指针之间保持的距离是(k-1)
  • 当先走的指针指向null时,输出后走的指针所指向的节点。
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode former = head;
        ListNode latter = head;
        for (int i = 0; i < k; i++) {
            if (former == null) return null;
            former = former.next;
        }
        while (former != null) {
            former = former.next;
            latter = latter.next;
        }
        return latter;
    }
}

删除链表的节点

  • 分两步走:定位节点+修改引用
  1. 定位节点:遍历,直到head.val == val
  2. 修改引用:节点cur的前驱节点为pre,后继节点为cur.next,只需要执行pre.next = cur.next即可达到删除cur节点的功能。
  3. 特例:若需要删除的是头节点,则直接返回head.next
  • 实现过程:
  1. 特例处理
  2. 初始化:pre = head; cur = head.next;
  3. 定位节点:cur为空或值相等时结束
  4. 删除节点
  • 代码:
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head.val == val) return head.next;
        ListNode pre = head;
        ListNode cur = head.next;
        while (cur != null && cur.val != val) {
            pre = cur;
            cur = cur.next;
        }
        if (cur != null) pre.next = cur.next;
        return head;
    }
}