0
点赞
收藏
分享

微信扫一扫

【剑指Offer】链表专项总结

code_balance 2022-04-14 阅读 39

链表的基本实现

#include<assert.h>
typedef int Rank;

template<typename T> struct ListNode{
    T data;
    ListNode<T> *pre;
    ListNode<T> *succ;
    ListNode(T const& data, ListNode<T> *pre, ListNode<T> *succ)
        : data(data), pre(pre), succ(succ) {}
    ListNode(){}
    ListNode<T>* insertAsPre(T const& e){
        return pre = new ListNode<T>(e, this, pre);
    }
    ListNode<T>* insertAsSucc(T const& e){
        return succ = new ListNode<T>(e, this, succ);
    }
};

template <typename T> class LinkedList{
private:
    ListNode<T> *header;
    ListNode<T> *trailer;
    int _size;
protected:
    void init(){
        header = new ListNode<T>();
        trailer = new ListNode<T>();
        header->succ = trailer;
        header->pre = NULL;
        trailer->pre = header;
        trailer->succ = NULL;
        _size = 0;
    }
    int clear(){
        int oldSize = _size;
        while(_size > 0)
            remove(header->succ);
        return oldSize;
    }
    void copyNodes(ListNode<T> *p, int n){
        assert(n >= 0 && valid(p));
        init();
        while(n--){
            insertAsLast(p->data);
            p = p->succ;
        }
    }
    void merge(LinkedList<T> left, int m, LinkedList<T> right, int n){
        init();
        int i = 0, j = 0;
        while(i < m && j < n){
            if(left.get(i) < right.get(j))
                insertAsLast(left.get(i++));
            else
                insertAsLast(right.get(j++));
        }
        while(i < m)
            insertAsLast(left.get(i++));
        while(j < n)
            insertAsLast(right.get(j++));
    }

    void mergeSort(LinkedList<T>& l, int n){
        if(n < 2)
            return;
        LinkedList<T> left, right;
        int m = n >> 1;
        left.copyNodes(l.header->succ, m);
        right.copyNodes(l.get(m), n - m);
        mergeSort(left, m);
        mergeSort(right, n - m);
        merge(left, m, right, n - m);
    }
    void selectionSort(LinkedList<T>& l, int n){
        for(int i = 0; i < n; i++){
            int min = i;
            for(int j = i + 1; j < n; j++)
                if(l.get(j) < l.get(min))
                    min = j;
            l.swap(i, min);
        }
    }
    void insertionSort(LinkedList<T>& l, int n){
        assert(valide(p) && n >= 0);
        for (Rank r = 0; r < n; r++){
            insertAsSucc(search(p->data, r,p), p->data);
            p = p->succ;
            remove(p->pre);
        }
    }


public:
    LinkedList()
    {
        init();
    }
    LinkedList(LinkedList<T> const *right, int n)
    {
        copyNodes(left, n);
    }
    LinkedList(LinkedList<T> const *right, Rank r, int n){
        copyNodes(right[r], n);
    }
    LinkedList(ListNode<T>* p, int n){
        copyNodes(p, n);
    }

    ~LinkedList()
    {
        clear();
        delete header;
        delete trailer;
    }

    // read only interface
    Rank size() const{
        return _size;
    }
    bool isEmpty() const{
        return !_size;
    }
    T& operator[](Rank r) const{
        return get(r);
    }
    T& get(Rank r) const{
        assert(0 <= r && r < _size);
        ListNode<T> *p = header;
        while(r--)
            p = p->succ;
        return p->data;
    }
    ListNode<T> first() const{
        return header->succ;
    }
    ListNode<T> last() const{
        return trailer->pre;
    }
    bool valid(ListNode<T> const* p) const{
        return p && (p != trailer) && (p != header);
    }
    bool isAssending() const{
        ListNode<T> *p = header->succ;
        while(p->succ != trailer){
            if(p->data > p->succ->data)
                return false;
            p = p->succ;
        }
        return true;
    }
    ListNode<T>* find(T const& e, int n, ListNode<T>* p) const{
        assert(0 <= n && n <= _size);
        while(n--){
            if(p->data == e)
                return p;
            p = p->succ;
        }
        return nullptr;
    }
    ListNode<T>* find(T const& e){
        return find(e, _size, header->succ);
    }
    ListNode<T>* search(T const& e, int n, ListNode<T>* p) const{
        assert(0 <= n && n <= _size);
        while(n-- > 0)
            if(((p=p->pre)->data) <= e)
                break;
        return p; //use validate to check if success
    }
    ListNode<T>* search(T const& e){
        return search(e, _size, header->succ);
    }

    ListNode<T>* max(ListNode<T>* p, int n) const{
        ListNode<T> *max = p;
        while(n--){
            if(max->data < p->data)
                max = p;
            p = p->succ;
        }
        return max;
    }
    ListNode<T>* max(){
        return max(header->succ, _size);
    }

    // writable interface

    ListNode<T>* insertAsFirst(T const& e){
        return header->insertAsSucc(e);
    }
    ListNode<T>* insertAsLast(T const& e){
        return trailer->insertAsPre(e);
    }
    ListNode<T>* insertAsPre(T const& e, ListNode<T>* p){
        ListNode<T> *x = new ListNode(e, p->pre, p);
        p->pre->succ = x;
        p->pre = x;
        _size++;
        return x;
    }
    ListNode<T>* insertAsSucc(T const& e, ListNode<T>* p){
        ListNode<T>* x = new ListNode(e, p, p->succ);
        p->succ->pre = x;
        p->succ = x;
        _size++;
        return x;
    }
    T remove(ListNode<T>* p){
        T e = p->data;
        p->pre->succ = p->succ;
        p->succ->pre = p->pre;
        delete p;
        _size--;
        return e;
    }
    void merge(LinkedList<T>& right){
        merge(*this, _size, right, right._size);
    }

    void sort(ListNode<T>* p, int n){
        
    }
    void sort(){
        sort(first(),_size)
    }

    int deduplicate(){
        if(_size < 2)
            return 0;
        int oldSize = _size;
        ListNode<T> *p = header->succ;
        Rank r = 0;
        while(p != trailer){
            ListNode<T> *q = find(p->data, r, p);
            q ? remove(q) : r++;
        }
        return oldSize - _size;
    }
    int uniquify(){
        if(_size < 2)
            return 0;
        int oldSize = _size;
        ListNode<T> *p, *q;
        for(p=header, q=p->succ; q != trailer; p=q, q=p->succ){
            if(p->data == q->data)
                remove(q);
        }
        return oldSize - _size;
        
    }
    void reverse(){}

    // traverse
    void traverse(void (*visit)(T&)){
        for(ListNode<T> *p = header->succ; p != trailer; p = p->succ)
            visit(p->data);
    }
    template<typename VST> void traverse(VST& visit){
        for(ListNode<T> *p = header->succ; p != trailer; p = p->succ)
            visit(p->data);
    }

};

