1.找出两个链表的交点

160.intersection-of-two-linked-lists
Leetcode

找两个链表的相交节点,简单的很,直接暴力解法,双重遍历就完事了。
时间复杂度O(n^2),这种解法属实憨逼,面试要是上来就这个解法你就已经结束了。

//2020年8月17日
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }
        ListNode a = headA;
        ListNode b = headB;
        while(headA!=null){
            while(headB!=null){
                if(headA == headB){
                    return headA;
                }
                headB = headB.next;
            }
            headA = headA.next;
            headB = b;
        }
        return null;
    }
}

经过一番思考后,有了如下代码。
假设两个链表有相交的话,那么定有广义上的一个长链表,一个短链表,将指针置为两个链表的头部,当短链表遍历完,长链表没遍历完的时候,指向短链表的指针重新指向长链表的头部,如此往复循环。
这个是个什么原理呢?
我们将下图中(1 2节点段)称为A,(3 4 5节点段)称为 C, (8 6 7节点段)称为B,那么就有
A + C + B = B + C + A
也就是上文提到的短链表遍历完从另一个链表头开始遍历的原因,这么下去,总有一天,两个指针会相遇的。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode a = headA,b=headB;
        while(a != b){
            a = (a == null) ? headB : a.next;
            b = (b == null) ? headA : b.next;
        }
        return a;
    }
}

图片说明

2.删除排序链表中的重复元素

83.remove-duplicates-from-sorted-list
Leetcode

看到这题的第一想法还是遍历,可能这就是笨比吧,下面解法的大致思路如下:
设置两个指针,一个旗标:
a指针指向head节点,b指针为a指针的next节点,如果b和a指针指向的节点的值相同,则a.next = b.next
否则a指针继续往前走。

//2020年8月18日
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode a = head;
        int flag = 0;
        while(a!=null){
            ListNode b = a.next;
            if(b == null) flag=0;
            while(b!=null){
                if(a.val == b.val){
                    a.next = b.next;
                    flag = 1;
                }else{
                    flag = 0;
                }
                break;
            }
            if(flag != 1){
                a = a.next;
            }
        }
        return head;
    }
}

由于这个写法实在是太笨了,看了下别人的解法:
首先将问题简化,当链表为null或[1]的时候,直接返回head
当链表长度为2及以上的时候,我们就要获取当前节点的next节点与当前节点进行比较,相等的话就取后面的节点。

写的好乱,md

public ListNode deleteDuplicates(ListNode head) {
    if (head == null || head.next == null) return head;
    head.next = deleteDuplicates(head.next);
    return head.val == head.next.val ? head.next : head;
}

3.删除链表的倒数第N个节点

19.remove-nth-node-from-end-of-list
Leetcode

我一开始想的是快慢指针的方法,一个快指针先走n步,然后两个指针再同时走,直到快指针的next为空;
这样慢指针的next就是我们要删除的节点,这样做确实可以通过一些样例,但是遇到就一个节点或者[1,2,3,4] 删除倒数第4个节点这样的,这个方法就不行了。

经过一番思考和搜索,我找到了一个设置预先指针的方法,预先指针p的next指向head节点,快指针与慢指针同时从预先节点出发,最后返回p.next,这样就避免了要删除的节点就是慢指针指向的那个节点的情况。

//2020年8月19日
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode c = new ListNode(-1);
        c.next = head;
        ListNode a = c,b = c;
        while(n != 0){
            b = b.next;
            n--;
        }
        while(b.next != null){
            a = a.next;
            b = b.next;
        }
        a.next = a.next.next;
        return c.next;
    }
}

4.两两交换链表中的节点

24.swap-nodes-in-pairs
Leetcode

//2020年8月20日
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null) return head;
        ListNode preNode = new ListNode(-1);
        preNode.next = head;
        ListNode c = preNode;

        while(preNode.next != null && preNode.next.next != null){
            ListNode a = preNode.next;
            ListNode b = a.next;
            preNode.next = b;
            a.next = b.next;
            b.next = a;
            preNode = preNode.next.next;
        }
        return c.next;
    }
}

5.链表求和

445.add-two-numbers-ii
Leetcode

