# 前言 终于再次开启了 一周代码总结这个系列 这一次的目标就是 冲击ACM 拿铜牌 希望一年后的结果可以让自己满意
LeetCode每日一题
设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。
当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来 7 天股票的价格是
[100,80,60,70,60,75,85]
,那么股票跨度将是[1,1,1,2,1,4,6]
。实现
StockSpanner
类:
StockSpanner()
初始化类对象。int next(int price)
给出今天的股价price
,返回该股票当日价格的 跨度 。
# 做题思路和 优化思路
根据问题 可以有一个基本思路: 利用数组来做 每一次price来询问的时候 可以从上一个数开始 一个一个的读取 是否小于等于当前的 price 循环停止条件:到第一个数字 或者 当前数字大于price
可以看出来 上述代码的效率很低 当数据量大的时候容易超出时间限制 所以我们需要优化思路
数组并不需要一个一个读数 我们可以利用哈希表来存储每个位置的信息:当前数字之前有多少小于等于自己的 当我们有查询需求的时候 每一次都和一个数字对比 如果当前数字大于price 就终止循环 如果小于等于price 就说明 至少还有 countMap[当前数字] 个连续数字 小于price 于是 res可以直接加上 countMap[当前数字] 并且 下标可以跳到 idx-countMap[当前数字] 和下一个数字继续比较 直到终止循环
# coding
class StockSpanner {
vector<int> arr;
int size;
public:
StockSpanner() {
arr.clear();
size=0;
}
int next(int price) {
int res=1;
if(!arr.empty())
{
int idx=size-1;
while(idx>=0 && arr[idx]<=price)
{
res++;
idx--;
}
}
arr.push_back(price);
size++;
return res;
}
};
//哈希表优化
class StockSpanner {
unordered_map<int,int> countMap;
vector<int> arr;
int size=0;
int lastNum;
public:
StockSpanner() {
countMap.clear();
arr.clear();
}
int next(int price) {
if(size==0)
{
arr.push_back(price);
countMap[0]=1;
size++;
lastNum=price;
return 1;
}
if(price<lastNum)
{
arr.push_back(price);
countMap[size]=1;
lastNum=price;
size++;
return 1;
}
int res=1,idx=size-1;
while(idx>=0 && price>=arr[idx])
{
res+=countMap[idx];
idx-=countMap[idx];
}
lastNum=price;
countMap[size]=res;
arr.push_back(price);
size++;
return res;
}
};
/**
* Your StockSpanner object will be instantiated and called as such:
* StockSpanner* obj = new StockSpanner();
* int param_1 = obj->next(price);
*/
/**
* Your StockSpanner object will be instantiated and called as such:
* StockSpanner* obj = new StockSpanner();
* int param_1 = obj->next(price);
*/
给你一支股票价格的数据流。数据流中每一条记录包含一个 时间戳 和该时间点股票对应的 价格 。
不巧的是,由于股票市场内在的波动性,股票价格记录可能不是按时间顺序到来的。某些情况下,有的记录可能是错的。如果两个有相同时间戳的记录出现在数据流中,前一条记录视为错误记录,后出现的记录 更正 前一条错误的记录。
请你设计一个算法,实现:
- 更新 股票在某一时间戳的股票价格,如果有之前同一时间戳的价格,这一操作将 更正 之前的错误价格。
- 找到当前记录里 最新股票价格 。最新股票价格 定义为时间戳最晚的股票价格。
- 找到当前记录里股票的 最高价格 。
- 找到当前记录里股票的 最低价格 。
请你实现
StockPrice
类:StockPrice()
初始化对象,当前无股票价格记录。void update(int timestamp, int price)
在时间点timestamp
更新股票价格为price
。int current()
返回股票 最新价格 。int maximum()
返回股票 最高价格 。int minimum()
返回股票 最低价格 。
#做题思路 和 优化思路
利用大小根堆来优化时间 在O(N*logN) 的时间复杂度情况下就可以实现最大小值的排序 每次遇到update 就把时间戳和价格包装成pair放入大小根堆 同时利用哈希表来记录最新时间戳的数值 查询最大最小值的时候 可以通过哈希表来查询 当前位置的值是否正确 如果不正确 就把他删掉 正确就返回他
#coding
class StockPrice {
//大小根堆 第二个是小根堆
priority_queue<pair<int,int>,vector<pair<int,int>>> maxQ;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> minQ;
unordered_map<int,int> umap;
int curPrice,curTime=0;
public:
StockPrice() {
umap.clear();
}
void update(int timestamp, int price) {
maxQ.push(make_pair(price,timestamp));
minQ.push(make_pair(price,timestamp));
umap[timestamp]=price;
if(timestamp>=curTime)
{
curPrice=price;
curTime=timestamp;
}
}
int current() {
return curPrice;
}
int maximum() {
cout<<maxQ.top().first<<endl;
while(!maxQ.empty())
{
int price=maxQ.top().first,time=maxQ.top().second;
if(price==umap[time]) break;
maxQ.pop();
}
return maxQ.top().first;
}
int minimum() {
while(!minQ.empty())
{
int price=minQ.top().first,time=minQ.top().second;
if(price==umap[time]) break;
minQ.pop();
}
return minQ.top().first;
}
};
/**
* Your StockPrice object will be instantiated and called as such:
* StockPrice* obj = new StockPrice();
* obj->update(timestamp,price);
* int param_2 = obj->current();
* int param_3 = obj->maximum();
* int param_4 = obj->minimum();
*/
给你一个正整数
num
,请你将它分割成两个非负整数num1
和num2
,满足:
num1
和num2
直接连起来,得到num
各数位的一个排列。
- 换句话说,
num1
和num2
中所有数字出现的次数之和等于num
中所有数字出现的次数。num1
和num2
可以包含前导 0 。请你返回
num1
和num2
可以得到的和的 最小 值。注意:
num
保证没有前导 0 。num1
和num2
中数位顺序可以与num
中数位顺序不同。
#做题思路 和 优化思路
今天的每日一题很简单 就是简单的排序 加 贪心策略 让数字最小就是让 最小的数字尽量的做最高位 这样num1 + num2 的和一定是最小的
#coding
class Solution {
public:
int splitNum(int num) {
int num1=0,num2=0;
vector<int> nums;
while(num)
{
int d=num%10;
nums.push_back(d);
num/=10;
}
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i)
{
if(i%2==0) num1=num1*10+nums[i];
else num2=num2*10+nums[i];
}
return num1+num2;
}
};
有一些机器人分布在一条无限长的数轴上,他们初始坐标用一个下标从 0 开始的整数数组
nums
表示。当你给机器人下达命令时,它们以每秒钟一单位的速度开始移动。给你一个字符串
s
,每个字符按顺序分别表示每个机器人移动的方向。'L'
表示机器人往左或者数轴的负方向移动,'R'
表示机器人往右或者数轴的正方向移动。当两个机器人相撞时,它们开始沿着原本相反的方向移动。
请你返回指令重复执行
d
秒后,所有机器人之间两两距离之和。由于答案可能很大,请你将答案对109 + 7
取余后返回。注意:
- 对于坐标在
i
和j
的两个机器人,(i,j)
和(j,i)
视为相同的坐标对。也就是说,机器人视为无差别的。- 当机器人相撞时,它们 立即改变 它们的前进方向,这个过程不消耗任何时间。
当两个机器人在同一时刻占据相同的位置时,就会相撞。
例如,如果一个机器人位于位置 0 并往右移动,另一个机器人位于位置 2 并往左移动,下一秒,它们都将占据位置 1,并改变方向。再下一秒钟后,第一个机器人位于位置 0 并往左移动,而另一个机器人位于位置 2 并往右移动。
例如,如果一个机器人位于位置 0 并往右移动,另一个机器人位于位置 1 并往左移动,下一秒,第一个机器人位于位置 0 并往左行驶,而另一个机器人位于位置 1 并往右移动。
#解题思路和优化思路
这道题属于思维题目 两个机器人碰撞后都反转方向 相当于两个机器人互相穿透了身体 所以我们只需要计算每个机器人的我最后位置 第二点就是 知道了n个点的坐标 如何计算他们的两两之间的和 我们可以用到前缀和的思想 我们把数组排序后 可以发现每两个点之间的差值 会被用 i*(n-i)次 可以画图来简单证明
#coding
class Solution {
const int mod=1e9+7;
public:
int sumDistance(vector<int>& nums, string s, int d) {
vector<long long> pos(nums.size());
for(int i=0;i<nums.size();++i)
{
if(s[i]=='L') pos[i]=(long long) nums[i]-d;
else pos[i]=(long long) nums[i]+d;
}
sort(pos.begin(),pos.end());
long long res=0;
for(int i=1;i<pos.size();++i)
{
res+=(pos[i]-pos[i-1])*i%mod*(pos.size()-i)%mod;
res%=mod;
}
return res;
}
};
LeetCode 周赛优选题目
给你一个下标从 0 开始的整数数组
nums
。请你从所有满足
i < j < k
的下标三元组(i, j, k)
中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回0
。下标三元组
(i, j, k)
的值等于(nums[i] - nums[j]) * nums[k]
。
#做题思路 和 优化思路
这道题根据题意 首先想到的就是 三重枚举 分别取 i j k 枚举 然后取最大值 但时间复杂度O() 数据量大的时候毫无疑问会超时 我们需要想办法 来减少枚举的次数 从而达到满足时间复杂度 引入后缀最大数组(从后到前依次枚举数组 将最大值填入当前位置 suffMax[i]=max(suffMax[i+1],nums[i]) 从而达到来到每一个点 都知道 后面的最大值是多少 由于nums数组是非负数数组 所以可以引入一个preMax 变量 来记录前面的最大值 这样 只需要枚举中间数字 就可以实现题目要求 时间复杂度 为 O(n) 大大的降低了时间复杂度
#coding
//暴力解法
class Solution {
public:
long long maximumTripletValue(vector<int>& nums) {
long long res=0;
for(int i=0;i<nums.size();++i)
{
for(int j=i+1;j<nums.size();++j)
{
for(int k=j+1;k<nums.size();++k)
{
res=(long long)max(res,(long long)(nums[i] - nums[j]) * nums[k]);
}
}
}
return res;
}
};
//后缀数组优化
class Solution {
public:
long long maximumTripletValue(vector<int>& nums) {
int n=nums.size();
vector<int> suffMax(n+1,0);
for(int i=n-1;i>0;--i)
{
suffMax[i]=max(suffMax[i+1],nums[i]);
}
long long res=0;
int preMax=nums[0];
for(int i=1;i<nums.size();++i)
{
res=max(res,(long long)(preMax-nums[i])*suffMax[i+1]);
preMax=max(preMax,nums[i]);
}
return res;
}
};
给你一个下标从 0 开始的数组
nums
和一个整数target
。下标从 0 开始的数组
infinite_nums
是通过无限地将 nums 的元素追加到自己之后生成的。请你从
infinite_nums
中找出满足 元素和 等于target
的 最短 子数组,并返回该子数组的长度。如果不存在满足条件的子数组,返回-1
。
#解题思路 和 优化思路
看到题目就知道这是一道数学题 分类讨论一下 如果 target 比较小 即小于数组的值 则有 target=rest 如果target 比较大 就有 target=n*sum+rest 其中·sum 是 nums的数组和 rest 是 target%sum 所以 问题就转化成了 找到 最小的 rest值 的子数组 由于 rest=target%sum < sum 所以只需要 2 个 nums 数组加一起就可以完成要求 解决如何找到最短长度子数组和等于rest 引入滑动窗口来解决问题 两个下标 left 和 right 来控制窗口的大小 并且窗口的和和 rest 作比较 从而实现题目的要求
# coding
class Solution {
public:
int minSizeSubarray(vector<int>& nums, int target) {
int sum=accumulate(nums.begin(),nums.end(),0LL);
int rest=target%sum;
int minLen=INT_MAX;
int left=0,n=nums.size();
long long curSum=0;
for(int right=0;right<2*n;++right)
{
curSum+=nums[right%n];
while(curSum>rest) curSum-=nums[left++%n];
if(curSum==rest) minLen=min(minLen,right-left+1);
}
return minLen==INT_MAX?-1:minLen+target/sum*n;
}
};
给你两个正整数
n
和m
。现定义两个整数
num1
和num2
,如下所示:
num1
:范围[1, n]
内所有 无法被m
整除 的整数之和。num2
:范围[1, n]
内所有 能够被m
整除 的整数之和。返回整数
num1 - num2
。
#解题思路 和 优化思路
题目很简单 但是有很多的优化方法 例如类似质数筛的方法可以加快求num2 以及高斯求和法 加速求num1 使得整个算法时间复杂度为O(1)
#coding
class Solution {
public:
int differenceOfSums(int n, int m) {
int k=1;
int num2=0;
while(k*m<=n)
{
num2+=k*m;
k++;
}
int num1=(n+1)*n/2-num2;
return num1-num2;
}
};
你有
n
颗处理器,每颗处理器都有4
个核心。现有n * 4
个待执行任务,每个核心只执行 一个 任务。给你一个下标从 0 开始的整数数组
processorTime
,表示每颗处理器最早空闲时间。另给你一个下标从 0 开始的整数数组tasks
,表示执行每个任务所需的时间。返回所有任务都执行完毕需要的 最小时间 。注意:每个核心独立执行任务。
# 解题思路 和 优化思路
这道题的思路是 贪心算法 最快的机子去解决时间最久的活 贪心下来就是答案
# coding
class Solution {
public:
int minProcessingTime(vector<int>& processorTime, vector<int>& tasks) {
sort(processorTime.begin(),processorTime.end());
sort(tasks.rbegin(),tasks.rend());
int index=0;
int maxTime=0;
for(int i=0;i<processorTime.size();++i)
{
for(int j=0;j<4;++j)
{
maxTime=max(maxTime,processorTime[i]+tasks[index]);
index++;
}
}
return maxTime;
}
};
给你一个下标从 0 开始的正整数数组
nums
。你可以对数组执行以下两种操作 任意次 :
- 从数组中选择 两个 值 相等 的元素,并将它们从数组中 删除 。
- 从数组中选择 三个 值 相等 的元素,并将它们从数组中 删除 。
请你返回使数组为空的 最少 操作次数,如果无法达成,请返回
-1
。
# 解题思路 和 优化思路
这道题是一道数学题 知道 2 和 3 的最小公倍数是6 并且我们可以把相同的数字用哈希表来统计个数 方便计算 算法设计 先将每个数字出现的个数变成 k*2+rest 其中rest是num%2的结果 这样就通过计算来简化算法 我们知道 2*3 == 3*2 即 每3次2个相同的数字消除 可以用 2次3个相同数字消除即每六个相同的数字 用3个相同的比2个相同的少一次 由此我们可以算一下 num%6的值 设为a 即将6个的全都转化成2次3个的 就可以算出来最终的结果 是 k-a 但是有一种特殊情况 即 2*k%6==0 并且 rest==1 这种情况 不能单独的用2变成3 只能拆掉一组 来凑 2 2 3 因此会多一次
# coding
class Solution {
public:
int minOperations(vector<int>& nums) {
unordered_map<int,int> umap;
int res=0;
for(int i=0;i<nums.size();++i)
{
umap[nums[i]]++;
}
for(auto& i : umap)
{
int num=i.second;
if(num==1) return -1;
int k=num/2,rest=num%2;
int sub=num/6;
if(2*k%6==0 && rest) k++;
k-=sub;
res+=k;
}
return res;
}
};
给你一个只包含 非负 整数的数组
nums
。我们定义满足
l <= r
的子数组nums[l..r]
的分数为nums[l] AND nums[l + 1] AND ... AND nums[r]
,其中 AND 是按位与运算。请你将数组分割成一个或者更多子数组,满足:
- 每个 元素都 只 属于一个子数组。
- 子数组分数之和尽可能 小 。
请你在满足以上要求的条件下,返回 最多 可以得到多少个子数组。
一个 子数组 是一个数组中一段连续的元素。
#解题思路 和 优化思路
解题思路很巧妙 分情况讨论 #1 整个与值不是0 设为a 那么每一段与值都不小于a 则总体和一定大于a 这种情况不合理 所以他只能分成一组 #2 整个与值是0 则最多的情况就是从左到右 只要与值和为0 就分割
#coding
class Solution {
public:
int maxSubarrays(vector<int>& nums) {
int res=0;
int cur=-1;
for(int i=0;i<nums.size();++i)
{
cur&=nums[i];
if(cur==0)
{
cur=-1;
res++;
}
}
return max(res,1);
}
};
给你一棵
n
个节点的无向树,节点编号为0
到n - 1
。给你整数n
和一个长度为n - 1
的二维整数数组edges
,其中edges[i] = [ai, bi]
表示树中节点ai
和bi
有一条边。同时给你一个下标从 0 开始长度为
n
的整数数组values
,其中values[i]
是第i
个节点的 值 。再给你一个整数k
。你可以从树中删除一些边,也可以一条边也不删,得到若干连通块。一个 连通块的值 定义为连通块中所有节点值之和。如果所有连通块的值都可以被
k
整除,那么我们说这是一个 合法分割 。请你返回所有合法分割中,连通块数目的最大值 。
#解题思路 和 优化思路
先解释树的定义 树的边和点数的关系是 边=点-1 而且树是一个连通图 且对于任何的点 x y 有且仅有一条路 使得x y联通 并且题目已知条件是%k==0 所以我们可以随便挑一点进行深度优先遍历 回溯的时候计算到子树和%k==0 就可以去掉这条边 最后回溯到 x就是答案
#coding
class Solution {
public:
int maxKDivisibleComponents(int n, vector<vector<int>>& edges, vector<int>& values, int k) {
vector<vector<int>> g(n);
for (auto &e : edges)
{
int x = e[0], y = e[1];
g[x].push_back(y);
g[y].push_back(x);
}
int ans = 0;
function<long long(int, int)> dfs = [&](int x, int fa) -> long long {
long long sum = values[x];
for (int y : g[x])
{
if (y != fa) sum += dfs(y, x);
}
ans += sum % k ==0;
return sum;
};
dfs(0, -1);
return ans;
}
};
专项类型训练(链表 栈 图论)
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
#解题思路 和 优化思路
链表的相加相减 如果原地算法 会变得非常的麻烦 所以一般的解法都是自己做一个新的链表头 ListNode* dummy = new ListNode() 这样就会简化非常多的问题 然后就是深入的看这道题 加法就会涉及到一个问题 进位问题 所以我们引入一个变量 carry 来记录 两个链表相加后的 进位值 最后就是注意链表的边界条件 和 终止条件 不要然链表变成NULL 后继续使用 会造成内存错误
#coding
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int carry=0;
ListNode* dummy=new ListNode(0);
ListNode* p=dummy;
//注意边界条件
while(carry || l1 || l2)
{
int val1=l1?l1->val:0;
int val2=l2?l2->val:0;
int val=(carry+val1+val2)%10;
carry=(carry+val1+val2)/10;
ListNode* node=new ListNode(val);
p->next=node;
p=node;
if(l1) l1=l1->next;
if(l2) l2=l2->next;
}
return dummy->next;
}
};
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。
#解题思路和优化思路
这道题用到一个非常经典的算法结构快慢指针 既然求倒数第k个节点位置的数值 我们可以先让快指针先走k步 最后再让快慢指针一起走 直到快指针到NULL节点终止 此时 慢指针的位置就是倒数第k个节点位置 然后就是怎么删掉指针 我们需要引入一个新的指针 Pre指针 初始化指向NULL 每次slow走前 pre指向slow的位置 这样就实现了 pre一直在slow的上一个位置节点 最后用 pre->next=slow->next 实现删除slow节点
#coding
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* fast=head,* slow=head;
ListNode* pre=NULL;
//快指针先走k步
for(int i=0;i<n;++i)
{
if(fast->next==NULL) return head->next;
fast=fast->next;
}
//快慢指针一起走 直到快指针到NULL节点终止
while(fast)
{
fast=fast->next;
pre=slow;
slow=slow->next;
}
pre->next=slow->next;
delete slow;
return head;
}
};
给定一个已排序的链表的头
head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
#解题思路和优化思路
这道题就很简单 利用 哈希表来解答就好了 统计每个数字出现的次数 只让出现1次的数字出现在链表上
#coding
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* dummy=new ListNode();
ListNode* p=dummy,*p1=head;
unordered_map<int,int> umap;//来记录每个数字出现的次数
while(p1)
{
umap[p1->val]++;
p1=p1->next;
}
p1=head;
while(p1)
{
if(umap[p1->val]==1)
{
ListNode* tmp=new ListNode(p1->val);
p->next=tmp;
p=tmp;
}
p1=p1->next;
}
return dummy->next;
}
};
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
#解题思路和优化思路
这道题的解题思路和反转链表特别特别相似 用next指针来记录当前指针的下一个的下一个节点next 然后 当前指针和 当前指针的下一个 交换节点 再让指针来到next位置 循环上述过程 同时用一个pre指针 来 抓主之前位置的链表节点 用pre->next=swapNode 实现链表的连接
#coding
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* p=head,* pre=NULL;
while(p)
{
ListNode* swapNode=p->next;
if(swapNode==NULL) break;
if(p==head) head=swapNode;
ListNode* next=swapNode->next;
if(pre!=NULL) pre->next=swapNode;
pre=p;
swapNode->next=p;
p->next=next;
p=next;
}
return head;
}
};
设计一个支持
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。实现
MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
#解题思路和优化思路
这道题的解题思路就是如何去完成最小值的记录 我使用了经典的算法方法 最小栈 栈只有不比自己小的数字在可以进来当栈顶 这样就可以实现 在栈顶被弹出的时候 可以保证他是整个栈的最小数 但是只有这一个栈还是不够 因为我们还需要统计什么时候弹出栈顶元素 所以还需要一个记录每个栈上面有几个数字 这样子就可以在上面元素弹完后在弹出栈顶元素
#coding
class MinStack {
stack<int> minStack;
stack<int> countStack;
stack<int> s;
int k=0;
public:
MinStack() {
}
void push(int val) {
s.push(val);
if(minStack.empty() || minStack.top()>=val)
{
if(!minStack.empty()) countStack.push(k);
minStack.push(val);
k=0;
}
else k++;
}
void pop() {
if(k) k--;
else
{
k=countStack.top();
if(!minStack.empty()) minStack.pop();
}
if(!s.empty()) s.pop();
}
int top() {
return s.top();
}
int getMin() {
return minStack.top();
}
};
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
#解题思路和优化思路
这道题就相较于前面那道题简单许多就是 代码方面需要抓细节 很简单的一道题
#coding
class Solution {
stack<int> st;
bool isDigit(string s)
{
if(s.size()==1 && (s[0]<'0' || s[0]>'9')) return false;
return true;
}
public:
int evalRPN(vector<string>& tokens) {
for(int i=0;i<tokens.size();++i)
{
if(isDigit(tokens[i])) st.push(stoi(tokens[i]));
else
{
int num1=st.top();
st.pop();
int num2=st.top();
st.pop();
if(tokens[i]=="+") num2+=num1;
if(tokens[i]=="-") num2-=num1;
if(tokens[i]=="*") num2*=num1;
if(tokens[i]=="/") num2/=num1;
st.push(num2);
}
}
return st.top();
}
};
给定一个单链表
L
的头节点head
,单链表L
表示为:L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
#解题思路和优化思路
和24题非常的像 不一样的地方就是 swapNode 不再是 p->next 而是 s.top() 思路很简单的一道题
#coding
class Solution {
public:
void reorderList(ListNode* head) {
stack<int> s;
queue<int> q;
ListNode* dummy=new ListNode();
ListNode* p=head;
while(p)
{
s.push(p->val);
q.push(p->val);
p=p->next;
}
p=head;
while(p)
{
p->val=q.front();
q.pop();
p=p->next;
if(p)
{
p->val=s.top();
s.pop();
p=p->next;
}
}
}
};
给你二叉树的根结点
root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
#解题思路和优化思路
二叉树类型的题目 核心思想还是栈的思想 根据题目意思 我们知道他需要将一颗二叉树转换变成一个只有右树没有左树的"链表" 解题思路是 我们需要通过 "右 左 中" 的遍历方式来遍历这颗二叉树 原因: 当我们改变左树结构的时候 会把root->right的部分 直接放到左树的right位置 所以我们需要先让 root->right先排好位置 其次就是 每一层的节点 都需要自己下面完全排序好 当前节点的操作是 将左树移植到右树上面 所以确定好遍历顺序后就可以考虑如何才能完成每一层的操作 最底一层很容易想到 最多就是 把左子树的单节点放到右子树上 但对于 左子树 右子树 都大于 1 层 这样的想法就是不正确的 所以我们需要一个node节点 来遍历这棵树的左子树 让他到达叶节点位置 再将叶节点与右子树连接 从而实现题目的要求 最后就是 代码 的细节问题 注意到就可以了
#coding
class Solution {
public:
void flatten(TreeNode* root) {
if(root==NULL) return;
flatten(root->right);
flatten(root->left);
if(root->left)
{
TreeNode* right=root->right;
TreeNode* node=root->left;
while(node->right) node=node->right;
node->right=right;
root->right=root->left;
root->left=NULL;
}
}
};
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值
val
(int
) 和其邻居的列表(list[Node]
)。class Node { public int val; public List<Node> neighbors; }
#解题思路和优化思路
这道题主要的思路是 深度拷贝一个图 图有两个重要因素 val 和 neighbor 所以map来记录cloneNode 同时递归的建立Node的邻接点
#coding
class Solution {
public:
unordered_map<Node*, Node*> visited;
Node* cloneGraph(Node* node)
{
if (node == nullptr) return node;
if (visited.find(node) != visited.end()) return visited[node];
Node* cloneNode = new Node(node->val);
visited[node] = cloneNode;
for (auto& neighbor: node->neighbors)
{
cloneNode->neighbors.emplace_back(cloneGraph(neighbor));
}
return cloneNode;
}
};