双指针技巧

判断链表中是否有环

题目

思路

  • 快慢指针遍历链表,若快指针能追上慢指针一圈则说明有环,否则无环

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head or not head.next: return False
        slow,fast = head,head
        while fast and fast.next:
            slow,fast = slow.next, fast.next.next
            if slow is fast: return True
        return False

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

查找链表中环的起点

题目

思路

  • 使用快慢指针
  • 当慢指针进入环中,快指针已经进入环中,又因为快指针速度是慢指针两倍,所以快指针必定在一圈内追上慢指针
  • 假设起点到环的起点距离为 a a a,环的起点到相遇点距离为 b b b,相遇点到终点距离为 c c c
  • 慢指针走过的路程为 a + b a+b a+b,快指针走过的路程为 a + n ( b + c ) + b a+n(b+c)+b a+n(b+c)+b,又因为快指针速度为慢指针两倍,所以有 a + n ( b + c ) + b = 2 ( a + b ) a+n(b+c)+b = 2(a+b) a+n(b+c)+b=2(a+b),推出 a = n ( b + c ) − b a = n(b+c)-b a=n(b+c)b
  • 观察上式可知当慢指针再走 b + n ( b + c ) b+n(b+c) b+n(b+c)时,可以回到环的起点,而同样时长也可以从起点走到环起点
  • 所以取链首,每次向前一格,慢指针每次向前一格,当相遇时则说明在环首

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head or not head.next: return None
        slow,fast = head,head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                cur = head
                while cur != slow:
                    cur = cur.next
                    slow = slow.next
                return cur
        return None

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

相交链表

题目

思路

  • 分别遍历两条链表,获取两条链表的长度
  • 长链表向前便宜长度差
  • 两链表同时向前,交点即为所求

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        a,b = headA,headB
        lenA,lenB = 0,0
        while a:
            lenA+=1
            a= a.next
        while b:
            lenB +=1
            b = b.next
        a,b = headA,headB
        if lenA>lenB:
            for i in range(lenA-lenB):
                a=a.next
        else:
            for i in range(lenB-lenA):
                b = b.next
        while a!=b and a:
            a=a.next
            b=b.next
        return a

复杂度

  • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
  • 空间复杂度: O ( 1 ) O(1) O(1)

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

题目

思路

  • 使用快慢指针,快指针先向前偏移N个结点;快慢指针同时前进,当快指针到达链尾时,慢指针恰好到达要删除的节点的前一个节点
  • 若快指针直接到达链尾后,则直接删除头结点

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        slow,fast = head,head
        if not head.next: return None
        for _ in range(n): fast = fast.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next
        if not fast: return head.next
        slow.next = slow.next.next
        return head

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

经典问题

反转链表

题目

题解1

思路

  • 使用一个变量保存反转后的链表头,然后每一次将当前结点指向该头结点,然后两个节点顺势下移

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        cur = head
        while cur:
            t = cur.next
            cur.next = prev
            prev = cur
            cur = t
        return prev

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

题解2

思路

在这里插入图片描述

  • 如图所示,反转子链表后,需将子链表的尾部(head.next.next)接到当前头部,然后再将当前头部和子链表尾部断开 否则成环

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next: return head
        reverse = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return reverse

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

