剑指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. VS2015 dlib编译 x64 Release .lib生成

    VS2015 dlib编译 x64 Release >------ 已启动生成: 项目: ZERO_CHECK, 配置: Release x64 ------ > Checking Bui ...

  2. 内存检测工具valgrind的安装和简单使用

    1. 安装 .tar.bz2 cd valgrind- sudo ./configure sudo make sudo make install 2. 简单使用 #include <stdio. ...

  3. ES6深入浅出-11 ES6新增的API(上)-1.Object.assign

    这些都是es6才有的 Object.assign 在a加上三个属性 分别是p1\p2\p3 以前是这么去加 b的三个属性p1.p2.p3就全部复制到a这个对象上了. 把后面的东西放到前面的东西上 两个 ...

  4. Swift细节记录<一>

    1.全局变量记录: import UIKit class HHTSwitchGlobalData: NSObject { var isWaiterAutoPop: Bool = true privat ...

  5. 原生Js页面滚动延迟加载图片

    原理和过程1.页面滚动加载事件2.获取元素在页面里的top值 根据滚动条的位置 判断何时显示图片3.获取元素集合 加载过的图片从集合里删除 效果预览:http://jsfiddle.net/dtdxr ...

  6. 06点睛Spring4.1-Bean的初始化和销毁

    6.1 Initialization和Destruction spring对bean初始化的时候和销毁时候进行某些操作提供了支持 利用@Bean的initMethod和destroyMethod(和x ...

  7. Brave浏览器

    Brave是基于Chromium的开源Web浏览器,具有更快的页面加载速度,并且默认情况下会阻止广告和跟踪器.整合了一些其它浏览器所没有的功能,其中包括被称为 Basic Attention Toke ...

  8. Centos7.0操作系统加固常见方法

    1. 账号和口令 1.1 禁用或删除无用账号 减少系统无用账号,降低安全风险. 操作步骤 使用命令 userdel <用户名> 删除不必要的账号. 使用命令 passwd -l <用 ...

  9. ffmpeg.编译(20191129)

    1.一步步实现windows版ijkplayer系列文章之一——Windows10平台编译ffmpeg 4.0.2,生成ffplay - HarlanC - 博客园.html(https://www. ...

  10. tracert详解

    1tracert作用 是用于探索源地址到目标地址当中所经过的路线.而每到达一个点,就会向源地址返回一个信号.例如A要访问D,那么当中经过B,再经过C.当经过B时,会向A返回一个信号,当经过C时,再向A ...