本系列笔记供自学算法用,仅记载题解代码和部分题解思路,所有题目版权归LeeCode及《剑指offer》所有;推荐一超棒的刷题笔记系列博客,本系列的题型分类均有所参考:剑指Offer系列刷题笔记汇总
注:题目序号以现官网中排序为准,或和其他参考有所出入;
6.从尾到头输出链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:head = [1,3,2]
输出:[2,3,1]限制:
0 <= 链表长度 <= 10000
解答:
class Solution { //递归
vector<int> res;
public:
vector<int>& reversePrint(ListNode* head) { //注意:返回值为引用,减少大量内存拷贝
if(head == NULL) return res;
reversePrint(head->next);
res.push_back(head->val);
return res;
}
};
class Solution { //数组翻转
public:
vector<int> reversePrint(ListNode* head) {
vector<int> res;
while(head)
{
res.push_back(head->val);
head = head->next;
}
reverse(res.begin(), res.end());
return res;
}
};
class Solution { //使用栈
public:
vector<int> reversePrint(ListNode* head) {
vector<int> res;
stack<int> s;
ListNode* node = head;//不改变原头指针
while(node)
{
s.push(node->val);
node = node->next;
}
while(!s.empty())
{
res.push_back(s.top());
s.pop();
}
return res;
}
};
//作者:zrita
//链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/solution/c-san-chong-fang-fa-z-by-zrita-7ctq/
18.删除链表节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
解答:
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(head->val == val) return head->next;//删除首节点的情况
ListNode *pre = head, *cur = head->next;
while(cur != nullptr && cur->val != val) {
pre = cur;
cur = cur->next;//遍历被删除节点位置
}
if(cur != nullptr) pre->next = cur->next;//删除该节点
return head;
}
};
//作者:jyd
//链接:https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/solution/mian-shi-ti-18-shan-chu-lian-biao-de-jie-dian-sh-2/
22链表倒数第K个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
解答:
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
// 排除输入是空指针和k<=0无意义
if (head == nullptr || k <= 0)
return nullptr;
ListNode* fast = head, * slow = head;
for (int i = 0; i < k; i ++){
// 排除k值大于链表长的情况
if (fast == nullptr)
return nullptr;
fast = fast->next;
}
while (fast != nullptr){
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
//作者:master_xue
//链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/solution/kao-lu-dai-ma-de-lu-bang-xing-cai-shi-be-7c5b/
24.反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解答:
class Solution { //每次改变两个相邻节点的指向
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* pre = NULL;
ListNode* cur = head;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,接下来改变cur->next
cur->next = pre; // 翻转链表方向操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
};
//作者:carlsun-2
//链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/solution/fan-zhuan-lian-biao-shuang-zhi-zhen-fa-d-7stu/
25.合并排序链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 限制: 0 <= 链表长度 <= 1000
解答:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution { //非递归解法
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode(0); //新建链表
ListNode* cur = head;
while (l1 && l2) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
} else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next; //每次合并一位
}
if (l1) cur->next = l1; //最后l2为空的情况
else cur->next = l2; //最后l1为空的情况
return head->next;
}
};
class Solution { //递归解法
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (!l1 || !l2) return !l1 ? l2 : l1; //其中一条链表为空的情况
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
//作者:moreality
//链接:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/solution/c-di-gui-he-fei-di-gui-jie-fa-by-moreali-7f4b/
class Solution { //另一种递归合并法
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
//判断指针是否为空
if(pHead1 == NULL){
return pHead2;
}
else if(pHead2 == NULL){
return pHead1;
}
ListNode* pMergedHead = NULL;
if(pHead1->val < pHead2->val){
pMergedHead = pHead1;
pMergedHead->next = Merge(pHead1->next, pHead2);
}
else{
pMergedHead = pHead2;
pMergedHead->next = Merge(pHead1, pHead2->next);
}
return pMergedHead;
}
};
//来自:https://cuijiahua.com/blog/2017/12/basis_16.html
35.复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]提示:
-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。
解答:
在原链表上复制新节点,在将偶数位节点拆分成新链
在这里插入图片描述
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != nullptr) {
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != nullptr) {
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
// 3. 拆分两链表
cur = head->next;
Node* pre = head, *res = head->next;
while(cur->next != nullptr) {
pre->next = pre->next->next;
cur->next = cur->next->next;
pre = pre->next;
cur = cur->next;
}
pre->next = nullptr; // 单独处理原链表尾节点
return res; // 返回新链表头节点
}
};
//作者:jyd
//链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/jian-zhi-offer-35-fu-za-lian-biao-de-fu-zhi-ha-xi-/
52.两链表的公共节点
输入两个链表,找出它们的第一个公共节点。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
解答:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
//作者:carlsun-2
//链接:https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/solution/dai-ma-sui-xiang-lu-liang-ge-lian-biao-d-i0tx/
附1-链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
解答:
确定环中有几个结点
可以使用快慢指针,一个每次走一步,一个每次走两步。如果两个指针相遇,表明链表中存在环,并且两个指针相遇的结点一定在环中。
随后,我们就从相遇的这个环中结点出发,一边继续向前移动一边计数,当再次回到这个结点时,就可以得到环中结点数目了。如果链表中的环有n个结点,指针P1先在链表上向前移动n步,然后两个指针以相同的速度向前移动。直到它们相遇,当第二个指针指向的入口结点时,第一个指针已经围绕着揍了一圈又回到了入口结点。。它们相遇的结点正好是环的入口结点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == NULL){
return NULL;
}
ListNode* meetingnode = MeetingNode(pHead);
if(meetingnode == NULL){
return NULL;
}
// 回环链表结点个数
int nodesloop = 1;
// 找到环中结点个数
ListNode* pNode1 = meetingnode;
while(pNode1->next != meetingnode){
pNode1 = pNode1->next;
nodesloop++;
}
pNode1 = pHead;
// 第一个指针向前移动nodesloop步
for(int i = 0; i < nodesloop; i++){
pNode1 = pNode1->next;
}
// 两个指针同时移动,找到环入口
ListNode* pNode2 = pHead;
while(pNode1 != pNode2){
pNode1 = pNode1->next;
pNode2 = pNode2->next;
}
return pNode1;
}
private:
// 使用快慢指针,找到任意的一个环中结点
ListNode* MeetingNode(ListNode* pHead){
ListNode* pSlow = pHead->next;
if(pSlow == NULL){
return NULL;
}
ListNode* pFast = pSlow->next;
while(pFast != NULL && pSlow != NULL){
if(pFast == pSlow){
return pFast;
}
pSlow = pSlow->next;
pFast = pFast->next;
if(pFast != NULL){
pFast = pFast->next;
}
}
return NULL;
}
};
//来自:https://cuijiahua.com/blog/2018/01/basis_55.html
附2-删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL){
return NULL;
}
// 指向当前结点前最晚访问过的不重复结点
ListNode* pPre = NULL;
// 指向当前处理的结点
ListNode* pCur = pHead;
// 指向当前结点后面的结点
ListNode* pNext = NULL;
while(pCur != NULL){
// 如果当前结点与下一个结点相同
if(pCur->next != NULL && pCur->val == pCur->next->val){
pNext = pCur->next;
// 找到不重复的最后一个结点位置
while(pNext->next != NULL && pNext->next->val == pCur->val){
pNext = pNext->next;
}
// 如果pCur指向链表中第一个元素,pCur -> ... -> pNext ->...
// 要删除pCur到pNext, 将指向链表第一个元素的指针pHead指向pNext->next。
if(pCur == pHead){
pHead = pNext->next;
}
// 如果pCur不指向链表中第一个元素,pPre -> pCur ->...->pNext ->...
// 要删除pCur到pNext,即pPre->next = pNext->next
else{
pPre->next = pNext->next;
}
// 向前移动
pCur = pNext->next;
}
// 如果当前结点与下一个结点不相同
else{
pPre = pCur;
pCur = pCur->next;
}
}
return pHead;
}
};
//来自:https://cuijiahua.com/blog/2018/01/basis_56.html