剑指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. osg Node getParentalNodePaths()报错

    node->getBound().center() * osg::computeLocalToWorld(node->getParentalNodePaths()[0]) osg::Vec ...

  2. 重启WMS服务

    一.重启API服务 查看进程ps ef|grep java 进入目录 cd /usr/local/tomcat-api/bin ./shutdown.sh ps –ef|grep 查看服务是否真的停止 ...

  3. k8s、jenkins集成

    参考连接 http://www.uml.org.cn/jchgj/201804252.asp https://huanqiang.wang/2018/03/30/Jenkins-Gitlab-Kube ...

  4. sem_wait sem_post信号量操作进本函数

    sem_wait   sem_post 信号量的数据类型为结构sem_t,它本质上是一个长整型的数.函数sem_init()用来初始化一个信号量.它的原型为: extern int sem_init ...

  5. windows添加“以管理员身份运行”

    方法: 新建一个txt文件,命名为"admin.txt",记得打开"显示后缀名",要求看到.txt并可修改之. 将下列代码粘贴进去. Windows Regis ...

  6. 6.2.3 reginst中的yzm

    @RequestMapping("/reginst/{yzm}") public Object reginst(User user,@PathVariable String yzm ...

  7. LeetCode 64. 最小路径和(Minimum Path Sum) 20

    64. 最小路径和 64. Minimum Path Sum 题目描述 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小. 说明: 每次只能向下或 ...

  8. [转帖]JAVA虚拟机和安卓虚拟机的区别

    作者:天光链接:https://www.zhihu.com/question/20207106/answer/14654536来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...

  9. composer 无法配置命令行写入配置文件问题

    composer config repo.packagist composer https://packagist.phpcomposer.com 这条命令无法修改composer.json 添加中国 ...

  10. Python06之分支和循环1(三目运算符)

    Python 为了使得程序更加简洁而新引入过来的一个三目操作符,顾名思义就是有三个参数. 格式: x if 条件表达式 else y 先判断条件表达式真假,真则取 x 的值,否则取 y 的值. 例如: ...