剑指offer面试常考手撸算法题-链表篇

1. 从头到尾打印链表

 class Solution {
public:
// 可以先压栈,再出栈到vector
// 时间/空间:O(n)
vector<int> printListFromTailToHead(ListNode* head) {
if(head == nullptr)
return {};
vector<int> res;
stack<int> s;
while(head != nullptr)
{
s.push(head->val);
head = head->next;
}
while(!s.empty())
{
res.push_back(s.top());
s.pop();
}
return res;
}
// 可以直接插入vector中,翻转vector
// 时间/空间:O(n)
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> res;
if(head == nullptr)
return {};
while(head)
{
res.push_back(head->val);
head = head->next;
}
reverse(res.begin(), res.end());
return res;
}
};

2. 链表中倒数第k个节点

 class Solution {
public:
//快慢指针,快指针先走k-1步,之后一起走,直到快指针到达链表尾。
//时间:O(n), 空间O(1)
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr || k == )
return nullptr;
auto p = pListHead;
for(int i=; i<k-; i++)
{
if(p->next == nullptr)
return nullptr;
p = p->next;
}
while(p->next != nullptr)
{
p = p->next;
pListHead = pListHead->next;
}
return pListHead;
}
};

3. 翻转链表

 class Solution {
public:
//一个取巧的方法(如果允许使用额外空间):先遍历链表用栈存储元素值,然后重新遍历链表,将链表值置为栈顶。
//时间:O(n), 空间O(n)
ListNode* ReverseList(ListNode* pHead) {
stack<int> s;
auto p = pHead, q = pHead;
while(pHead != nullptr)
{
s.push(pHead->val);
pHead = pHead->next;
}
while(p != nullptr)
{
p->val = s.top();
s.pop();
p = p->next;
}
return q;
}
};
 class Solution {
public:
//2. 真正地翻转链表,交换地址
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr)
return nullptr;
decltype(pHead) pre = nullptr;
auto next = pre;
ListNode *res = nullptr;
while(pHead != nullptr)
{
next = pHead->next;
if(next == nullptr)
res = pHead;
pHead->next = pre;
pre = pHead;
pHead = next;
}
return res;
}
};

4. 合并两个排序链表

 class Solution {
public:
//非递归版本:双指针分别遍历两个链表
ListNode* Merge1(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
if(pHead2 == nullptr)
return pHead1;
ListNode *head = new ListNode();
head->next = nullptr;
ListNode *res = head;
while(pHead1 != nullptr && pHead2 != nullptr)
{
if(pHead1->val <= pHead2->val)
{
head->next = pHead1;
head = head->next;
pHead1 = pHead1->next;
}
else
{
head->next = pHead2;
head = head->next;
pHead2 = pHead2->next;
}
}
if(pHead1 != nullptr)
head->next = pHead1;
else
head->next = pHead2;
return res->next;
}
//递归版本
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
if(pHead2 == nullptr)
return pHead1; ListNode* head = nullptr;
if(pHead1->val <= pHead2->val)
{
head = pHead1;
head->next = Merge(pHead1->next, pHead2);
}
else
{
head = pHead2;
head->next = Merge(pHead2->next, pHead1);
}
return head;
}
};

5. 两个链表第一个公共节点

 class Solution {
public:
//暴力法太低级,O(n2)不可接受
//使用unordered_map存储一个链表节点吧,时间O(n),空间O(n)
//unordered_map使用[]/insert插入,不是push_back()
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr)
return nullptr;
if(pHead2 == nullptr)
return nullptr; unordered_map<int, ListNode*> ump;
while(pHead1 != nullptr)
{
ump.insert({pHead1->val, pHead1});
pHead1 = pHead1->next;
}
while(pHead2 != nullptr)
{
auto res = ump.find(pHead2->val);
if(res != ump.end())
return res->second;
pHead2 = pHead2->next;
}
return nullptr;
}
};

6. 链表中环的入口节点(快2满1指针判断成环,再走一圈计算环长,快慢指针找到入口)

