算法—链表

链表中常用的技巧

  1. 引入虚拟"头"节点:便于处理边界情况,并且方便对链表操作。
  2. 不要吝啬空间,大胆定义变量:比如我们在已经存在的链表中插入节点时容易因为插入顺序的原因导致找不到某个节点了,这时只需要定义一个指针变量,指向容易因为插入顺序而找不到的节点,这样通过这个指针就能找到这个节点,以那种顺序插入其他节点就都可以了。
  3. 使用快慢双指针(适用部分题)。

两数相加

**思路:**定义两个指针分别指向两链表,然后模拟题目说的流程即可,两个节点的值相加时可能会有进位,所以需要定义一个变量存储进位,当两个链表都遍历结束后,还需要单独判断一下进位是否为 0,如果不为 0,还需要在创建一个节点存储最终的进位。

代码:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = new ListNode(0);
        ListNode* tail = head;
        int carry = 0; //存储进位
        ListNode* cur1 = l1;
        ListNode* cur2 = l2;
        while(cur1 != NULL || cur2 != NULL){
            if(cur1 != NULL)
                carry += cur1->val;
            if(cur2 != NULL)
                carry += cur2->val;

            int num = carry % 10;
            carry /= 10;
            ListNode* newnode = new ListNode(num);
            tail->next = newnode;
            tail = newnode;

            if(cur1 != NULL)
                cur1 = cur1->next;
            if(cur2 != NULL)
                cur2 = cur2->next;
        }
        if(carry != 0){
            ListNode* newnode = new ListNode(carry);
            tail->next = newnode;
            tail = newnode;
        }

        ListNode* newhead = head->next;
        delete head;
        return newhead;
    }
};

两两交换链表中的节点

**思路:**定义一个虚拟头节点,然后定义四个指针,分别指向要交换的两个节点的前一个节点,要交换的两个节点,要交换的两个节点的后一个节点,通过这四个指针就能完成这两个节点位置的交换,然后再移动这四个指针指向下一对交换的节点,继续这个过程直到所有节点完成交换。

循环结束条件是 cur 指针为空(对应节点为偶数个)或者 next 指针为空(对应节点为奇数个)

代码:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return head;
        ListNode* newnode = new ListNode(0);
        ListNode* prev = newnode;
        ListNode* cur = head;
        ListNode* next = head->next;
        ListNode* nnext = next->next;
        while(cur != NULL && next != NULL){
            prev->next = next;
            next->next = cur;
            cur->next = nnext;

            prev = cur;
            cur = nnext;
            if(cur)
                next = cur->next;
            if(cur && next)
                nnext = next->next;
        }

        head = newnode->next;
        delete newnode;
        return head;
    }
};

重排链表

**思路:**找到链表的中间节点,把链表分成两部分,把后面部分逆序,然后合并两个链表。将链表逆序可以先创建一个虚拟头节点,然后将要逆序的部分头插到这个虚拟头节点的链表中即可。合并两个链表先创建一个虚拟头节点,然后使用尾插就行。

代码:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        if(head == NULL || head->next == NULL || head->next->next == NULL)
            return;

        ListNode* slow = head;
        ListNode* fast = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
        }

        ListNode* newhead = new ListNode(0);
        ListNode* cur = slow->next;
        slow->next = NULL;
        while(cur){
            ListNode* next = cur->next;
            cur->next = newhead->next;
            newhead->next = cur;
            cur = next;
        }

        ListNode* newhead2 = new ListNode(0);
        ListNode* tail = newhead2;
        ListNode* cur1 = head;
        ListNode* cur2 = newhead->next;
        while(cur1){
            tail->next = cur1;
            tail = cur1;
            cur1 = cur1->next;
            if(cur2){
                tail->next = cur2;
                tail = cur2;
                cur2 = cur2->next;
            }
        }

        delete newhead;
        delete newhead2;
    }
};

合并 K 个升序链表

**思路:**这道题可以使用优先级队列来解决,创建一个小根堆,然后将所有链表头节点放入小根堆中,每次取出堆顶元素尾插到结果链表中,然后判断一下,如果取出的这个节点后面还有其他节点,将它后面的节点也扔入小根堆中(每次只扔一个)。另外,这个结果链表要有一个虚拟头节点,方便插入数据和返回结果时找到头节点。

代码:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
    struct cmp{
        bool operator()(ListNode* l1, ListNode* l2){
            return l1->val > l2->val;
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;
        for(auto l : lists){
            if(l)
                heap.push(l);
        }

        ListNode* newhead = new ListNode(0);
        ListNode* tail = newhead;
        while(!heap.empty()){
            ListNode* node = heap.top();
            heap.pop();
            tail->next = node;
            tail = node;
            if(node->next)
                heap.push(node->next);
        }

        tail = newhead->next;
        delete newhead;
        return tail;
    }
};

K 个一组翻转链表

**思路:**先求出需要逆序多少组,然后重复 n 次长度为 k 的链表逆序即可。逆序还是使用创建虚拟头节点然后头插的方法就可以。

代码:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution 
{
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
        int n = 0;
        ListNode* cur = head;
        while(cur)
        {
            n++;
            cur = cur->next;
        }   
        n /= k;

        ListNode* newhead = new ListNode();
        ListNode* prev = newhead;
        cur = head;
        for(int i = 0; i < n; i++)
        {
            ListNode* tmp = cur;
            for(int j = 0; j < k; j++)
            {
                ListNode* next = cur->next;
                cur->next = prev->next;
                prev->next = cur;
                cur = next;
            }
            prev = tmp;
        }

        prev->next = cur;
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};
相关推荐
想带你从多云到转晴3 小时前
01、JAVAEE--多线程(一)
java·开发语言·javaee
枷锁—sha3 小时前
【CTFshow-pwn系列】06_前置基础【pwn 035】详解:利用 SIGSEGV 信号处理机制
java·开发语言·安全·网络安全·信号处理
BlackWolfSky3 小时前
鸿蒙中级课程笔记7—给应用添加通知
笔记·华为·harmonyos
f狐0狸x3 小时前
【C++修炼之路】C++容器:stack、queue、deque 基本用法详解
c++·stl
学嵌入式的小杨同学3 小时前
【Linux 封神之路】文件操作 + 时间编程实战:从缓冲区到时间格式化全解析
linux·c语言·开发语言·前端·数据库·算法·ux
xqqxqxxq3 小时前
结构体(Java 类)实战题解笔记(持续更新)
java·笔记·算法
Gain_chance3 小时前
27-学习笔记尚硅谷数仓搭建-数据仓库DWD层介绍及其事务表(行为)相关概念
大数据·数据仓库·笔记·学习
会开花的二叉树3 小时前
高性能定时器:时间轮算法的工程实践
算法
大江东去浪淘尽千古风流人物4 小时前
【LingBot-Depth】Masked Depth Modeling for Spatial Perception
人工智能·算法·机器学习·概率论