奇偶链表

题目

思路

在这里插入图片描述

  • 如上图所示,将odd,even分别指向奇节点尾部、偶节点尾部
  • 然后将偶节点尾部的下一个结点插入到奇节点尾部
  • 再将两个节点下移一格

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head or not head.next: return head
        odd,even = head,head.next
        while odd and even and odd.next and even.next:
            t = even.next.next
            even.next.next = odd.next
            odd.next = even.next
            even.next = t
            odd,even = odd.next,even.next
        return head

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

回文链表

题目

思路

  • 将链表的第 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n个结点作为头翻转,然后比对两个子链是否相等
  • ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n个结点可以使用快慢指针的方式找到,而翻转链表见 反转链表

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head.next: return True
        slow,fast = head,head
        while fast and fast.next: slow,fast = slow.next,fast.next.next
        if fast: slow = slow.next
        mid = slow
        def reverse(head: ListNode):
            prev = None
            cur = head
            while cur:
                t = cur.next
                cur.next = prev
                prev = cur
                cur = t
            return prev
        mid = reverse(mid)
        left,right = head,mid
        while right:
            if left.val != right.val: return False
            left,right = left.next,right.next
        return True

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

应用

合并两个有序链表

题目

思路

  • 选举头结点较小的作为初始链,设置两个指针分别指向两条链的当前位置
  • 将较小的节点插入主链后方,若主链当前位置为较小则直接向前移动

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1 or not l2:
            return l1 if l1 else l2
        if l1.val>l2.val: l1,l2 = l2,l1
        head,i,j = l1,l1,l2
        while i.next and j:
            if i.next.val > j.val:
                temp = j.next
                j.next = i.next
                i.next = j
                j = temp
            i=i.next
        if j:
            i.next = j
        return head

复杂度

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

两数相加

题目

思路

  • 从左往右遍历两条链表,将链表两个节点和进位的和添加到结果链表当中,并将记录进位

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        overflow = 0
        if(l1 or l2):
            x = l1.val + l2.val + overflow
            head = cur = ListNode(x if x < 10 else x % 10)
            # print(cur.val)

            overflow = x // 10
            l1 = l1.next
            l2 = l2.next
        while(l1 or l2):
            a = l1.val if l1 else 0
            b = l2.val if l2 else 0
            x = a + b + overflow
            overflow = x // 10

            cur.next = ListNode(x if x < 10 else x % 10)

            # print(cur.val)
            cur = cur.next
            if l1: l1 = l1.next
            if l2: l2 = l2.next
        # while head:
        #     print(head.val)
        #     head = head.next
        if overflow > 0:
            cur.next = ListNode(overflow)
        return head

复杂度

  • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
  • 空间复杂度: O ( 1 ) O(1) O(1)

扁平化多级双向链表

题目

思路

  • dfs链表,将回溯到的值保存在数组当中
  • 将数组转化为链表

代码

"""
# Definition for a Node.
class Node:
    def __init__(self, val, prev, next, child):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
"""
from collections import deque
class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head: return head
        res = []
        def dfs(head: 'Node'):
            if not head: return
            res.append(head.val)
            if head.child: dfs(head.child)
            if head.next: dfs(head.next)
        dfs(head)
        head = Node(res[0])
        cur = head
        for n in res[1:]:
            node = Node(n)
            cur.next = node
            node.prev = cur
            cur = cur.next
        return head

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

复制带随机指针的链表

题目

题解1

思路

  • 使用map保存旧节点和新节点之间的相互引用
  • 当旧节点指向空的时候,从map中取值的时候需要判空

代码

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        if not head: return None
        old,newHead = head.next,Node(head.val)
        nodeMap = {head: newHead}
        new = newHead
        while old:
            node = Node(old.val)
            new.next = node
            nodeMap[old] = node
            old,new = old.next,new.next
        old,new = head,newHead
        while old:
            new.random = nodeMap.get(old.random)
            old,new = old.next,new.next
        return newHead

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

题解2

思路

  • 复制过程,将新节点插到旧节点之后
  • 复制完成之后将偶数节点全部取出

代码

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        if not head: return None
        cur = head
        while cur:
            newNode = Node(cur.val)
            newNode.next = cur.next
            cur.next = newNode
            cur = cur.next.next
        cur = head
        while cur:
            cur.next.random = cur.random.next if cur.random else None
            cur = cur.next.next

        head = head.next
        cur = head
        while cur and cur.next:
            cur.next = cur.next.next
            cur = cur.next
        return head

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

旋转链表

题目

思路

  • 将链表首尾相连
  • 旋转 k k k次相当于将链表的第 k m o d    n k \mod n kmodn个节点断开

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if not head: return None
        cur = head
        length = 0
        while cur.next:
            cur = cur.next
            length += 1
        length += 1
        cur.next = head

        for _ in range(length - (k % length) - 1): head = head.next
        newHead = head.next
        head.next = None
        return newHead

复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

引用

题目源

举报

相关推荐

0 条评论