本文主要记录链表的相关LeetCode题目的实现代码,直接给出本文各个题目的答案,供有需求的读者学习或复制。

一、设计链表

707. 设计链表(中等难度)

class MyLinkedList {
public:
    // 定义链表节点结构体
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };
private:
    int _size;// 元素个数
    LinkedNode* _dummyHead;// 虚拟头结点
public:
    MyLinkedList() {
        _dummyHead = new LinkedNode(0); 
        _size = 0;
    }
    int get(int index) {
        if(index>(_size-1)||index<0) return -1;
        // 查找第index个节点
        LinkedNode* cur = _dummyHead->next;
        while(index--)cur = cur->next;
        return cur->val;
    }
    void addAtHead(int val) {
        //在头部添加新节点
        LinkedNode* newNode = new LinkedNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }
    void addAtTail(int val) {
        //在尾部添加新节点
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while(cur->next!=nullptr)cur = cur->next;
        cur->next = newNode;
        _size++;
    }
    void addAtIndex(int index, int val) {
        if (index>_size) return;
        LinkedNode* newNode = new LinkedNode(val);
        // 找到第index个节点
        LinkedNode* cur = _dummyHead;
        while(index--) cur = cur->next;
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }
    void deleteAtIndex(int index) {
        if (index>=_size||index<0) return;
        // 找到第index个节点
        LinkedNode* cur = _dummyHead;
        while(index--) cur = cur->next;
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        _size--;
    }
};

382. 链表随机节点(中等难度)

class Solution {
    vector<int> arr;// 将所有节点添加进一个数组中
public:
    Solution(ListNode *head) {
        while (head) {
            arr.emplace_back(head->val);//高效添加
            head = head->next;
        }
    }
    int getRandom() {
        return arr[rand()%arr.size()];
    }
};

二、链表删除

1、删除链表中的特定值

203. 移除链表元素(简单难度)

剑指 Offer 18. 删除链表的节点(简单难度)

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //新建虚拟头结点
        ListNode * myHead = new ListNode(0);
        myHead->next=head;
        //双指针
        ListNode* prev=myHead;//慢指针负责删除节点
        ListNode* cur=prev->next;//当前节点负责确定是否需要删除
        while (cur) {
            // 如果满足条件则执行删除操作;否则慢指针前进一步
            if(cur->val == val) {
                prev->next = cur->next;//删除快指针
            } else {
                prev = cur;//慢指针前进一步
            }
            cur = cur->next;//快指针前进一步
        }
        return myHead->next;
    }
};

83. 删除排序链表中的重复元素(简单难度)

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        // 新建虚拟头结点
        ListNode * myHead=new ListNode(-101);
        myHead->next=head;
        // 快慢指针扫描单链表
        ListNode * prev=myHead;//负责删除
        ListNode * cur=head; // 负责遍历和确定
        while(cur!=nullptr){
            if(cur->val==prev->val){
                prev->next=cur->next;
            }
            else{
                prev=prev->next;
            }
            cur=cur->next;
        }
        return myHead->next;
    }
};

2、只给出待删除元素,实现删除效果

237. 删除链表中的节点(简单难度)

面试题 02.03. 删除中间节点(简单难度)

class Solution {
public:
    void deleteNode(ListNode* node) {
        // node是要删除的节点,将其伪装成下一个节点son
        ListNode* son=node->next;
        node->val=son->val;//伪装其val
        node->next=son->next;//伪装其next
    }
};

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

19. 删除链表的倒数第 N 个结点(中等难度)

剑指 Offer 22. 链表中倒数第k个节点(简单难度)

剑指 Offer II 021. 删除链表的倒数第 n 个结点(中等难度)

方式一:计数法

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        // 循环单链表获取len
        int len=0;
        ListNode* p=head;
        while(p){
            p=p->next;
            len++;
        }
        // 循环单链表找到第len-k个元素(倒数第k个==正数第len-k+1)
        int i=0;
        p=head;
        while(p&&i<len-k){
            p=p->next;
            i++;
        }
        return p;
    }
};

方式二:快慢指针法

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* fast=head;
        ListNode* slow=head;
        //先让fast走n步,此时fast可能不存在
        for(int i=0;i<n;i++){
            fast=fast->next;
        }
        //若fast不存在,说明n>链表的长度,则删除掉头结点,保留头结点之后的节点
        if(!fast){
            return head->next;
        }
        //若fast存在,同时遍历
        while(fast->next){
            slow=slow->next;
            fast=fast->next;
        }
        slow->next=slow->next->next;
        
        return head;
    }
};

2095. 删除链表的中间节点(中等难度)

快慢指针法:

class Solution {
public:
    ListNode* deleteMiddle(ListNode* head) {
        ListNode* p=head;
        int len=0;
        while(p){
            p=p->next;
            len++;
        }
        if(len==1) return nullptr;
        if(len==2) {
            head->next=nullptr;
            return head;
        }
        int i=0;
        p=head;
        while(p&&i<len/2-1){
            p=p->next;
            i++;
        }
        p->next=p->next->next;
        return head;
    }
};