判断链表是否成环(快慢指针解决)

 /*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
//判断成环及入口:快慢指针
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
ListNode *pMeet = judgeLoop(pHead);
if(pMeet == nullptr)//未成环
return nullptr;
auto p = pMeet;
int loopLen = ;
//计算环长,相遇点再走一圈
while(p->next != pMeet)
{
p = p->next;
loopLen++;
}
auto q = pHead;
//后指针先走环长
while(loopLen--)
{
q = q->next;
}
//快慢一起走
p = pHead;
while(p != q)
{
p = p->next;
q = q->next;
}
return p;
}
//判断是否成环,快指针走两步-慢指针走一步,指针相遇必在环内
ListNode* judgeLoop(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
auto pSlow = pHead->next;
if(pSlow == nullptr)
return nullptr;
auto pFast = pSlow->next;
while(pSlow != nullptr && pFast != nullptr)
{
if(pSlow == pFast)
return pSlow;
pSlow = pSlow->next;
pFast = pFast->next;
if(pFast != nullptr)
pFast = pFast->next;
}
return nullptr;
}
};

7. 删除链表重复节点(重复保留一个)

 class Solution {
public:
//一次遍历,前后节点值相等,删除后一节点
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
auto pre = pHead;
auto cur = pre->next;
while(cur != nullptr)
{
while(cur->val == pre->val)
{
pre->next = cur->next;
cur = pre->next;
if(cur == nullptr)
return pHead;
}
pre = cur;
cur = cur->next;
}
return pHead;
}
};

8. 删除链表重复节点(重复节点不保留)

 class Solution {
public:
/*创建一个头节点,它的next指向链表头,然后再用两个指针
一前一后来遍历链表,后一个指针判断有无重复并进行后移*/
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
ListNode *first = new ListNode();
first->next = pHead;
ListNode *last = first;
ListNode *p = pHead;
while(p != nullptr && p->next != nullptr)
{
if(p->val == p->next->val)//有重复,需要删除
{
int val = p->val;
while(p != nullptr && p->val == val)
p = p->next;
last->next = p;
}
else
{
last = p;
p = p->next;
}
}
return first->next;
}
};

9. 判断两个链表是否交叉

(同样可使用一个unordered_map来存储一个链表中的节点指针,再遍历另外一个链表逐个查找)

 bool FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr || pHead2 == nullptr)
return false;
unordered_map<ListNode*, int> ump;
while(pHead1 != nullptr)
{
ump.insert({pHead1, pHead1->val});
pHead1 = pHead1->next;
}
while(pHead2 != nullptr)
{
auto res = ump.find(pHead2);
if(res != ump.end())
return true;
pHead2 = pHead2->next;
}
return false;
}

10.相交链表

O(n)

 class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
auto p = headA, q = headB;
while(p != q)
{
if(p == q)
return p;
p = (p == nullptr) ? headB : p->next;
q = (q == nullptr) ? headA : q->next;
}
return p;
}
};

说明:以上其他题目代码由牛客oj/leetcode通过,第9个未测试。

11.删除链表第n个节点(leetcode 19)

O(n)

 class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int len = ;
auto p = head;
while(p != nullptr)//计算表长
{
len++;
p = p->next;
}
if(n > len)//边界处理
return nullptr; len = len-n-;//先走len-n-1步
p = head;
if(len < )//删除头节点
return p->next; while(len--)//删除其他节点
p = p->next;
p->next = p->next->next;
return head;
}
};

the end.

