算法通关村第一关——链表白银挑战笔记

1. 五种方法解决两个链表的第一个公共子节点问题

剑指offer52

1.1 集合/Hash做法

先将第一个链表元素全部存到Map里,然后一边遍历第二个链表,一边检测当前元素是否在Hash中,如果两个链表有交点,那就找到了。

class Solution {
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Set<ListNode> firstSet = new HashSet<>();
        for (ListNode p = headA; p != null; p = p.next) {
            firstSet.add(p);
        }
        for (ListNode p = headB; p != null; p = p.next) {
            if (firstSet.contains(p)) {
                return p;
            }
        }
        return null;
    }
}

1.2 栈

将两个链表分别压到两个栈里,之后一边同时出栈,一边比较出栈元素是否一致,如果一致则说明存在相交,然后继续找,最晚出栈的那组一致的节点就是要找的位置

class Solution {
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Stack<ListNode> stack1 = new Stack<>();
        Stack<ListNode> stack2 = new Stack<>();

        ListNode p1 = headA;
        while (p1 != null) {
            stack1.push(p1);
            p1 = p1.next;
        }

        ListNode p2 = headB;
        while (p2 != null) {
            stack2.push(p2);
            p2 = p2.next;
        }

        ListNode pubNode = null;
        while (!stack1.empty() && !stack2.empty()) {
            ListNode s1 = stack1.pop();
            ListNode s2 = stack2.pop();
            if (Objects.equals(s1, s2)) {
                pubNode = s1;
            }
        }

        return pubNode;
    }
}

1.3 拼接两个字符串

先看下面的链表A和B:
A: 0-1-2-3-4-5
B: a-b-4-5
如果分别拼接成AB和BA会怎么样呢?
AB:0-1-2-3-4-5-a-b-4-5
BA:a-b-4-5-0-1-2-3-4-5

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

1.4 差和双指针

假如公共子节点一定存在第一轮遍历,假设La长度为L1,Lb长度为L2.则|L2-L1|就是两个的差值。第二轮遍历,长的先走|L2-L1|,然后两个链表同时向前走,结点一样的时候就是公共结点了。

class Solution {
    public ListNode getIntersectionNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null || pHead2==null){
            return null;
        }
        ListNode current1=pHead1;
        ListNode current2=pHead2;
        int l1=0,l2=0;
        //分别统计两个链表的长度
        while(current1!=null){
            current1=current1.next;
            l1++;
        }

        while(current2!=null){
            current2=current2.next;
            l2++;
        }
        current1=pHead1;
        current2=pHead2;
        int sub=l1>l2?l1-l2:l2-l1;
        //长的先走sub步
        if(l1>l2){
            int a=0;
            while(a<sub){
                current1=current1.next;
                a++;
            }   
        }

        if(l1<l2){
            int a=0;
            while(a<sub){
                current2=current2.next;
                a++;
            }   
        }
        //同时遍历两个链表
        while(current2!=current1){
            current2=current2.next;
            current1=current1.next;
        } 

        return current1;
    }
}

2. 判断链表是否为回文序列

LeetCode234

2.1 栈

将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较两者元素值,只要有一个不相等,那就不是。

class Solution {
    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> stack = new Stack<>();
        ListNode p = head;
        while (p != null) {
            stack.add(p);
            p = p.next;
        }
        p = head;
        while (p != null) {
            ListNode p2 = stack.pop();
            if (p.val != p2.val)
                return false;
            p = p.next;
        }
        return true;
    }
}

3. 合并有序链表

3.1 合并两个有序链表

LeetCode21

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummyHead = new ListNode();
        ListNode p1 = list1, p2 = list2, dummy = dummyHead;
        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                dummy.next = p1;
                dummy = dummy.next;
                p1 = p1.next;
            } else {
                dummy.next = p2;
                dummy = dummy.next;
                p2 = p2.next;
            }
        }
        if (p1 == null) {
            dummy.next = p2;
        } else {
            dummy.next = p1;
        }
        return dummyHead.next;
    }
}

3.2 合并K个链表

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode dummy = null;
        for (ListNode node : lists) {
            dummy = merge2List(dummy, node);
        }
        return dummy;
    }

    public ListNode merge2List(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode();
        ListNode p = dummy, p1 = list1, p2 = list2;

        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }

        if (p1 == null) {
            p.next = p2;
        } else {
            p.next = p1;
        }
        return dummy.next;
    }
}

3.3 无聊的好题

LeetCode1669

class Solution {
    public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
        ListNode p = list1, aBefore, bAfter;
        for (int i = 0; i < a - 1; i++){
            p = p.next;
        }
        aBefore = p;
        for (int i = 0; i< b - a + 2; i++) {
            p = p.next;
        }
        bAfter = p;
        
        aBefore.next = list2;
        p = list2;
        while (p.next != null) {
            p = p.next;
        }
        p.next = bAfter;

        return list1;
    }
}

4. 双指针专题

4.1 寻找中间结点

LeetCode876

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode slow = head, fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

4.2寻找倒数第K个元素

LeetCode22

这里也可以使用快慢双指针,我们先将fast 向后遍历到第 k+1 个节点, slow仍然指向链表的第一个节点,此时指针fast 与slow 二者之间刚好间隔 k 个节点。之后两个指针同步向后走,当 fast 走到链表的尾部空节点时,slow 指针刚好指向链表的倒数第k个节点。
这里需要特别注意的是链表的长度可能小于k,寻找k位置的时候必须判断fast是否为null,这是本题的关键问题之一


4.3 删除指定节点

Leetcode203

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode(-1, head);
        ListNode p = dummy;
        while (p.next != null) {
            if (p.next.val == val) {
                p.next = p.next.next;
            } else {
                p = p.next;
            }
            
        }
        return dummy.next;
    }
}

4.4 重复元素都不要

LeetCode82

public ListNode deleteDuplicates(ListNode head) {
    ListNode dummy = new ListNode(-1, head);
    ListNode prev = dummy;
    while (prev.next != null && prev.next.next != null) {
        if (prev.next.val == prev.next.next.val) {
            int val = prev.next.val;
            while (prev.next != null && prev.next.val == val) {
                prev.next = prev.next.next;
            }
        } else {
            prev = prev.next;
        }
    }

    return dummy.next;

}

你可能感兴趣的:(算法,链表,笔记)