4、删除链表中的一段特殊序列

82. 删除排序链表中的重复元素 II(中等难度)

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        // 新建虚拟头结点
        ListNode * myHead=new ListNode(-101);
        myHead->next=head;
        // 快慢指针扫描单链表删除重复元素
        ListNode* p=myHead;//遍历指针,负责遍历
        ListNode * left,*right;//快慢指针,
        while(p->next!=nullptr){
            left=p->next;//保留起始指针
            right=left; //一开始快慢指针相同,比p多一步
            // 尝试移动right,如果right->next存在且和right相同就移动一步,最终right将指向最后一个相同的数
            while(right->next && right->next->val==right->val)
                right=right->next;
            // 如果left==right,说明right没有移动,即没有相同值,p正常移动
            if(left == right) p=p->next;
            // 如果left!=right,说明right有移动,left和right所指的都是相同值
            else p->next=right->next;
        }
        return myHead->next;
    }
};

1171. 从链表中删去总和值为零的连续节点(中等难度)

class Solution {
public:
    ListNode* removeZeroSumSublists(ListNode* head) {
        // 新建虚拟头结点
        ListNode* myHead=new ListNode(0);
        myHead->next=head;
        // 遍历
        ListNode*p=myHead;
        ListNode* left,* right;
        while(p->next!=nullptr){
            left=p->next;
            right=left;
            // 尝试移动right,记录left到right的和作为标准
            int sum=left->val;//[left,right]的元素之和,
            while(right->next){
                // 如果sum为零则退出需要删除节点
                if(sum==0)  break;
                // 如果sum不为零则继续累加直至表尾
                else{
                    right = right->next;//right前进
                    sum += right->val;//right
                }
            }
            if(sum!=0) p=p->next;// 如果sum不为零,p正常移动
            else p->next=right->next; // 如果sum为零,p移动到right的右侧
        }
        return myHead->next;
    }
};

三、链表反转

1、反转整个链表

206. 反转链表(简单难度)

剑指 Offer 24. 反转链表(简单难度)

剑指 Offer II 024. 反转链表(简单难度)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* left=nullptr;
        ListNode* right=head;
        while(right!=nullptr){
            //保存
            ListNode* temp=right->next;
            //反转
            right->next=left;
            //更新
            left=right;
            right=temp;
        }
        return left;
    }
};

2、反转单链表的某个区间

92. 反转链表 II(中等难度)

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        // 新建虚拟头结点
        ListNode* myHead = new ListNode(0);
        myHead->next = head;
        // 慢指针先找到第left-1个节点
        ListNode* slow=myHead;
        int i=0;
        while(slow&&i<left-1){
            slow=slow->next;
            i++;
        }
        if(slow==nullptr) return myHead->next;//说明链表没有要反转的区间,直接返回
        // 慢指针和快指针一起反转[left,right]
        ListNode* rhead=slow->next;//第left个节点(反转序列的头结点)
        // 循环right-left次
        for(int i=left;i<right;i++){
            //保存
            ListNode* temp=rhead->next;//保存rhead的后1步
            rhead->next = temp->next;//rhead重新指向后2步
            //反转
            temp->next = slow->next;//rhead的后1步指向快指针
            //更新
            slow->next=temp;//慢指针指向rhead的后1步
        }
        // 更新头结点
        return myHead->next;
    }
};

3、判断是否是回文链表

234. 回文链表(简单难度)

剑指 Offer II 027. 回文链表(简单难度)

面试题 02.06. 回文链表(简单难度)

class Solution {
public:
    //反转链表
    ListNode* reverse(ListNode* head){
        ListNode* pre=NULL;
        ListNode* temp=NULL;
        ListNode* cur=head;
        while(cur){
            temp=cur->next;
            cur->next=pre;
            pre = cur;
            cur = temp;
        }
        head=pre;
        return head;
    }
    bool isPalindrome(ListNode* head) {
        // 特殊情况
        if (head == NULL || head->next == NULL) {
            return true;
        }
        ListNode* left=head;
        ListNode* right=head;
        //根据快慢指针找到中点,同时出发
        while(right->next&&right->next->next){
            right=right->next->next;//快指针走两步
            left=left->next;//慢指针走一步
        }
        //反转链表的后半部分
        right = reverse(left->next);//right指向后半部分头结点
        left  = head;//left指向前半部分头结点
        //比较链表的前后两部分
        while(right&&left){
            if(right->val!=left->val){
                return false;
            }
            right=right->next;
            left=left->next;
        }
        return true;
    }
};

四、链表相加

