【剑指Offer】链表专项总结

链表的基本实现

C++实现

#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);
    }

};

Python单向链表实现

# LeetCode: 707

class ListNode:
    def __init__(self, val: int) -> None:
        self.val = val
        self.next = None

class Chain:

    def __init__(self):
        self.head = None
        self.size = 0

    def get(self, index: int) -> int:
        if index>=self.size: return -1
        cur = self.head
        for _ in range(index):
            cur = cur.next
        return cur.val

    def addAtHead(self, val: int) -> None:
        newHead = ListNode(val)
        newHead.next = self.head
        self.head = newHead
        self.size+=1

    def addAtTail(self, val: int) -> None:
        if not self.head:self.head = ListNode(val)
        else:
            cur = self.head
            while cur and cur.next:
                cur = cur.next
            cur.next = ListNode(val)
        self.size+=1
    def addAtIndex(self, index: int, val: int) -> None:
        if index<=0: self.addAtHead(val)
        elif index==self.size:self.addAtTail(val)
        elif index < self.size:
            cur = self.head
            for _ in range(index-1):
                cur = cur.next
            node = ListNode(val)
            node.next=cur.next
            cur.next=node 
            self.size+=1

    def deleteAtIndex(self, index: int) -> None:
        if index<0 or index>=self.size: return
        if index==0: self.head = self.head.next
        else:
            cur = self.head
            for _ in range(index-1):
                cur = cur.next
            cur.next = cur.next.next
        self.size -= 1
        
    def print(self):
        print(F"size: {self.size}")
        cur = self.head
        while cur:
            print(cur.val, end=" ")
            cur = cur.next

双指针技巧

判断链表中是否有环

题目

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

思路

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

代码

# 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)

查找链表中环的起点

题目

环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -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)

相交链表

题目

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

listA 中节点数目为 m
listB 中节点数目为 n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 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 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:

输入:head = [1], n = 1
输出:[]
示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

思路

  • 使用快慢指针,快指针先向前偏移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)

经典问题

反转链表

题目

反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:

输入:head = [1,2]
输出:[2,1]
示例 3:

输入:head = []
输出:[]

提示:

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

题解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)

奇偶链表

题目

奇偶链表
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

示例 1:

输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
示例 2:

输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]

提示:

n == 链表中的节点数
0 <= n <= 104
-106 <= Node.val <= 106

思路

在这里插入图片描述

  • 如上图所示,将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)

回文链表

题目

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

输入:head = [1,2,2,1]
输出:true
示例 2:

输入:head = [1,2]
输出:false

提示:

链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 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)

应用

合并两个有序链表

题目

合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:

输入:l1 = [], l2 = []
输出:[]
示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

思路

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

代码

# 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)

两数相加

题目

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零

思路

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

代码

# 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)

扁平化多级双向链表

题目

你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的 多层数据结构 。

给定链表的头节点 head ,将链表 扁平化 ,以便所有节点都出现在单层双链表中。让 curr 是一个带有子列表的节点。子列表中的节点应该出现在扁平化列表中的 curr 之后 和 curr.next 之前 。

返回 扁平列表的 head 。列表中的节点必须将其 所有 子指针设置为 null 。

示例 1:

输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
输出:[1,2,3,7,8,11,12,9,10,4,5,6]
解释:输入的多级列表如上图所示。
扁平化后的链表如下图:

示例 2:

输入:head = [1,2,null,3]
输出:[1,3,2]
解释:输入的多级列表如上图所示。
扁平化后的链表如下图:

示例 3:

输入:head = []
输出:[]
说明:输入中可能存在空列表。

提示:

节点数目不超过 1000
1 <= Node.val <= 105

如何表示测试用例中的多级链表?

以 示例 1 为例:

1—2—3—4—5—6–NULL
|
7—8—9—10–NULL
|
11–12–NULL
序列化其中的每一级之后:

[1,2,3,4,5,6,null]
[7,8,9,10,null]
[11,12,null]
为了将每一级都序列化到一起,我们需要每一级中添加值为 null 的元素,以表示没有节点连接到上一级的上级节点。

[1,2,3,4,5,6,null]
[null,null,7,8,9,10,null]
[null,11,12,null]
合并所有序列化结果,并去除末尾的 null 。

[1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]

思路

  • 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)

复制带随机指针的链表

题目

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

0 <= n <= 1000
-104 <= Node.val <= 104
Node.random 为 null 或指向链表中的节点。

题解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)

旋转链表

题目

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:

输入:head = [0,1,2], k = 4
输出:[2,0,1]

提示:

链表中节点的数目在范围 [0, 500] 内
-100 <= Node.val <= 100
0 <= k <= 2 * 109

思路

  • 将链表首尾相连
  • 旋转 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pass night

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值