剑指offer-链表相关的更多相关文章

  1. 剑指offer——链表相关问题总结

    首先统一链表的数据结构为: struct ListNode { int val; struct ListNode *next; ListNode(int x) :val(x), next(NULL) ...

  2. 剑指Offer 链表中倒数第k个结点

    题目描述 输入一个链表,输出该链表中倒数第k个结点.     思路: 法1:设置2个指针p,q.p先移动k次,然后pq同时后移,p到链表尾尾的时候,q指向倒数第k个节点. 注意://需要考虑k=0,以 ...

  3. 剑指offer——链表中倒数第k个结点

    输入一个链表,输出该链表中倒数第k个结点. class Solution { public: ListNode* FindKthToTail(ListNode* pListHead, unsigned ...

  4. 剑指Offer——链表中环的入口结点

    题目描述: 一个链表中包含环,请找出该链表的环的入口结点. 分析: 设置两个指针p1,p2, 两个指针都从链表的头部开始走,不过p1每次走一步,p2每次走两步. 直到相遇的时候,p2走的长度是p1的两 ...

  5. 剑指Offer——链表中倒数第k个节点

    Question 输入一个链表,输出该链表中倒数第k个结点. Solution 一种想法就是扫描两边,第一遍求出总的节点个数,第二遍从头开始走n-k个 第二种思想类似于fast-slow指针的方法,f ...

  6. python剑指offer 链表中环的入口节点

    题目: 一个链表中包含环,请找出该链表的环的入口结点. 思路: 先说个定理:两个指针一个fast.一个slow同时从一个链表的头部出发, fast一次走2步,slow一次走一步,如果该链表有环,两个指 ...

  7. acwing 70-72 剑指OFFER 二叉树相关

    地址 https://www.acwing.com/problem/content/66/ https://www.acwing.com/problem/content/67/ https://www ...

  8. 用js刷剑指offer(链表中倒数第k个结点)

    题目描述 输入一个链表,输出该链表中倒数第k个结点. 牛客网链接 思路 设置两个指针,p,q,先让p走k-1步,然后再一起走,直到p为最后一个 时,q即为倒数第k个节点 js代码 // 空间复杂度1 ...

  9. 剑指offer 链表中环的入口位置

    题目描述 一个链表中包含环,请找出该链表的环的入口结点.   思路:这题需要知道a = c,然后head和slow每次走一步,相遇的时候就是第一个入口交点, 注意:for循环或者while循环之后,一 ...

  10. 剑指offer 链表中倒数第K个节点

    利用两个间隔为k的指针来实现倒数第k个 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ...

随机推荐

  1. datax实例——全量、增量同步

    一.全量同步 本文以mysql -> mysql为示例: 本次测试的表为mysql的系统库-sakila中的actor表,由于不支持目的端自动建表,此处预先建立目的表: CREATE TABLE ...

  2. ES6深入浅出-4 迭代器与生成器-5.科班 V.S. 培训

    为什么要学用不到的东西 科班是把你未来一二十年用的东西都给你入个门 做前端 三年后一定要再学一门语言. 买一本图解算法 培训讲究的是技能,只能满足3到5年,而不是术,学术学的是你未来10年甚至20年用 ...

  3. Python - Django - session 的基本使用

    urls.py: from django.conf.urls import url from app02 import views urlpatterns = [ # app02 url(r'^app ...

  4. 代码内存泄露检测(1) MLeaksFinder (Wechat开源) + FBRetainCycleDetector (FaceBook开源)

    每次项目编译完成之后,都被内存搞得头昏脑胀,压力甚大. 利用两周时间,稍微研究了 微信开源的 MLeaksFinder 和 facebook 开源的 FBMemoryProfiler, 这两个开源三方 ...

  5. 原生xgboost中如何输出feature_importance

    网上教程基本都是清一色的使用sklearn版本,此时的XGBClassifier有自带属性feature_importances_,而特征名称可以通过model._Booster.feature_na ...

  6. js多种去重的方法总结

    第一种是比较常规的方法 思路: 1.构建一个新的数组存放结果 2.for循环中每次从原数组中取出一个元素,用这个元素循环与结果数组对比 3.若结果数组中没有该元素,则存到结果数组中 Array.pro ...

  7. SecureCRT系列:生成公私钥

    SecureCRT下载地址:http://www.portablesoft.org/securecrt-securefx-legacy-versions/1.打开我们的SecureCRT客户端,点击t ...

  8. idea使用maven+Tomcat

    1.创建maven项目,并使用webapp骨架,并修改pom.xml文件 <build> <finalName>myWebApp</finalName> <! ...

  9. PAT(B) 1081 检查密码(Java)

    题目链接:1081 检查密码 (15 point(s)) 题目描述 本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能.该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母 ...

  10. 小细节--关于printf的输出问题

    关于printf输出函数,曾经在栈和队列中有过提及,并未作过深入的研究,今天在看一些面经的时候发现有的公司有所提及,那么就势研究一下 面试题如下: 首先看一下printf函数在汇编层面上是如何实现的: ...