2. 两数相加(中等难度)

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 新建结果链表的虚拟头结点
        ListNode* myHead = new ListNode(0);
        // 同时遍历2个可能不等长的链表
        ListNode* cur = myHead;//遍历指针
        int sum = 0;
        bool carry = false; 
        while(l1 != NULL || l2 != NULL){
            sum = 0;
            // 获取链表1的值
            if(l1 != NULL){
                sum += l1->val;
                l1 = l1 -> next;
            }
            // 获取链表2的值
            if(l2 != NULL){
                sum += l2->val;
                l2 = l2 -> next;
            }
            // 如果需要进位则+1
            if(carry) sum++;
            // 为结果链表添加新的元素
            cur -> next = new ListNode(sum % 10);//第一元素
            cur = cur -> next;
            carry = sum >= 10 ? true: false;//发现需要进位
        }
        //如果最后还需要+1位,则需要继续添加一个新结点
        if(carry) cur -> next = new ListNode(1);
        return myHead -> next;
    }
};

445. 两数相加 II(中等难度)

剑指 Offer II 025. 链表中的两数相加(中等难度)

面试题 02.05. 链表求和(中等难度)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* slow=NULL;//慢指针
        ListNode* fast=head;//快指针
        while(fast!=NULL){
            //保存
            ListNode* temp = fast->next;
            //反转
            fast->next=slow;
            //更新
            slow=fast;
            fast=temp;
        }
        //更新头结点
        head=slow;
        return head;
    }
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head=new ListNode(-1);//结果链表的虚拟头结点
        ListNode* cur=head;//遍历指针
        l1=reverseList(l1);
        l2=reverseList(l2);
        // 同时遍历2个链表
        int sum=0;
        bool carry=false;//是否需要进位
        while(l1||l2){
            sum=0;
            if(l1!=nullptr){
                sum+=l1->val;
                l1=l1->next;
            }
            if(l2!=nullptr){
                sum+=l2->val;
                l2=l2->next;
            }
            if(carry) sum++;
            // 为结果链表添加一个新的结果
            cur->next=new ListNode(sum%10);
            cur=cur->next;
            carry = sum >= 10 ? true: false;//发现需要进位
        }
        if(carry)   cur->next=new ListNode(1);
        head->next = reverseList(head->next);
        return head->next;
    }
};

五、链表相交

1、找到2个单链表的相交点

160. 相交链表(简单难度)

面试题 02.07. 链表相交(简单难度)

剑指 Offer 52. 两个链表的第一个公共节点(简单难度)

剑指 Offer II 023. 两个链表的第一个重合节点(简单难度)

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *pA=headA;
        ListNode *pB=headB;
        // 获取两个单链表的长度
        int lenA=0;
        int lenB=0;
        while(pA){
            lenA++;
            pA=pA->next;
        }
        while(pB){
            lenB++;
            pB=pB->next;
        }
        // 让长链表的遍历指针先走
        pA=headA;
        pB=headB;
        if(lenA>lenB){
            int gap=lenA-lenB;
            while(gap--){
                pA=pA->next;
            }
        }
        else if(lenA<lenB){
            int gap=lenB-lenA;
            while(gap--){
                pB=pB->next;
            }
        }
        //长短链表同时开始走
        while(pA&&pB){
            if(pA==pB) return pA;
            pA=pA->next;
            pB=pB->next;
        }
        return NULL;
    }
};

2、环形链表

876. 链表的中间结点(简单难度)

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        // 方法一:快慢指针
        // ListNode* slow=head;
        // ListNode* fast=head;
        // while(slow&&fast&&fast->next){
        //     slow=slow->next;//2,3,4
        //     fast=fast->next->next;//3,5,null
        // }
        // return slow;
        // 方法二:计数法
        int len=0;
        ListNode* p=head;
        if(p==nullptr) return nullptr;
        while(p){
            p=p->next;
            len++;
        }
        // 
        int i=0;
        p=head;
        while(p&&i<len/2){
            p=p->next;
            i++;
        }
        return p;
    }
};

141. 环形链表(简单难度)

class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 快慢指针
        ListNode *fast=head;
        ListNode *slow=head;
        while(fast  && fast->next){
            slow=slow->next;
            fast=fast->next->next;
            if(slow==fast) return true; //指针相同说明相遇
        }
        return false;
    }
};

142. 环形链表 II(中等难度)

剑指 Offer II 022. 链表中环的入口节点(中等难度)

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        // 快指针每次走2步,慢指针每次走1步,相遇即有环
        while(fast  && fast->next ) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,说明有环
            if (slow == fast) {
                slow=head;
                // head和相遇点相同速度出发再次相遇的就是入口
                while(fast&& slow) {
                    // 快慢指针第一次相遇,说明此时为入口
                    if (slow == fast) return slow;;
                    slow = slow->next;
                    fast = fast->next;
                }
            }
        }
        return NULL;
    }
};

六、链表合并

1、合并有序链表

21. 合并两个有序链表(简单难度)

剑指 Offer 25. 合并两个排序的链表(简单难度)

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* result = new ListNode(-1); // 结果链表,默认第一个为虚拟头结点
        ListNode* p = result;// 遍历指针,该指针会变化
        // 同时遍历两2个链表
        while(list1&&list2){
            // 谁小将谁添加进结果链表
            if(list1->val<=list2->val){
                p->next=list1;
                list1=list1->next;
            }
            else{
                p->next=list2;
                list2=list2->next;
            }
            p=p->next;
        }
        // 此时有个链表有剩余,继续添加
        while(list1!=NULL){
            p->next=list1;
            list1=list1->next;
            p=p->next;
        }
        while(list2!=NULL){
            p->next=list2;
            list2=list2->next;
            p=p->next;
        }
        return result->next;
    }
};

