目录
前言
今天在街上发现了一条标语,写着“如果你不敢克服恐惧与担忧,那么你将从来不会知道,没有它们是多么美妙”,也算是被这句话激励了吧,我决定今天开始我害怕的指针相关题目!
剑指 Offer 06. 从尾到头打印链表
题目描述
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
前言
这个题我记得前阵子在力扣上面刷过,但是现在又忘了……既然是用数组返回,那么我就先按顺序存到数组里,然后再反转数组吧!
居然通过了!
天呢,我终于勇敢地进行了链表、指针相关的题目,而且通过这道题,我认识了栈这个知识点,原来栈不需要我自己写出来,在STL容器里已经定义了“栈”这个数据结构。小小地欢呼一下吧
思路
- 方法1 利用栈
将链表中每个结点的值按顺序压入栈中,然后再弹出,储存到vector< int>数组中。
vector<int> reversePrint(ListNode* head)
{
vector<int> result;
stack<int> st;
ListNode* cur = head;
while(cur != NULL)
{
st.push(cur->val);
cur = cur->next;
}
while(!st.empty())
{
result.push_back(st.top());
st.pop();
}
return result;
}
- 方法2 递归
class Solution
{
private:
vector<int> v;
public:
vector<int> reversePrint(ListNode* head)
{
if(head)
{
reversePrint(head->next);
v.push_back(head->val);
}
return v;
}
};
- 方法3
利用vector容器的insert函数
v.insert(v.begin() , p->val)
知识点
stack
stack即C++中的“栈”,是C++STL中一种先进后出(FILO)的容器。
1.stack的定义
stack<typename> name;
2.stack常用函数
- s.push( i ) //将 i 压入栈
- s.top( ) //获取栈顶元素
- s.pop( ) //弹出栈顶元素
- s.empty( ) //检测stack是否为空,返回true为空,返回false为非空
- s.size( ) //返回stack内元素的个数
以上五个函数时间复杂度均为O(1)
剑指 Offer 18. 删除链表的节点
题目描述
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
前言
这道题看起来普普通通,完全没想到我的答案超出时间限制……额仔细检查了一下,原来是忘记写 p = p -> next
思路
解答区有个方法,定义了两个指针,一个指向当前结点,一个指向当前结点的前一个结点。
剑指 Offer 22. 链表中倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
前言
这个常规方法做就可以了,我们首先求出链表的长度 length,然后顺序遍历到链表的第 length−k 个结点返回即可
解答区有个双指针的方法,就是指针1先走k步,然后指针2和指针1再一起走。
class Solution {
public:
//这个方法是方法2
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* fast = head;
ListNode* slow = head;
while (fast && k > 0) {
fast = fast->next;
k--;
}
while (fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
插入语
做到这里我突然认识到,在力扣上面,这个专题里通过率最高的题目,好像就是相对比较容易的题目,那么我就从链表、指针相关题目里最高的开始做好啦!这样多做几道就很有成就感
剑指 Offer 24. 反转链表
题目描述
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
前言
一开始我准备把链表每个结点指向的下一个结点都改变,后来发现太困难了,于是我想到可以通过改变每个结点的值,从而让这条链表记录的值和之前顺序相反,果然通过了。
思路
-
前言中我用的方法(此处略)
-
把链表每个结点指向的下一个结点都改变
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr)
{
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
剑指 Offer 25. 合并两个排序的链表
题目描述
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
前言
这个熟悉的题目,俨然是曾经数据结构讲过的一个经典算法,但是我忘记了,那么就好好回想一下吧。
玩了很长时间手机,心里慌慌张张的,感觉今天学得也太少了,赶紧把这个题努力地回想,反复试错终于做出来了。
易错点
注意到三种容易被忽略的情况
- 链表l1、l2都为空;
- 链表l1为空,l2不为空;
- 链表l2为空,l1不为空
我一开始忽略了这三种情况,直接当做都不为空,导致这种极端用例无法通过。
思路
本题思路有两个
- 迭代,即我用的方法
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while (l1 != nullptr && l2 != nullptr)
{
if (l1->val < l2->val)
{
prev->next = l1;
l1 = l1->next;
}
else
{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
prev->next = l1 == null ? l2 : l1;
return preHead->next;
}
};
- 递归
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr)
{
return l2;
}
else if (l2 == nullptr)
{
return l1;
}
else if (l1->val < l2->val)
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
再见
今天竟然做了5道题,从前想着每天最少五道题,哪知道最多的一天才五道呢!但是这已经是一个巨大的进步,接下来今天就躺床上休息了!