链表问题

  • 前言
  • 链表基本操作
    • 链表创建
    • 头插法
    • 尾插法
    • 节点遍历
    • 节点删除
  • 双指针法
    • 链表中的倒数k个结点
      • 解题思路
      • 解题代码
  • 分割法
    • 链表分割
    • 解题思路
    • 解题代码
  • 遍历与创建链表结合
    • 链式A+B
      • 解题思路
      • 解题代码
  • 链表翻转 双指针结合
    • 回文链表
      • 解题思路
      • 解题代码
  • 转换路径问题
    • 链表相交
    • 解题思路
    • 换个思路
    • 解题代码
    • 链表环路检测
      • 解题思路
      • 解题代码

前言

链表问题因为涉及指针,出现错误时通常为段错误(由于访问越界引起的错误)如果没有外用编译器或者GDB是很难debug的,由于野指针,悬浮指针等问题,导致在C/C++中尤其难以debug。而如果对于链表操作不熟练,如节点连接,节点删除那就更难以处理出错。

链表基本操作

以单链表为例

链表创建

链表创建有头插法和尾插法两种方法,题做多了,就能根据不同的情境使用不同的方法。
下述操作基于此结构体节点:

struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(nullptr) {}
};

头插法

/* head开始有n个节点** 头插法 每新建一个节点都接到头节点head之后* 连接的方法为 把头节点head之后的节点接到新节点后面即 node->next=head->next;*             把新节点接到头节点head之后即 head->next=node;
*/
ListNode *headInsert(int n)
{ListNode *head=new ListNode(1);for(int i=2;i<=n;++i){ListNode *node=new ListNode(i);node->next=head->next;head->next=node;}return head;
}

尾插法

/*head开始有n个节点** 尾插法 每新建一个节点都接到尾部tail之后* 连接的方法为 把新节点接到尾部tail即 tail->next=node;*            修改尾部tail为新节点node即 tail=node;*/
ListNode *tailInsert(int n)
{ListNode *head=new ListNode(1);ListNode *tail=head;for(int i=2;i<=n;++i){ListNode *node=new ListNode(i);tail->next=node;tail=node;}return head;
}

节点遍历

/*传入一个头节点执行遍历* cur为遍历时的当前节点* 访问完一个节点就指针cur后移即 cur=cur->next;* 边界条件为尾节点为空 跳出*/
void travel(ListNode *head){ListNode *cur =head;while(cur){cout<<cur->val<<" ";cur=cur->next;}cout<<endl;
}

节点删除

/*传入一个头节点和要删除的节点序号* 遍历找到该节点位置* 删除方法为:找到待删除节点的前驱,接上其后继节点* 假设删除节点为 3 则找到2接上4。** 特别的 n<=0的的情况应该排除*       超出链表范围的也应该排除*       头节点没有前驱,只需 head=head->next;*/
ListNode* del(ListNode *head , int n){if(n<=0) return head;ListNode *cur=head;if(n==1){head=head->next;delete cur;}else {for (int i=2;i<=n-1&&cur;++i) {cur=cur->next;}if(!cur||!cur->next)return head;//超出链表长度ListNode *p=cur->next;cur->next=cur->next->next;delete p;}return head;
}

双指针法

这个方法是以快慢指针的方法,达到定序的作用。如:

链表中的倒数k个结点


原题地址

解题思路

让快指针提前走k步,慢指针才开始走。

解题代码

class Solution {public:ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {ListNode *front=pListHead,*back=pListHead;unsigned i;for(i=0;i<k&&front;++i){front=front->next;}if(i<k)return nullptr;while(front){front=front->next;back=back->next;}return back;}
};

分割法

类快排的分割法,对链表进行分割,序列重排。

链表分割


原题地址

解题思路

新起两个指针,分别连接 <x 和 >=x 的两条链表,最后把大链表接在小链表后面。

特别要注意的是:由于采用在原链表的节点,通过调整其连接序列来新建两个链表,不需要额外空间开销,但需要注意bigHead的尾部可能连接着 <x 的节点所以需要将其置空否则将导致链表成环。

解题代码

//这里没有先建头节点,采用尾插法创建链表
//需要先判断头节点是否建立
//且需要注意边界条件 如:没有 <x或者没有 >=x 的节点
class Partition {public:ListNode* partition(ListNode* pHead, int x) {// write code hereListNode *smallHead=nullptr,*bigHead=nullptr;ListNode *p=pHead,*smallp=nullptr,*bigp=nullptr;while(p){if(p->val<x){if(!smallHead){smallHead=p;smallp=smallHead;}else {smallp->next=p;smallp=smallp->next;}}else {if(!bigHead){bigHead=p;bigp=bigHead;}else{bigp->next=p;bigp=bigp->next;}}p=p->next;}if(bigp)bigp->next=nullptr;if(smallp)smallp->next=bigHead;return smallHead?smallHead:bigHead ;}
};

遍历与创建链表结合

链式A+B

解题思路

1) 首先需要遍历A,B两个链表获得两个值,可采用遍历累加,每一位乘上数位权重
2) 将其和值sum转换为链表。循环(对10)取余、求商,获得每一位的值。
每次获得的值都是sum的低位,且题目要求数位反向存储。那么采用头插法即可。