2、拼接链表

1669. 合并两个链表(中等难度)

class Solution {
public:
    ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
        // 找到list1的第a-1个节点
        int i=0;
        ListNode* pa=list1;
        while(pa&&i<a-1){
            pa=pa->next;
            i++;
        }
        // 找到list1的第b个节点;
        i=0;
        ListNode* pb=list1;
        while(pb&&i<b){
            pb=pb->next;
            i++;
        }
        // 找到list2的最后一个节点
        ListNode* last=list2;
        while(last->next){
            last=last->next;
        }
        // 拼接两个链表
        pa->next=list2;
        last->next=pb->next;
        return list1;
    }
};

23. 合并K个升序链表(困难难度)

剑指 Offer II 078. 合并排序链表(困难难度)

class Solution {
public:
   // 合并2个有序链表
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* result = new ListNode(-1); // 结果链表,默认第一个为虚拟头结点
        ListNode* p = result;// 遍历指针,该指针会变化
        // 同时遍历两2个链表
        while(list1&&list2){
            // 谁小将谁添加进结果链表
            if(list1->val<=list2->val){
                p->next=list1;
                list1=list1->next;
            }
            else{
                p->next=list2;
                list2=list2->next;
            }
            p=p->next;
        }
        // 此时有个链表有剩余,继续添加
        while(list1!=NULL){
            p->next=list1;
            list1=list1->next;
            p=p->next;
        }
        while(list2!=NULL){
            p->next=list2;
            list2=list2->next;
            p=p->next;
        }
        return result->next;
    }
    // 合并k个有序链表
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *ans = nullptr;
        for (size_t i = 0; i < lists.size(); ++i) {
            ans = mergeTwoLists(ans, lists[i]);// 调用合并2个有序链表
        }
        return ans;
    }
};

七、循环链表

61. 旋转链表(中等难度)

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        // 空链表直接返回空
        if(head==nullptr) return nullptr;
        // 找到最后一个节点并统计len
        ListNode* p=head;
        int len=1;
        while(p->next){
            p=p->next;
            len++;
        }
        // 如果k==len,直接返回
        if(k==len){
            return head;
        } 
        // 如果k!=len,修改循环链表
        else{
            p->next=head;
        }
        // 找到第len-k个节点
        p=head;
        if(k>len) k=k%len;
        int i=len-k-1;
        while(i--){
            p=p->next;
        }
        ListNode* res=p->next;//新的头结点
        p->next=nullptr;//解除循环
        return res;
    }
};

剑指 Offer II 029. 排序的循环链表(中等难度)

​ 思路:如果链表为空,直接新建结点,改变指针指向后返回即可;如果链表不空:先找到循环链表的尾巴,即最大值结点,找到了尾巴相当于也找到了最小值结点,因为tail->next就是最小的;判断插入值是否比最小值小或者比最大值大,如果满足,就插入到tail后面即可;如果插入值介于最小和最大之间,循环查找插入位置,当某个结点值比插入值小且那个结点的后一个结点比插入结点大时,即可插入。

class Solution {
public:
    Node* insert(Node* head, int insertVal) {
        Node* newNode = new Node(insertVal);
        // 如果链表为空,直接新建节点,改变指针指向后返回
        if(head==NULL){
            newNode->next=newNode;
            return newNode;
        }
        // 找到循环链表中的最大值,设为循环链表的尾部
        Node* p=head->next;
        Node* tail=head;
        int max=head->val;
        while(p!=head){
            if(p->val>=max) {
                max=p->val;
                tail=p;
            }
            p=p->next;
        }
        // 如果插入值大于最小值或者小于最小值,插入tail之后即可
        if(insertVal>max||insertVal<tail->next->val){
            Node* temp =tail->next;//保存tail->next
            tail->next=newNode;
            newNode->next=temp;
        }
        else{
            // 如果插入值介于最小值和最大值之间,则遍历寻找插入位置
            while(p){
                if(p->val<=insertVal&&p->next->val>=insertVal){
                    Node* temp =p->next;//保存p->next
                    p->next=newNode;//p指向新节点
                    newNode->next=temp;//新节点指向p->next
                    break;
                }
                p=p->next;
            }
        }
        return head;
    }
};

八、分隔链表

86. 分隔链表(中等难度)

面试题 02.04. 分割链表(中等难度)

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode* small=new ListNode(0);
        ListNode* pSmall=small;
        ListNode* large=new ListNode(0);
        ListNode* pLarge=large;
        // 找出所有值比listNode小的
        ListNode* p=head;
        while(p){
            if(p->val<x) {
                pSmall->next=p;//添加一个新节点
                pSmall=pSmall->next;//指针前进一步
            }else{
                pLarge->next=p;
                pLarge=pLarge->next;
            }
            p=p->next;
        }
        pLarge->next=nullptr;//去除掉最后
        pSmall->next=large->next;//拼接
        return small->next;
    }
};

