【算法】链表题的常用技巧及算法题(C++)

这篇具有很好参考价值的文章主要介绍了【算法】链表题的常用技巧及算法题(C++)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 常用技巧 && 操作

下面的技巧通过一些后面的例题可以较好的理解熟练。

常用技巧

  1. 画图
    • 链表题通过画图可以较为直观分析链表,方便分析如何进行对链表的操作等
  2. 引入虚拟“头”节点
    • 虚拟节点适合处理一些边界情况
    • 同时放便我们进行对链表的操作
  3. 多定义变量
    • 多定义变量增强可读性,也方便自己写代码时的思路
    • 如当频繁使用cur节点的next时,直接定义 ListNode* next = cur->next; 不用再频繁对cur->next进行操作(这个例子较为简单,对于偏复杂的情况有大优点)
    • 不用过于在意这一点空间开销
  4. 双指针
    • 如快慢双指针可以解决链表判环、找环入口等。
  5. 常用操作
    • 头插 - “反转链表等题,可以解题”
    • 尾插

2. 根据技巧 小试牛刀

下面的题目难度不算太高,就用文字描述的形式解释思路、原理。

141.环形链表

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 解法:即前面介绍过的快慢双指针

  • 解法原理:快指针每次移动两步,而慢指针每次移动一步。如果链表中存在环,那么快指针相对于慢指针的速度差是一定的,因此快指针会逐渐靠近慢指针,最终追上它们。如果链表中不存在环,那么快指针会先到达链表的末尾。

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

代码文章来源地址https://www.toymoban.com/news/detail-779958.html

bool hasCycle(ListNode *head) {
    // 快慢指针
    ListNode* slow = head, *fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast =  fast->next->next;
        if(slow == fast)    return true;
    }
    return false;
}

142.环形链表II

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 解法快慢双指针
    1. 同理我们首先 判断链表是否存在环
    2. 当链表存在环则:
      • 让相遇位置节点与头节点依次向后移,直到两节点相遇
      • (也可以将慢指针重新指向头节点,随后快慢指针依次后移一步)

原理分析:我们设链表头节点到环入口节点的距离为a,环入口节点到相遇节点的距离为b,相遇节点到环入口节点的距离为c,则有以下关系:快指针走过的距离是慢指针的两倍,即2(a+b)=a+b+c+b,化简得到a=c。因此,在快慢指针相遇之后,让快指针重新指向头节点,然后快慢指针同时以相同的速度向前移动,当它们再次相遇时所在的节点就是环的入口节点。

代码

ListNode *detectCycle(ListNode *head) {
    if(head == nullptr || head->next == nullptr) return nullptr;
    
    ListNode* slow = head, *fast = head;
    // 先判断是否有环
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            ListNode* meet = slow; // 相遇位置设为meet
            while(meet != head)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return nullptr;
}

19.删除链表的倒数第N个结点

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 解法快慢双指针
    1. 让快指针向前移动n步,使得快指针与慢指针之间相隔n个节点。
    2. 同时移动快指针和慢指针,直到 fast到达链表的末尾 (即为空)。此时slow指向的节点就是要删除的节点的前一个节点 (倒数第n-1)
    3. 此时将slow指向的节点的next指针指向下下个节点,即跳过待删除节点。

代码

ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode* PNode = new ListNode(0); // 虚拟头节点
    PNode->next = head;
    ListNode* slow = PNode, *fast = PNode;
    // fast先走n+1步
    for(int i = 0; i < n+1; ++i)
        fast = fast->next;
    // 两指针同步走
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    // 此时slow就是倒数第n-1节点
    slow->next = slow->next->next;
    return PNode->next;
}

LCR024.反转链表

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 解法虚拟头指针+头插
    1. 我们首先创建虚拟头指针newhead,作为最终反转后的链表头节点
    2. 通过将原链表中的节点依次提取头插到新链表中,当cur指向空,此时newhead就是新链表的头

代码

ListNode* reverseList(ListNode* head) {
    ListNode* newhead = nullptr; // 虚拟头指针
    ListNode* cur = head; 

    // 利用头插翻转
    while(cur)
    {
        ListNode* next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }

    return newhead;
}

3. 解决算法题

2.两数相加

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

如上图所示:

代码

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    int t = 0; // 记录相加和
    ListNode* newhead = new ListNode(0); // 虚拟头节点
    ListNode* cur1 = l1, *cur2 = l2; // 用于遍历链表
    ListNode* prev = newhead; // 尾指针
    while(cur1 || cur2 || t)
    {
        // 相加并移动指针 
        if(cur1){
            t += cur1->val;
            cur1 = cur1->next;
        }
        if(cur2){
            t += cur2->val;
            cur2 = cur2->next;
        }

        // 更新结果
        prev->next = new ListNode(t % 10);
        prev = prev->next;
        t /= 10;
    }
    // 释放空间
    prev = newhead->next;
    delete newhead;

    return prev;
}

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

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

这里我们提供两种解法:① 递归 ② 循环(模拟过程)

  • 解法一递归

    【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

    • 根据图中思路,两两交换节点后,新头节点应该是之前在后面的节点
    1. 我们首先记录新头节点节点,后更改指针朝向,将新头节点的next指向下一位的新头节点
    2. 函数的返回结果就是每次的新头节点,利用递归即tmp = swapPairs(head->next->next),即交换后面的节点并每次链接新头节点

代码

ListNode* swapPairs(ListNode* head) {
    if(head == nullptr || head->next == nullptr)
        return head;
    // 递归
    
    auto tmp = swapPairs(head->next->next); // 翻转后的前一位节点
    auto ret = head->next; // 新头节点
    head->next->next = head; // 更改指针指向
    head->next = tmp;

    return ret;
}
  • 解法二循环

    【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

    1. 思路如图所示,我们首先定义四个指针(首先创建一个虚拟头节点)
    2. 根据四个指针位置可以完成交换节点,移动指针的操作
    3. cur代表每一轮待交换的指针,当在一轮交换后,我们进行指针位置修正
    4. 直到cur、next任意一方为空,结束循环

代码

ListNode* swapPairs(ListNode* head) {
    if(head == nullptr || head->next == nullptr)
        return head;

    ListNode* prev = new ListNode(0);
    ListNode* cur = head;
    ListNode* next = cur->next, *nnext = next->next;
    head = next; // 一轮交换后,head会到第二位,修正位置
    while(cur && next) // cur与next都不为空时才进行交换
    {
        // 两两交换操作
        prev->next = next;
        next->next = cur;
        cur->next = nnext;

        // 移动指针
        prev = cur;
        cur = nnext;
        next = cur ? cur->next : nullptr;
        nnext = next ? next->next : nullptr;
    }

    return head;
}

143.重排链表

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 题目要求将链表按照规则重拍
  • 我们可以按照以下思路执行(以链表[1, 2, 3, 4, 5]举例):
    1. 将链表从中点处分开 1 2 3 与 4 5
    2. 将中点后的链表逆序 5 4
    3. 合并两链表 1 2 3 + 5 4 = 1 5 2 4 3

代码