解题代码

class Plus {public:ListNode* plusAB(ListNode* a, ListNode* b) {// write code hereint A=0,B=0;int n=0;while(a){A+=a->val*pow(10,n);++n;a=a->next;}n=0;while(b){B+=b->val*pow(10,n);++n;b=b->next;}int sum=A+B;ListNode *res=new ListNode(-1);ListNode *p=res;while(sum){ListNode *node=new ListNode(sum%10);p->next=node;p=node;sum/=10;}return res->next;}
};

链表翻转 双指针结合

以链表翻转结合双指针来解决经典问题: 回文链表

回文链表


原题地址

解题思路

如果不看进阶要求,那么只需要用一个栈,来存储数据再比较即可或者转换为回文数组。

进阶:
如何判断回文链表:
找到中间结点:快慢指针,快指针速度为慢指针速度的一半。
翻转其中一半链表,则得到两段一样的链表,遍历判断即可

解题代码

1、简单模拟下链表长度为单数或者双数的情况。
得知以head为起点,如果为双数,快指针最后为空,慢指针位于中点的next。
如果为单数,快指针位于尾节点,慢指针位于中点。
2、翻转链表需要:当前节点的上一个即pre,而继续往遍历则不能丢掉当前节点的尾巴。
即以pre节点迭代记住前一个节点,同时需要个临时节点记住当前节点的尾巴,以便后移。
3、最后得到pre节点为前半部分翻转链表头,slow为后半链表头

class Solution {public:bool isPalindrome(ListNode* head) {ListNode *fast,*slow;fast=slow=head;ListNode *pre=nullptr;//快慢指针遍历,同时翻转前半部分while(fast&&fast->next){fast=fast->next->next;ListNode *temp=slow->next;slow->next=pre;pre=slow;slow=temp;}//如果为单数的情况,需要后移一位if(fast) slow=slow->next;//开始判断两段链表是否一致。while(slow&&pre){if(slow->val!=pre->val)return false;else slow=slow->next,pre=pre->next;}return (!slow||!pre)?true:false;}
};

转换路径问题

链表可以看成一条路径,在某些链表问题如:链表相交、链表成环等等 ,我们可以把链表看成路径,问题则可以变成简单的数学问题如:追及问题。

链表相交

原题地址

解题思路

1、如果通过暴力法写两个循环,比较A链的每个节点与B链的每个节点,那么复杂度将是O(n2)。

换个思路

2、如果把链表看成路径,在假设有两人在路径上竞走,但两人起始地点不一定相同,求其相遇的地点。这个问题怎么解?
评论区有个特别浪漫的说法:我变成你,走过你的路;你变成我,走过我的路,最后我们相遇。即:让先各自遍历链表,到达一方结尾,走另一方的路,最后路程相等:A+B=B+A
只要让两个点遍历链表,判断条件为节点指针是否相等,如果达到一方末端则走另一方的路。

解题代码

class Solution {public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *A=headA, *B=headB;while(A!=B){A!=NULL?A=A->next:A=headB; B!=NULL?B=B->next:B=headA;}return A;}
};

链表环路检测


原题地址

解题思路

要求:原题需要我们找到环路的入口,如果没有环则返回nullptr。

设环路起点距离head为X,环一圈为Y长。如果求链表的中点我们可以用快慢指针,快指针为慢指针的两倍步长。
1、这里我们如果用快慢指针,如果存在环,他们必在环上相遇。设相遇点距离入口为k,此时快指针已在环上走了n圈:存在关系2(x+k)=x+k+nY画简得X+k=nY
X=(n-1)Y+Y-k这里Y-k也是相遇点和入口的距离。
2、那么答案已经呼之欲出了;让快指针回到head按照慢指针的速度走,下次相遇的节点就是环的入口。
3、如果无环节点,快慢指针将在空指针相遇。

解题代码

class Solution {public:ListNode *detectCycle(ListNode *head) {if(!head||!head->next)return nullptr;ListNode *fast,*slow;fast=slow=head;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow) break;}if(fast!=slow)return nullptr;//无环fast=head;while(fast!=slow){fast=fast->next;slow=slow->next;}return fast;}
};