328. 奇偶链表(中等难度)

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        ListNode* front=new ListNode(0);//结果链表的前部分,奇数
        ListNode* curFront=front;
        ListNode* back=new ListNode(0);//结果链表的后部分,偶数
        ListNode* curBack=back;
        // 遍历整个链表
        ListNode* p=head;
        bool flag=true;
        while(p){
            if(flag){
                curFront->next=p;
                curFront=curFront->next;
                flag=false;
            }else{
                curBack->next=p;
                curBack=curBack->next;
                flag=true;
            }
            p=p->next;
        }
        // 将后链表的末尾去除
        curBack->next=nullptr;
        // 将前链表的末尾接上后链表
        curFront->next=back->next;
        return front->next;
    }
};

九、链表复制

138. 复制带随机指针的链表(中等难度)

剑指 Offer 35. 复杂链表的复制(中等难度)

class Solution {
public:
    /*难点:random可能指向未创建的节点*/
    Node* copyRandomList(Node* head) {
        if(head==NULL) return NULL;
        // 第一步:遍历原始链表,创建新节点并且存入哈希表中
        unordered_map<Node*,Node*> dict;//key:原有链表节点 value:新创建链表节点
        Node* cur=head;
        while(cur){
            dict[cur]=new Node(cur->val);//存入哈希表
            cur=cur->next;
        }
        // 第二步:遍历原始链表,修改哈希表中每个新节点的next指针和random指针
        cur=head;
        while(cur){
            Node* curNewNode=dict[cur];// 获取当前节点对应的新节点
            curNewNode->next=dict[cur->next];// 修改新节点的next指针
            curNewNode->random=dict[cur->random]; // 修改新节点的random指针
            // 循环前进
            cur=cur->next;
        }
        return dict[head];
    }
};

十、链表排序

1、链表中点+反转链表+合并链表

143. 重排链表(中等难度)

剑指 Offer II 026. 重排链表(中等难度)

class Solution {
public:
    // 获取中间节点
    ListNode* getMiddleNode(ListNode* head){
        ListNode* slow=head;
        ListNode* fast=head;
        while(fast->next&&fast->next->next){
            slow=slow->next;
            fast=fast->next->next;
        }
        return slow;
    }
    // 反转整个链表
    ListNode* reverseList(ListNode* head){
        ListNode* left=nullptr;
        ListNode* right=head;
        while(right){
            // 保存
            ListNode* temp=right->next;
            // 反转
            right->next=left;
            // 前进
            left=right;
            right=temp;
        }
        return left;
    }

     // 在l1链表中插入l2链表1,2  4,3  -》1,4,2  3
    void mergeList(ListNode* l1,ListNode* l2){
        ListNode* res=new ListNode(0);//结果链表的虚拟头结点
        ListNode* p=res;
        while(l1&&l2){
            p->next=l1;
            l1=l1->next;
            p->next->next=l2;
            l2=l2->next;
            p=p->next->next;
        }
        // l1和l2可能是不等长的,有一个会多出一个元素
        if(l1) p->next=l1;
        if(l2) p->next=l2;
        l1=res->next;
        // ListNode* p1;
        // ListNode* p2;
        // while(l1&&l2){
        //     // 保存
        //     p1 = l1->next;//2
        //     p2 = l2->next;//3
        //     // 连接
        //     l1->next = l2;//1指向4
        //     l2->next = p1;//4指向2
        //     // 移动指针
        //     l1 = p1;//l1指向2
        //     l2 = p2;//l2指向3
        // }
    }
    void reorderList(ListNode* head) {
        if(head==nullptr) return;
        // 寻找链表中点
        ListNode* mid = getMiddleNode(head);
        // 将链表分割为左右两部分
        ListNode* l1 = head;
        ListNode* l2 = mid->next;
        mid->next = nullptr;//将l1和l2分割开
        // 反转右半部分的链表
        l2 = reverseList(l2);
        // 将链表合并起来
        mergeList(l1, l2);
    }
};

2、调整相邻节点的顺序

24. 两两交换链表中的节点(中等难度)

方法一:

class Solution {
public:
    // 方法:分隔链表+合并链表:
    ListNode* swapPairs(ListNode* head) {
        // 分隔链表:遍历一遍将奇数节点和偶数节点分开存放
        ListNode* oddHead=new ListNode(0);//奇数元素所在链表
        ListNode* pOdd=oddHead;
        ListNode* evenHead=new ListNode(0);//偶数元素所在链表
        ListNode* pEven=evenHead;
        bool isOdd=true;
        while(head){
            if(isOdd){
                pOdd->next=head;
                pOdd=pOdd->next;
                isOdd=false;
            }else{
                pEven->next=head;
                pEven=pEven->next;
                isOdd=true;
            }
            head=head->next;
        }
        pOdd->next=nullptr;
        pEven->next=nullptr;
        // 合并链表
        ListNode* myHead=new ListNode(0);
        ListNode* p=myHead;
        pOdd=oddHead->next;
        pEven=evenHead->next;
        while(pEven&&pOdd){
            p->next=pEven;
            pEven=pEven->next;
            p->next->next=pOdd;
            pOdd=pOdd->next;
            p=p->next->next;
        }
        if(pOdd) p->next=pOdd;
        if(pEven) p->next=pEven;
        return myHead->next;
    }
};

