力扣技巧-链表

本文探讨了链表相关的LeetCode题目,包括设计链表、反转链表、删除重复元素、合并链表等。文章介绍了三种核心策略:假头处理、新链表构建和双指针技术,并通过具体题目如删除链表倒数N个节点、环形链表等展示了这些方法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

707. 设计链表
class MyLinkedList {

    private class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }

        public ListNode() {

        }
    }

    private ListNode dummy = new ListNode();
    private int length = 0;
    private ListNode tail = dummy;

    public MyLinkedList() {

    }

    private ListNode getPreNode(int index) {

        ListNode front = dummy.next;
        ListNode back = dummy;
        for (int i = 0; i < index; i ++) {
            back = front;
            front = front.next;
        }

        return back;
    }
    
    public int get(int index) {

        if (index < 0 || index >= length) {
            return -1;
        }

        return getPreNode(index).next.val;
    }
    
    public void addAtHead(int val) {
        ListNode p = new ListNode(val);
        p.next = dummy.next;
        dummy.next = p;
        if (tail == dummy) {
            tail = p;
        }
        length ++;
    }
    
    public void addAtTail(int val) {
        ListNode p = new ListNode(val);
        tail.next = p;
        tail = p;
        length ++;
    }
    
    public void addAtIndex(int index, int val) {
        if (index > length) {
            return;
        } else if (index == length) {
            addAtTail(val);
            return;
        } else if (index <= 0) {
            addAtHead(val);
            return;
        }

        ListNode pre = getPreNode(index);
        ListNode p = new ListNode(val);
        p.next = pre.next;
        pre.next = p;
        length ++;
    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= length) {
            return;
        }

        ListNode pre = getPreNode(index);
        if (tail == pre.next) {
            tail = pre;
        }
        length --;
        pre.next = pre.next.next;
    }
}
206. 反转链表
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode temp = head;
        ListNode dummy = new ListNode();
    
        while (null != temp) {
            ListNode p = new ListNode(temp.val);
            p.next = dummy.next;
            dummy.next = p;
            temp = temp.next;
        }

        return dummy.next;
    }
}
203. 移除链表元素
class Solution {
    public ListNode removeElements(ListNode head, int val) {

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode back = dummy;
        ListNode front = dummy.next;

        while (null != front) {

            while (null != front && val == front.val) {
                front = front.next;
            }

            back.next = front;
            back = back.next;
            if (null != front) {
                front = front.next;
            }
        }

        return dummy.next;
    }
}
83. 删除排序链表中的重复元素
class Solution {
    public ListNode deleteDuplicates(ListNode head) {

        ListNode front = head;
        ListNode back = head;

        while (null != front) {

            while (null != front && back.val == front.val) {
                front = front.next;
            }

            back.next = front;
            back = front;
        }

        return head;
    }
}
82. 删除排序链表中的重复元素 II
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
       // 这里我们采用新生成2条件新链表的思路

        // ans 这个链表总是用来存放返回结果
        ListNode ans = new ListNode();
        ListNode ans_tail = ans;

        // tmp 这个链表中总是用来存放相同元素。
        // 如果发现要append一个不同的
        ListNode tmp = new ListNode();
        ListNode tmp_tail = tmp;
        ListNode p = head;

        while (p != null) {
            ListNode back = p.next;

            // 如果tmp链表为空,或者tmp链表中的元素和p.val
            // 相等 => 那么把p添加到tmp链表中
            if (tmp_tail == tmp || p.val == tmp_tail.val) {
                tmp_tail.next = p;
                tmp_tail = p;
            } else {
                // 如果发现和tmp链表中的元素不相等
                // 那么看一下tmp链表中的结点个数
                // 如果tmp链表中只有一个结点
                if (tmp_tail == tmp.next) {
                    // 那么这里把这个结点放到ans中
                    ans_tail.next = tmp.next;
                    ans_tail = ans_tail.next;
                }
                // 如果tmp链表中有多个结点,那么什么也不做

                // 无论tmp链表中是一个结点,还是有多个结点
                // 都要清空tmp链表

                tmp_tail = tmp;
                tmp.next = null;

                // 再把p结点安装到tmp上
                // 可以和前面的语句合并
                // 这里为了逻辑更清晰这么写。
                tmp_tail.next = p;
                tmp_tail = p;
            }

            p = back;
        }

        // 如果tmp链表中还有元素,并且只有一个
        if (tmp_tail == tmp.next) {
            ans_tail.next = tmp.next;
            ans_tail = ans_tail.next;
        }
        ans_tail.next = null;

        return ans.next;
    }
}
21. 合并两个有序链表
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        if (null == list1 && null == list2) {
            return null;
        }

        if (null == list1) {
            return list2;
        }

        if (null == list2) {
            return list1;
        }

        ListNode res = new ListNode(Integer.MIN_VALUE);
        ListNode tail = res;

        while (null != list1 && null != list2) {
            if (list1.val < list2.val) {
                tail.next = new ListNode(list1.val);
                tail = tail.next;
                list1 = list1.next;
            } else {
                tail.next = new ListNode(list2.val);
                tail = tail.next;
                list2 = list2.next;
            }
        }

        while (null != list1) {
            tail.next = new ListNode(list1.val);
            tail = tail.next;
            list1 = list1.next;
        }

        while (null != list2) {
            tail.next = new ListNode(list2.val);
            tail = tail.next;
            list2 = list2.next;
        }

        return res.next;
    }
}
23. 合并K个升序链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        final int n = (null == lists ? 0 : lists.length);
        Queue<ListNode> queue = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);

        for (int i = 0; i < n; i ++) {
            ListNode node = lists[i];
            if (null != node) {
                queue.add(node);
            }
        }

        ListNode dummy = new ListNode();
        ListNode tail = dummy;

        while (!queue.isEmpty()) {

            ListNode curNode = queue.poll();
            tail.next = curNode;
            tail = tail.next;
            if (null != curNode.next) {
                queue.add(curNode.next);
            }
        }

        tail.next = null;

        return dummy.next;
    }
}
24. 两两交换链表中的节点
class Solution {
    public ListNode swapPairs(ListNode head) {

        if (null == head) {
            return head;
        }

        ListNode front = head.next;
        ListNode back = head;

        while (null != front) {

            int temp = back.val;
            back.val = front.val;
            front.val = temp;

            if (null == front.next) {
                front = null;
            } else {
                back = back.next.next;
                front = front.next.next;
            }
        }

        return head;
    }
}