void reorderList(ListNode* head) {
   if(!head || !head->next || !head->next->next) return;

    // 1. 找到链表中点
    ListNode* slow = head, *fast = head;
    while(fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }

    // 2. 翻转中点后的节点
    ListNode* newhead = nullptr;
    ListNode* cur = slow->next;
    while(cur) 
    {
        ListNode* next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    slow->next = nullptr; // 切断前半部分与后半部分的连接

    // 3. 合并链表(123 与 54 -> 1 5 2 4 3)
    ListNode* cur1 = head, *cur2 = newhead;
    while(cur1 && cur2) {
        ListNode* next1 = cur1->next, *next2 = cur2->next;
        cur1->next = cur2;
        cur2->next = next1;
        cur1 = next1;
        cur2 = next2;
    }
}

23.合并K个升序链表

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

  • 解法小根堆
    【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++
  • 细节注意:创建小根堆时需要用自定义比较,下面我们使用自定义比较结构体、也可以使用lambda表达式

代码

class Solution {
public:
    struct cmp // 自定义比较
    {
        bool operator()(const ListNode* l1, const ListNode* l2){
            return l1->val > l2->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap; // 创建小根堆

        for(auto l : lists)
            if(l) heap.push(l); // 将所有插入到堆中

        // 合并链表
        ListNode *head = new ListNode(0);
        ListNode *prev = head;
        while(!heap.empty()) // 堆有元素时,进行操作
        {
            ListNode* tmp = heap.top();
            heap.pop();

            prev->next = tmp; // 更新链表链接关系和prev
            prev = tmp;
            if(tmp->next)   heap.push(tmp->next);
        }

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

25.K个一组翻转链表

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

思路

【算法】链表题的常用技巧及算法题(C++),算法,算法,链表,c++

代码

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* cur = head;
        int n = 0; // 记录待翻转链表个数
        while(cur)
        {
            cur = cur->next;
            n++;
        }
        n /= k;


        ListNode* newhead = new ListNode(0);
        ListNode* prev = newhead;
        cur = head;
        // 进行链表的分组旋转 n组 每组k个节点
        for(int i = 0; i < n; ++i)
        {
            ListNode* tmp = cur; // tmp记录每次待翻转链表的头节点
            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;
        // 删除newhead释放空间
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};

到了这里,关于【算法】链表题的常用技巧及算法题(C++)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 链表综合算法设计(c++数据结构)

      一、设计内容 已知简单的人事信息系统中职工记录包含职工编号(no)、职工姓名(name)、部门名称(depname)、职称(title)和工资数(salary)等信息(可以增加其他信息),设计并完成一个简单的人事信息管理系统,要求完成但不限于以下功能: (1)    增加一个职工信息

    2024年02月02日
    浏览(57)
  • 算法竞赛基础:C++双向链表的结构和实现(普通链表、List、静态链表)

    本文将会介绍在算法竞赛中双向链表的几种使用方式,适合有一定基础的人阅读。 一般来说,普通的链表结构是这样的: next指针指向下一个链表,这样的结构只能够支持单向查询。 双向链表,顾名思义,就是可以支持双向的访问和查询。 也就是这样的: 这种链表为访问前

    2024年01月24日
    浏览(41)
  • 【数据结构】单链表经典算法题的巧妙解题思路

    目录 题目 1.移除链表元素 2.反转链表 3.链表的中间节点 4.合并两个有序链表 5.环形链表的约瑟夫问题 解析 题目1:创建新链表 题目2:巧用三个指针 题目3:快慢指针 题目4:哨兵位节点 题目5:环形链表   介绍完了单链表,我们这次来说几个经典的题目,本篇的题目衔接下一

    2024年04月28日
    浏览(63)
  • Java之包装类的算法小题的练习

    需求: 键盘录入一些1~10日之间的整数,并添加到集合中。直到集合中所有数据和超过200为止。 代码示例: 需求: 自己实现parseInt方法的效果,将字符串形式的数据转成整数。要求:字符串中只能是数字不能有其他字符最少一位,最多10位 0不能开头 代码示例: 需求: 定义一

    2024年02月09日
    浏览(43)
  • C++中的算法与数据结构优化技巧

    在C++编程中,算法和数据结构的优化是提高程序性能和效率的关键因素之一。下面是一些常见的算法和数据结构优化技巧,希望对您有帮助: 选择合适的数据结构:数据结构的选择对算法效率有重要影响。根据具体问题的需求,选择合适的数据结构,如数组、链表、树、散列

    2024年01月17日
    浏览(43)
  • 过去一周写过的算法题的一部分(dfs,贪心)

    (首先说明一点哈:这是我第一次写博客,写的不好大家见谅) 自我介绍:一个脑子不好的大一学生,c语言接触还没到半年,若涉及到效率等问题,各位都可以在评论区提出见解,谢谢啦 (题目链接:P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)) 我一开始用

    2024年02月03日
    浏览(30)
  • 小白也能学会的链表 | C++ | 算法与数据结构 (新手村)

    本质:找到两个有序数据段中的第一个相同数据 解题:利用set的不重复性,首先把headA都塞到set中,再遍历headB找有没有已经出现在set中的节点即可。 注意! set的数据是ListNode* 不是 int。如果是int可能出现节点不同但是var相同的情况,而ListNode* 就不会。 时间复杂度:O(2n)

    2024年02月14日
    浏览(55)
  • 编程导航算法通关村第 1 关|青铜 - C++是如何构造出链表的

             在C++中,链表是由一系列节点构成的,每个节点包含一个值和一个指向下一个节点的指针。         我们可以用结构体定义出一个节点:               在定义完后,我们将链表进行初始化,并插入5条数据:   接着我们在主函数中进行测试,看看是否能成

    2024年02月13日
    浏览(41)
  • C++期末考试选择题题库100道&&C++期末判断题的易错知识点复习

    今天备考C++,看到了一些好的复习资料,整合一起给大家分享一下 对于常数据成员,下面描述正确的是 【 B 】 A. 常数据成员必须被初始化,并且不能被修改 B. 常数据成员可以不初始化,并且不能被修改 C. 常数据成员可以不初始化,并且可以被修改 D. 常数据成员必须被初始

    2024年02月10日
    浏览(59)
  • 【C++】常用排序算法

    2024年02月09日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包