方法二:指针法

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 新建虚拟头结点
        ListNode* myHead=new ListNode(0);
        myHead->next=head;
        // 遍历指针
        ListNode* cur=myHead;
        while(cur->next&&cur->next->next){
            // 新建指针
            ListNode* slow=cur->next;
            ListNode* fast=cur->next->next;
            // 改变指向
            cur->next=fast;//cur指向fast
            slow->next=fast->next;//slow指向fast的后一位
            fast->next=slow;//fast指向slow
            // cur前进一步
            cur=slow;
        }
        return myHead->next;
    }
};

3、插入排序

147. 对链表进行插入排序(中等难度)

class Solution {
public:
    // 从小到大排序,难点:插入算法的思想,搜索插入位置
    ListNode* insertionSortList(ListNode* head) {
        if(head==nullptr) return nullptr;
        // 新建虚拟头结点
        ListNode* myHead=new ListNode(0);
        myHead->next=head;
        // 双指针进行插入排序
        ListNode* lastSorted=head;//最后一个排序好的节点
        ListNode* cur=head->next;// 当前遍历到的节点
        while(cur){
            // 如果当前节点值>最后一个排序好的节点,直接+1
            if(cur->val>=lastSorted->val){
                lastSorted=lastSorted->next;
            }
            // 如果当前节点值<最后一个排序好的节点,从头遍历寻找插入位置
            else{
                ListNode* prev = myHead;//从虚拟头结点开始
                // 遍历找到第一个prev->next的值大于cur的值
                while (prev->next->val <= cur->val) {
                    prev = prev->next;
                }
                // 保存cur的后一个元素
                lastSorted->next = cur->next;//排序好的节点+1
                // 在prev后面插入cur
                cur->next = prev->next;//当前节点
                prev->next = cur;
            }
            cur = lastSorted->next;
        }
        return myHead->next;
    }
};

4、归并排序

148. 排序链表(中等难度)

剑指 Offer II 077. 链表排序(中等难度)

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        return sortList(head, nullptr);//归并排序
    }
    // 分割2个单链表
    ListNode* sortList(ListNode* head,ListNode* tail) {
        // 如果头结点为空,直接返回
        if(head==nullptr) return nullptr;
        // 如果头结点的下一个节点就是尾结点,说明当前链表只有1个元素
        if (head->next == tail) {
            head->next = nullptr;
            return head;
        }
        // 快慢指针找到head到tail的中间元素
        ListNode* slow = head, *fast = head;
        while (fast != tail) {
            slow = slow->next;
            fast = fast->next;
            if (fast != tail) {
                fast = fast->next;
            }
        }
        ListNode* mid = slow;//中间元素
        ListNode* l1 = sortList(head, mid); // 将左半部分变为有序链表
        ListNode* l2 = sortList(mid, tail); // 将右半部分变为有序链表
        return mergeList(l1, l2);
    }
    // 合并2个单链表
    ListNode* mergeList(ListNode* l1,ListNode* l2){
        ListNode* myHead= new ListNode(0);
        ListNode* p1=l1,*p2=l2,*res=myHead;

        while(p1&&p2){
            if(p1->val<=p2->val){
                res->next=p1;
                p1=p1->next;
            }
            else{
                res->next=p2;
                p2=p2->next;
            }
            res=res->next;
        }
        while(p1){
            res->next=p1;
            p1=p1->next;
            res=res->next;
        }
        while(p2){
            res->next=p2;
            p2=p2->next;
            res=res->next;
        }
        return myHead->next;
    }
};

十一、链表与二叉树

剑指 Offer 36. 二叉搜索树与双向链表(中等难度)

426. 将二叉搜索树转化为排序的双向链表(中等难度)

class Solution {
private:
    Node* pre=NULL; // 当前访问节点的前一个节点
    Node* head=NULL;// 双向循环链表的头结点
public:
    Node* treeToDoublyList(Node* root) {
        if(root==NULL) return NULL;
        // 采用中序遍历的方式遍历二叉搜索树,修改为双向链表
        dfs(root);
        // 修改为循环链表
        head->left = pre;//头结点指向尾结点
        pre->right = head;//尾结点指向头结点
        return head;
    }
    // 中序遍历
    void dfs(Node* cur){
        // 终止条件
        if(cur==NULL) return;
        // 递归左节点 
        dfs(cur->left);
        // 处理节点head,pre,cur
        if(pre) pre->right=cur;// 如果pre不为空,说明双向链表上已经有节点,其指向当前节点
        else head=cur;// 如果pre为空,说明双向链表没有节点,设置头结点
        cur->left=pre;// 当前节点指向上一个
        pre=cur;// 更新上一个节点
        // 递归右节点
        dfs(cur->right);
    }
};