双指针

1.间隔指针:前面的指针先走一步,然后后面的指针再一起走;前面的指针先走 k 步,后面的指针再一起走。
2。快慢指针:两个指针的速度一快一慢前进,比如一个每次走一步,一个每次走两步。

19. 删除链表的倒数第 N 个结点
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode dummy = new ListNode();
        dummy.next = head;

        int preWalkedSteps = 0;
        ListNode front = dummy;
        while (preWalkedSteps < n && null != front && null != front.next) {
            front = front.next;
            preWalkedSteps ++;
        }

        ListNode back = dummy;
        while (null != front && null != front.next) {
            back = back.next;
            front = front.next;
        }

        if (preWalkedSteps == n) {
            back.next = back.next.next;
        }

        return dummy.next;
    }
}
让指针指向链表最后一个结点的 while 语句的写法
while (null != front && null != front.next) {
	// your code
}
141. 环形链表
public class Solution {
    public boolean hasCycle(ListNode head) {

       ListNode front = head;
       ListNode back = head;

        while (null != front && null != front.next) {
            back = back.next;
            front = front.next.next;
            if (back == front) {
                return true;
            }
        }

        return false;
    }
}
142. 环形链表 II
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode front = dummy;
        ListNode back = dummy;
        boolean flag = false;

        while (null != front && null != front.next) {
            back = back.next;
            front = front.next.next;
            if (back == front) {
                flag = true;
                break;
            }
        }

        if (!flag) {
            return null;
        }

        back = dummy;
        while (back != front) {
            back = back.next;
            front = front.next;
        }

        return front;
    }
}

总结:
第一斧:假头。假头的作用主要是避免关于空链表的判断与讨论,假头还可以用来避免检查前驱结点为空的情况。

第二斧:新链表。新链表的引入是为了解决在旧链表中进行原地的交换、插入、删除,把复杂的操作变成在新链表中头部插入或者尾部添加。

第三斧:双指针。双指针主要是用于寻找链表中的特定结点,双指针的走法可以一次一步,可以有快有慢,出发点也可以有前有后。

148. 排序链表
class Solution {
    public ListNode sortList(ListNode head) {

        Queue<ListNode> queue = new PriorityQueue<>((v1, v2) -> (v1.val - v2.val));
        while (null != head) {
            queue.add(head);
            head = head.next;
        }

        ListNode dummy = new ListNode();
        ListNode tail = dummy;

        while (!queue.isEmpty()) {
            ListNode curNode = queue.poll();
            tail.next = curNode;
            tail = tail.next;
            tail.next = null;
        }

        return dummy.next;
    }
}
160. 相交链表
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        ListNode tempA = headA;
        ListNode tempB = headB;
        boolean flagA = false;
        boolean flagB = false;

        while (tempA != tempB) {
            tempA = tempA.next;
            if (null == tempA) {
                if (flagA) {
                    return null;
                }
                flagA = true;
                tempA = headB;
            }

            tempB = tempB.next;
            if (null == tempB) {
                if (flagB) {
                    return null;
                }
                flagB = true;
                tempB = headA;
            }
        }

        return tempA;
    }
}
328. 奇偶链表
class Solution {
    public ListNode oddEvenList(ListNode head) {

        if (null == head) {
            return null;
        }

        ListNode odd = head;
        ListNode oddHead = head;
        ListNode even = head.next;
        ListNode evenHead = head.next;

        while (null != even && null != even.next) {
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = evenHead;

        return head;
    }
}
234. 回文链表
class Solution {
    public boolean isPalindrome(ListNode head) {

        Stack<ListNode> stack = new Stack<>();
        ListNode curNode = head;
        while (null != curNode) {
            stack.add(curNode);
            curNode = curNode.next;
        }

        while (null != head) {
            if (head.val != stack.pop().val) {
                return false;
            }
            head = head.next;
        }

        return true;
    }
}
430. 扁平化多级双向链表
class Solution {
    public Node flatten(Node head) {
        Node curNode = head;
        while (null != curNode) {
            if (null == curNode.child) {
                curNode = curNode.next;
            } else {
                Node temp = curNode.next;
                Node curNext = flatten(curNode.child);
                curNode.child = null;
                curNode.next = curNext;
                curNext.prev = curNode;
                while (null != curNode.next) {
                    curNode = curNode.next;
                }
                if (null != temp) {
                    curNode.next = temp;
                    temp.prev = curNode;
                }
                
            }
        }

        return head;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值