💓前言
每日算法练习,千锤百炼,静待花开。
Leetcode的专栏会持续更,因为在跟着英雄哥做知识星球的事儿:在lc被欺负的这些年; 对英雄哥的知识星球有兴趣的可以看看他这篇文章喔: 英雄算法联盟 | 31天让你的算法与众不同 但是英雄哥这儿了,五月不再招人啦,有想法的小伙伴浅等六月嗷~ 单片机是会持续更的,但是硬件看到人比较少吧,或者我理解它不投策,这个更得慢:十四天学会51单片机;
至于现在这个专栏只会记录全部是每日的算法题:知识星球每天的习题,以及在咱高校算法学习社区中,泡泡和p佬普及组和提高组的题目,一般是当天写当天更吧。现在优先写泡泡的题,p佬的有点小把握不住
好朋友执梗正在带新星计划了,有想法的小伙伴不要犹豫嗷 点击查看详情
💓第一题 P1095 [NOIP2007 普及组] 守望者的逃离
线性动态规划
💒题目描述
原题传送门
🌟解题报告
注意题目中的信息,让咱们找到最短时间或者能够跑的最大距离。说人话就是 让咱们找最优解 。再看看这个数据范围,暴力是没法暴力了,老老实实动态规划吧。 这个题可以不用闫式DP分析了吧。因为状态转移比较明显了,但还是浅用一下吧。 闫式DP是从集合的角度来分析动态规划的,主要有状态表示 和状态计算 两个环节。
状态表示 状态表示主要是对用于描述问题的这个集合f 进行定义,以及对这个集合f 最终存放的数据,也就是属性 进行确定。 ——集合:f[i]表示在i这个时间内,可以跑的距离 ——属性:最大值
状态计算 状态计算对应的是集合划分的过程,通常的玩法是选取最后一个不同点,其实也就是看上个状态转移到当前状态有没有不同的地方
对于本题而言,本题是线性DP,当前状态和上个状态分别使用f[i] 和f[i-1] 表示。 从f[i-1] 到f[i] 有三种情况 ——① 剩余魔法值大于等于10,可使用技能,f[i] = f[i-1] + 60 ——② 剩余魔法值小于10,不可使用技能,原地休息,此时没有位移,f[i] = f[i-1] ——③老老实实跑步,f[i] = f[i-1] + 17
将上述文字提炼一下,得到这张闫式DP分析图:
🌻参考代码(C++版本)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int f[N];
int m,s,t;
int main()
{
scanf("%d %d %d",&m,&s,&t);
for(int i =1;i <= t;i++)
{
if(m >= 10)
{
m -= 10;
f[i] = f[i-1] + 60;
}else
{
m += 4;
f[i] = f[i-1];
}
}
for(int i = 1;i <= t;i++) f[i] = max(f[i],f[i-1]+17);
bool isSuccess = false;
for(int i = 0; i <= t;i++)
{
if(f[i] >= s)
{
isSuccess = true;
puts("Yes");
printf("%d\n",i);
break;
}
}
if(!isSuccess)
{
puts("No");
printf("%d\n",f[t]);
}
return 0;
}
💓第二题 1290. 二进制链表转整数
💒题目描述
原题传送门
🌟解题报告
采用的常规模拟的思路,先求出当前链表的长度,再逐一的去取链表中每个结点的数据,把取出来的这位数据,转换成十进制数,累加起来,就是需要的结果。 思路挺简单的,效率差了一点。
🌻参考代码(C++版本)
class Solution {
public:
int getDecimalValue(ListNode* head) {
ListNode* temp = head;
int n = 0;
while(temp)
{
temp = temp->next;
n ++;
}
int ans = 0;
temp = head;
for(int i = n-1;i >= 0; i--)
{
if(temp != nullptr)
{
ans += (temp->val) * pow(2,i);
temp = temp->next;
}
}
return ans;
}
};
💓第三题 237. 删除链表中的节点
覆盖
💒题目描述
原题传送门
🌟解题报告
不是常规思路,这种使用覆盖来实现删除的想法可以浅积累一下。 补充一下常规思路: 常见的玩法是定位到待删除 结点的上一个结点,比如它叫做prenode ,通过修改这个prenode 的next 指针,指向待删除结点的下一个结点,比如叫做nenode ,通过这种方式实现删除。 本题了,传入的参数是要被删除的结点,无法定位到上一个结点。因此不能使用常规的方法来实现了。
现在我们有的是node 结点,我们是可以通过next 指针来使用node 结点之后的数据的,也可以删除node 结点之后的数据。 我们可以删除node 结点的下一个结点,为了出现node被删除的效果 ,可以让node 存储的数据变成其下一个结点存储的数据。
🌻参考代码(C++版本)
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
💓第四题 剑指 Offer II 024. 反转链表
💒题目描述
原题传送门
🌟解题报告
按照下面的小字吧,提供两种解法思路,一种是常规的迭代法 ,一种是递归法 ,其中迭代的效率要高一点。
迭代法可以画图解,但是今天暂时没有时间,后续补上。
🌻参考代码(C++版本)
解法一:迭代法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr) return head;
ListNode* move = head->next;
head->next = nullptr;
while(move)
{
ListNode* tmp = move->next;
move->next = head;
head = move;
move = tmp;
}
return head;
}
};
解法二:递归法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head) return head;
if(head->next == nullptr) return head;
ListNode* ans = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return ans;
}
};
💓第五题 1019. 链表中的下一个更大节点
💒题目描述
原题传送门
🌟解题报告
因为直接操作链表也不太方便,就考虑将数据存储到数组中,直接操作数值吧。 这个题是使用的栈的想法,让当前栈从栈顶到栈底是严格单调递减 的,也就是假如新的元素一旦大于栈顶,就把当前的栈顶元素弹出。
🌻参考代码(C++版本)
class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
vector<int> ans;
ListNode* curr = head;
while (curr != nullptr)
{
ans.push_back(curr->val);
curr = curr->next;
}
int n = ans.size();
vector<int> res(n, 0);
stack<int> stk;
for (int i = 0; i < n; i++)
{
if (stk.empty()) stk.push(i);
else
{
while (!stk.empty() && ans[stk.top()] < ans[i])
{
res[stk.top()] = ans[i];
stk.pop();
}
stk.push(i);
}
}
return res;
}
};
💓 总结
①积累动态规划,因为动归了,有点吃自己的经验 ② 今天链表的题大多数是可以根据题目意思进行模拟的,需要注意空链表的情况
|