109. 有序链表转换二叉搜索树(中等难度)

class Solution {
public:
    // 在[left,right)获取中间节点
    ListNode* getMid(ListNode* left, ListNode* right) {
        ListNode* fast = left;
        ListNode* slow = left;
        while (fast != right && fast->next != right) {
            fast = fast->next->next;// 前进2步
            slow = slow->next;// 前进1步
        }
        return slow;
    }
    // 在[left,right)中序遍历构建二叉搜索树
    TreeNode* buildTree(ListNode* left, ListNode* right) {
        if (left == right) return nullptr;
        // 构建中间节点
        ListNode* mid = getMid(left, right);// 获取中间节点
        TreeNode* root = new TreeNode(mid->val);//构建根节点
        // 递归左右节点
        root->left =  buildTree(left, mid);
        root->right = buildTree(mid->next, right);
        return root;
    }

    TreeNode* sortedListToBST(ListNode* head) {
        return buildTree(head, nullptr);
    }
};

114. 二叉树展开为链表(中等难度)

class Solution {
public:
    vector<TreeNode*> arr;
    void flatten(TreeNode* root) {
        dfs(root);
        for(int i=1;i<arr.size();++i){
            arr[i-1]->right=arr[i];
            arr[i-1]->left=nullptr;
            arr[i]->left=nullptr;
        }
    }
    void dfs(TreeNode* root){
        if(root==nullptr) return;
        arr.push_back(root);
        dfs(root->left);
        dfs(root->right);
    }
};

附录

1、单链表的C++实现

#include<iostream>
using namespace std;

/*单链表的实现*/
class SingleList{
private:
    struct ListNode {
        int val;
        ListNode *next;
        ListNode() : val(0), next(nullptr) {}
        ListNode(int x) : val(x), next(nullptr) {}
        ListNode(int x, ListNode *next) : val(x), next(next) {}
    };
    ListNode * head;//头结点,头结点可以直接赋值,其他节点必须在上一个节点处添加
public:
    SingleList():head(nullptr){}
    /**增加**/
    void push_front(int value){
        ListNode* newNode= new ListNode(value);
        newNode->next=head;
        head=newNode;
    }
    void push_back(int value){
        ListNode * newNode=new ListNode(value);
        // 如果链表为空添加为头结点
        if(head==nullptr) {
            head=newNode;
            return;
        }
        // 如果链表不为空添加到链表末尾
        else{
            // 遍历找到链表的最后一个节点,注意不能是while(p),因为链表节点的添加必须在上一个节点操作
            ListNode *p=head;
            while(p->next){
                p=p->next;
            }
            p->next=newNode;
        }
    }
    void insert(int index,int value){
        if(index<0) return;
        if(index==0) push_front(value);
        //找到第index个元素
        int i=0;
        ListNode* p=head;
        while(p&&i<index){
            i++;
            p=p->next;
        }
        if(p==nullptr) return;
        //在第index元素后添加新元素
        ListNode* newNode=new ListNode(value);
        newNode->next=p->next;
        p->next=newNode;
    }
    /**删除**/
    void pop_front(){
        if(head!=nullptr){
            ListNode* del = head;
            head=head->next;
            delete del;
        }
    }
    void pop_back(){
        this->erase(this->size()-1);
    }
    // 删除第index个元素
    void erase(int index){
        if(index<0) return;
        if(index==0) pop_front();
        else{
            //尝试找到第index-1个元素,删除其p->next
            ListNode *p=head;
            int i=0;
            while (p&&i<index-1)
            {
                p=p->next;
                i++;
            }
            //没有找到,可能是index超出size
            if(p==nullptr)return;
            //如果找到了且p->next不为空
            if(p->next){
                ListNode * del=p->next;
                p->next=del->next; 
                delete del;
            }
        }

    }
    /**查找**/
    int front(){
        if(head!=nullptr) return head->val;
        return -1;
    }
    int get(int index){
        if(index<0) return -1;
        if(index==0) return front();
        // 遍历找到第index元素,访问其p->val
        int i=0;
        ListNode* p=head;
        while(p&&i<index){
            p=p->next;
            i++;
        } 
        // 如果没有找到
        if(p==nullptr) return -1;
        return p->val;
    }
    int back(){
        return this->get(this->size()-1);
    }
    int size(){
        int len=1;
        ListNode* p=head;
        if(p==nullptr) return 0;
        while(p->next){
            len++;
            p=p->next;
        }
        return len;
    }

    void show(string name=""){
        cout<<name;
        for(ListNode* p=head;p!=nullptr;p=p->next){
            cout<<p->val<<" ";
        }
        cout<<endl;
    }
};