编程题总结 链表问题常用解决方法相关推荐

  1. Java编程题_面向对象和常用API01_B级

    Java编程题_面向对象和常用API01_B级 第1题 面向对象.异常.集合.IO 题干: 请编写程序,完成键盘录入学生信息,并计算总分将学生信息与总分一同写入文本文件 需求:键盘录入3个学生信息(姓 ...

  2. directx11编程中遇到的错误及解决方法

    directx11编程中遇到的错误及解决方法 参考文章: (1)directx11编程中遇到的错误及解决方法 (2)https://www.cnblogs.com/zhangbaochong/p/55 ...

  3. win10 不能查看其它电脑共享文件夹常用解决方法

    win10 不能查看其它电脑共享文件夹常用解决方法 问题描述 解决方法一: 解决方法二: 解决方法三: 原文链接: 问题描述 同一局域网呢,其它系统可以访问到win10系统的共享文件夹,win10 网 ...

  4. arcgis gp 选择图层_【干货】ArcGIS的一些常用解决方法

    原标题:[干货]ArcGIS的一些常用解决方法 1.ArcMap突然打不开了或者崩溃: 查看安全软件(例如360安全软件的拦截),是不是误删了ArcGIS相关文件. 2.ArcMap一些功能突然不能用 ...

  5. 实用技能:DNS故障分析及常用解决方法

    由于计算机无法直接识别我们输入的域名,所以必须通过DNS解析环节将域名翻译成可由计算机识别的IP地址,才能完成整个访问过程.如果DNS发生故障就无法将域名正确指向对应的IP地址,进而无法实现通过域名访 ...

  6. 小目标检测常用解决方法

    小目标检测常用解决方法 1 定义 通用的定义来自 COCO 数据集,定义小于 32x32 pix 的为小目标. 2 小目标检测的难点 可利用特征少 现有数据集中小目标占比少 小目标聚集问题 首先小目标 ...

  7. Android手机编程初学遇到的问题及解决方法

    对高手来讲不值一提,可是对我这个初学来讲却是因为这些问题费了老长时间,有的不是编程问题,但不注意也会浪费不少宝贵时间!随时遇到随时更新... 引入第三方类库的问题,开始引用后没什么问题,但发现了该类库 ...

  8. java安装有错误码咋办_java编程出现的错误对应的解决方法

    error: could not open D:\java\jre1.8\lib\amd64\jvm.cfg 解决方法:把java的环境变量%JAVA_HOME%/bin上移到最上面 优化 查看网页源 ...

  9. 16章编程题-C语言程序设计:现代方法(第2版)课后答案

    1. 编写程序用来要求用户录入国际电话区号,然后在数组country_codes中查找它(见16.3节).如果找到对应的区号,程序需要显示相应的国家名称,否则显示出错消息. #include < ...

最新文章

  1. sd.js帮助您简化繁重的获取数据、存储数据(CRUD)骚操作(吐槽~在安卓9.0以下或者IOS10.X以下手机端H5页面不支持,在这两种情况下的系统只能使用ajax或者原生js请求后台数据)
  2. USB学习6---Linux Android USB软件架构设计
  3. priorityqueue 的 add和offer方法有区别吗_日常在家安吉白茶应该如何去保存?城市与农村存放的方法有区别吗...
  4. PHP调用外部服务获取IP地域信息实现信息的地域性关联
  5. android 面试题(一)
  6. bfc是什么_全面分析总结BFC原理及实践
  7. CCD与CMOS摄像头的区别
  8. SpringBoot创建第一个Web项目——Hello SpringBoot
  9. (二)Graphivz 简单结构图及子图
  10. 【渝粤教育】国家开放大学2018年春季 0550-21T素描(一) 参考试题
  11. Android 8(1),腾讯字节爱奇艺网易华为实习面试汇总
  12. WinRAR去广告实现
  13. golang 中文处理
  14. 华为手机的视频剪辑功能居然这么强大,太实用啦
  15. WebView---android webview组件如何使用 Webview与js交互
  16. java注解和反射原理_Java中的注解和反射
  17. Pikachu靶场通过记录
  18. 自己剪辑的视频,怎么配音比较准确完美?
  19. 和文华7一样好用的指标策略预警系统
  20. 产品工作中的沟通注意事项小总结

热门文章

  1. 初中数学老师计算机培训反思,初中数学特级教师培训会学习心得体会
  2. 13.7.7.42 SHOW WARNINGS 语句
  3. web安全基础之HTTP
  4. Nape实现坐标旋转角度回弹
  5. [HDU - 2063] 过山车(二分图)
  6. iPhone 12易掉漆、边框太锋利还割手?库克快出来对线......
  7. 大数据的应用场景都有哪些(交通篇)
  8. Androidstudio ADB调试
  9. 如何在OUTLOOK签名中自动加入日期
  10. 转:感想东莞,想想东莞为外来工做了什么?