//2020年8月28日
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Stack<Integer> s1 = new Stack();
        Stack<Integer> s2 = new Stack();

        Stack<Integer> res = new Stack();
        ListNode p1 = l1,p2 = l2;
        ListNode head = new ListNode(-1);
        int mul = 0;
        while(p1!=null){
            s1.push(p1.val);
            p1 = p1.next;
        }
        while(p2!=null){
            s2.push(p2.val);
            p2 = p2.next;
        }
        while(!s1.isEmpty() || !s2.isEmpty() || mul!=0){
            int x = s1.isEmpty() ? 0 : s1.pop();
            int y = s2.isEmpty() ? 0 : s2.pop();
            int sum = x + y + mul;
            ListNode node = new ListNode(sum % 10);
            node.next = head.next;
            head.next = node;
            mul = sum / 10;
        }
        return head.next;
    }
}

6.回文链表

  1. Palindrome Linked List
    Leetcode

不知道为啥,我总是能想出来这种拉跨的解法。
将链表中的值放到数组中,然后在数组的首尾设置指针,比较两个指针的值是否相等

但题中提出要求说:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
说实话,O(1)空间复杂度我觉着是可以做到的,大概也是用两个指针这样的,但是具体思路嘛....
参考了cyc的思路,解法应该是这样的:将链表对半切开,将后边一半反转,依次比较是否相等
这里就涉及到了三个操作,切开,反转,比较相等
把这个解放到后面了

//2020年9月1日
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        ListNode a = head;
        ArrayList<Integer> arr = new ArrayList();
        while(a != null){
            arr.add(a.val);
            a = a.next;
        }
        Object[] newArr = arr.toArray();
        int len = newArr.length;
        for(int i=0,j=len-1;i<j;i++,j--){
            if((int)newArr[i] == (int)newArr[j]){
            }else{
                return false;
            }
        }
        return true;
    }
}
public boolean isPalindrome(ListNode head) {
    if (head == null || head.next == null) return true;
    ListNode slow = head, fast = head.next;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    if (fast != null) slow = slow.next;  // 偶数节点,让 slow 指向下一个节点
    cut(head, slow);                     // 切成两个链表
    return isEqual(head, reverse(slow));
}
//切开
private void cut(ListNode head, ListNode cutNode) {
    while (head.next != cutNode) {
        head = head.next;
    }
    head.next = null;
}
//反转
private ListNode reverse(ListNode head) {
    ListNode newHead = null;
    while (head != null) {
        ListNode nextNode = head.next;
        head.next = newHead;
        newHead = head;
        head = nextNode;
    }
    return newHead;
}
//相等否
private boolean isEqual(ListNode l1, ListNode l2) {
    while (l1 != null && l2 != null) {
        if (l1.val != l2.val) return false;
        l1 = l1.next;
        l2 = l2.next;
    }
    return true;
}

7.分隔链表

  1. Split Linked List in Parts
    Leetcode

今天就先不写解析了,看剧了,半泽直树2第七集

//2020年9月2日
class Solution {
    public ListNode[] splitListToParts(ListNode root, int k) {
        int len = 0;
        ListNode countForList = root;
        while(countForList != null){
            len++;
            countForList = countForList.next;
        }
        int group = len / k;
        int mod = len % k;
        ListNode[] result = new ListNode[k];
        ListNode now = root;
        for(int i=0;now!=null && i<k;i++){
            result[i] = now;
            int curSize = group + (mod-- > 0 ? 1 : 0);
            for(int j = 0; j < curSize - 1;j++){
                now = now.next;
            }
            ListNode next = now.next;
            now.next = null;
            now = next;
        }
        return result;
    }
}

8.链表元素按奇偶聚集

  1. Odd Even Linked List (Medium)
    Leetcode

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

在不使用额外空间的情况下,使用双指针法构造两个链表(一个奇链表,一个偶链表),最后再将两个链表合并到一起。
大致的思路如下图:
图片说明
最后再将奇链表和偶链表连接起来
odd.next = evenHead;

//2020年9月8日
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null) return null;
        ListNode odd = head,even = head.next,evenHead = even;
        while(even != null && even.next != null){
            odd.next = odd.next.next;
            odd = odd.next;
            even.next = even.next.next;
            even = even.next;
        }
        odd.next = evenHead;
        return head;
    }
}