int main()
{
    SingleList* lst=new SingleList();
    lst->push_back(1);
    lst->push_back(2);
    lst->push_back(3);
    lst->push_front(0);
    lst->insert(2,4);
    lst->show("插入后:");
    cout<<"get:"<<lst->get(1)<<endl;
    lst->pop_front();
    lst->erase(1);
    lst->pop_back();
    lst->pop_back();
    lst->pop_back();
    cout<<"len:"<<lst->size()<<endl;
    lst->show("删除后:");


    system("pause");
    return 0;
}

2、双链表的C++实现

#include<iostream>
using namespace std;

/*双链表的实现*/
class DoubleList{
private:
    struct ListNode {
        int val;
        ListNode *next,*prev;
        ListNode() : val(0), next(nullptr),prev(nullptr) {}
        ListNode(int x) : val(x), next(nullptr),prev(nullptr) {}
        ListNode(int x, ListNode *next,ListNode *prev) : val(x), next(next),prev(prev) {}
    };
    ListNode * head,*tail;//头结点指向链表第一个元素;尾结点指向最后一个元素
public:
    DoubleList():head(nullptr),tail(nullptr){}
    /**增加**/
    void push_front(int value){
        ListNode * newNode=new ListNode(value);
        if(head==nullptr){
            head=newNode;
            tail=head;
        }
        else{
            newNode->next=head;
            head->prev=newNode;
            head=newNode;
        }
    }
    void push_back(int value){
        ListNode * newNode=new ListNode(value);
        // 如果尾结点为空添加为尾结点和头结点
        if(tail==nullptr) {
            tail=newNode;
            head=tail;
            return;
        }
        // 如果尾结点不为空添加到尾结点尾部
        else{
            // 遍历找到链表的最后一个节点,注意不能是while(p),因为链表节点的添加必须在上一个节点操作
            newNode->prev=tail;
            tail->next=newNode;
            tail=newNode;
        }
    }
    // 插入第index个元素
    void insert(int index,int value){
        if(index<0) return;
        if(index==0) push_front(value);
        if(index==size()-1) push_back(value);
        // 找到第index个元素
        int i=0;
        ListNode* p=head;
        while(p&&i<index){
            i++;
            p=p->next;
        }
        if(p==nullptr) return;
        // 在index个元素后添加新元素
        ListNode* newNode=new ListNode(value);
        newNode->next=p->next;
        p->next->prev=newNode;
        newNode->prev=p;
        p->next=newNode;
    }
    /**删除**/
    void pop_front(){
        if(head!=nullptr){
            ListNode* del = head;
            head=head->next;
            if(head) head->prev=nullptr; // 如果链表存在2个以上元素
            else tail=nullptr;//如果链表只有一个元素
            delete del;
        }
    }
    void pop_back(){
        if(tail!=nullptr){
            ListNode * del= tail;
            tail=tail->prev;
            if(tail) tail->next=nullptr;
            else head=nullptr;
            delete del;
        }
        
    }
    // 删除第index个元素
    void erase(int index){
        if(index<0) return;
        if(index==0) pop_front();
        if(index==size()-1) pop_back();
        else{
            //尝试找到第index个元素,删除其p->next和p->prev
            ListNode *cur=head;
            int i=0;
            while (cur&&i<index)
            {
                cur=cur->next;
                i++;
            }
            //没有找到,可能是index超出size
            if(cur==nullptr)return;
            //如果找到了且p->next不为空
            ListNode * del=cur;
            ListNode *p=cur->prev;
            p->next=cur->next;
            if(cur->next){
                cur->next->prev=p;
            }
            delete del;
        }

    }
    /**查找**/
    int front(){
        if(head!=nullptr) return head->val;
        return -1;
    }
    int back(){
        if(tail!=nullptr) return tail->val;
        return -1;
    }
    int get(int index){
        if(index<0) return -1;
        if(index==0) return front();
        if(index==size()-1) return back();
        // 遍历找到第index元素,访问其p->val
        int i=0;
        ListNode* p=head;
        while(p&&i<index){
            p=p->next;
            i++;
        } 
        // 如果没有找到
        if(p==nullptr) return -1;
        return p->val;
    }
    
    int size(){
        int len=1;
        ListNode* p=head;
        if(p==nullptr) return 0;
        while(p->next){
            len++;
            p=p->next;
        }
        return len;
    }

    void show(string name=""){
        cout<<name;
        for(ListNode* p=head;p!=nullptr;p=p->next){
            cout<<p->val<<" ";
        }
        cout<<endl;
    }
};

int main()
{
    DoubleList* lst=new DoubleList();
    lst->push_back(1);
    lst->push_back(2);
    lst->push_back(3);
    lst->push_front(0);
    lst->insert(3,4);
    lst->show("插入后:");
    cout<<"get:"<<lst->get(2)<<endl;
    lst->pop_front();
    lst->erase(2);
    lst->pop_back();
    // lst->pop_back();
    // lst->pop_back();
    cout<<"len:"<<lst->size()<<endl;
    lst->show("删除后:");


    system("pause");
    return 0;
}