起来继续刷题吧
加油QAQ
剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
按照题解写的
第一种递归的方法
我们通过不断判断左右子树在先序和中序遍历中的范围
class Solution {
map<int,int>index;
public:
TreeNode* mybuildTree(vector<int>& preorder, vector<int>& inorder,int preleft,int preright,int inleft,int inright)
{
if(preleft>preright)
return nullptr;
int inroot=index[preorder[preleft]];//定位中序遍历根节点位置preorder[preleft]得到数字,再用index得到位置下标
TreeNode* t=new TreeNode(preorder[preleft]);//先序遍历第一个值为根节点
int size=inroot-inleft;//得到左子树大小
t->left=mybuildTree(preorder,inorder,preleft+1,preleft+size,inleft,inroot-1);
t->right=mybuildTree(preorder,inorder,preleft+1+size,preright,inroot+1,inright);
return t;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n=preorder.size();
for(int i=0;i<n;i++)
index[inorder[i]]=i;
return mybuildTree(preorder,inorder,0,n-1,0,n-1);
}
};
方法二迭代
写完了差不多明白了但是还是有些一知半解其实QAQ
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(!preorder.size())
return nullptr;
stack<TreeNode *>s;
TreeNode* root=new TreeNode(preorder[0]);//第一个为根节点
int index=0;
s.push(root);
for(int i=1;i<preorder.size();i++)
{
TreeNode* t=s.top();
if(t->val!=inorder[index])
{
t->left=new TreeNode(preorder[i]);
s.push(t->left);
}
else
{
while(!s.empty()&&s.top()->val==inorder[index])
{
t=s.top();
s.pop();
index++;
}
t->right=new TreeNode(preorder[i]);
s.push(t->right);
}
}
return root;
}
};
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
偶尔写写水题开心
其实还可以写矩阵乘法的,快速幂
class Solution {
public:
int fib(int n) {
if(n==0)
return 0;
else if(n==1)
return 1;
else
{
int a=0,b=1,t;
n--;
while(n--)
{
t=(b+a)%(1000000007);
a=b;
b=t;
}
return t;
}
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
没思路。。。难道是组合题?
再想想这题是简单题欸
取模应该可以用快速幂
对了这里
前一个状态和后一个状态有关
可以用递推
斐波那契数列
所以这题和上一题类似?
写一个快速幂吧好久不写了
果然找到规律了
f0=1
f1=1
f2=2
f3=4
f4=5
斐波那契数列
class Solution {
public:
vector<vector<int>> mul(vector<vector<int>> a,vector<vector<int>> b)
{
vector<vector<int>> c{{0,0},{0,0}};
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
{
c[i][j] = (int) (((long) a[i][0] * b[0][j] + (long) a[i][1] * b[1][j]) % 1000000007);
}
return c;
}
int quck(int n)
{
vector<vector<int>> a{{1, 1}, {1, 0}};
vector<vector<int>> ret{{1, 0}, {0, 1}};
while(n>0)
{
if(n&1)
{
ret=mul(ret,a);
}
n>>=1;
a=mul(a,a);
}
return (ret[1][0]+ret[1][1])%1000000007;
}
int numWays(int n) {
if(n==0)
return 1;
else
return quck(n);
}
};
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
T~T前几天字节青训营遇到过,天啊我才刚刚理解什么意思,一定是那个青训营题目写的太抽象
其实很简单
我们遍历一下
如果原来单增的
就有a1<a2恒等
我们找到一个a1>a2
那么这个a2肯定是最小值
遍历就ok我写写看
class Solution {
public:
int minArray(vector<int>& numbers) {
int n=numbers.size();
for(int i=0;i<n-1;i++)
{
if(numbers[i]>numbers[i+1])
return numbers[i+1];
}
return numbers[0];
}
};
ok过了
看了其他人写的二分妙呀
写一个二分的吧
可以从51234和34512来思考怎么二分
class Solution {
public:
int minArray(vector<int>& numbers) {
int n=numbers.size();
int left=0,right=n-1;
while(left<right)
{
int mid=(left+right)/2;
if(numbers[mid]<numbers[left])
right=mid;
else if(numbers[mid]>numbers[right])
left=mid+1;
else
right--;
}
return numbers[left];
}
};
剑指 Offer 12. 矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。
接下来连续三个中等题欸
加油
初看题有些像动态规划?
想不出来,先写一个简单的搜索吧
还真的是搜索。。。写了一个深搜
广搜写到一半忘记怎么写了,官方解也是搜索。。。。。
class Solution {
public:
int f=0,len,mp[6][6],n,m;
void dfs(int x,int y,int step,string word,vector<vector<char>>& board)
{
if(step==len)
{
f=1;
return;
}
if(step>len)
return;
if(x-1>=0&&!mp[x-1][y]&&board[x-1][y]==word[step])
{
mp[x-1][y]=1;
dfs(x-1,y,step+1,word,board);
mp[x-1][y]=0;
}
if(x+1<n&&!mp[x+1][y]&&board[x+1][y]==word[step])
{
mp[x+1][y]=1;
dfs(x+1,y,step+1,word,board);
mp[x+1][y]=0;
}
if(y-1>=0&&!mp[x][y-1]&&board[x][y-1]==word[step])
{
mp[x][y-1]=1;
dfs(x,y-1,step+1,word,board);
mp[x][y-1]=0;
}
if(y+1<m&&!mp[x][y+1]&&board[x][y+1]==word[step])
{
mp[x][y+1]=1;
dfs(x,y+1,step+1,word,board);
mp[x][y+1]=0;
}
}
bool exist(vector<vector<char>>& board, string word) {
n=board.size();
m=board[0].size();
memset(mp,0,sizeof(mp));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(board[i][j]==word[0])
{
len=word.length();
mp[i][j]=1;
dfs(i,j,1,word,board);
mp[i][j]=0;
}
}
return f;
}
};
剑指 Offer 14- I. 剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
没思路。。感觉是单纯的数学题有个固定的方程
感觉分成2或者3段的时候有最优解?
最优解必须是均分?
n<=58或许我们可以思考一下58时候的最优解
分成三段191920=7220
分成两段2929=841
四段15151414=44100
猜想?
分成接近10会有最大值
分成五个10一个八的话就是800000
分成8份,七个8+2=4194304
。。。猜想破裂划掉
回到之前的均分有最优解
一共有n/m个
也就是(n/m)^m差不多是这个函数
可以求最大值?单调性?试一下
取对数m=e^(lnn-1)时有最大值
写出来了第一次T~T错了我再写写看
class Solution {
public:
int cuttingRope(int n) {
if(n==2)
return 1;
int m=int(exp((log(n)/log(2.7))-1)+0.5);//计算分m段时最大
if(m==1) m=2;
int a=int((n*1.0/m)+0.5);//这里第一次脑子有些乱写成了0.5
return pow(a,m-1)*(n-a*(m-1));//还有这里是m-1
}
};
还是错了T~T只能看解析了
背包。。。动态规划
一开始的思路错了啊
这题真的神奇emmmmm
终于过了
class Solution {
public:
int cuttingRope(int n) {
int ans[60]={0};
ans[1]=1,ans[2]=1;
for(int i=3;i<=n;i++)
for(int j=1;j<i-1;j++)
ans[i]=max(max(j*(i-j),ans[i-j]*j),ans[i]);
return ans[n];
}
};
1807. 替换字符串中的括号内容
给你一个字符串 s ,它包含一些括号对,每个括号中包含一个 非空 的键。
比方说,字符串 “(name)is(age)yearsold” 中,有 两个 括号对,分别包含键 “name” 和 “age” 。
你知道许多键对应的值,这些关系由二维字符串数组 knowledge 表示,其中 knowledge[i] = [keyi, valuei] ,表示键 keyi 对应的值为 valuei 。
你需要替换 所有 的括号对。当你替换一个括号对,且它包含的键为 keyi 时,你需要:
将 keyi 和括号用对应的值 valuei 替换。
如果从 knowledge 中无法得知某个键对应的值,你需要将 keyi 和括号用问号 “?” 替换(不需要引号)。
knowledge 中每个键最多只会出现一次。s 中不会有嵌套的括号。
请你返回替换 所有 括号对后的结果字符串。
写一下每日一题
第一次尝试用纯模拟
超时了。。应该是可以稍稍优化的
再看看
class Solution {
public:
string evaluate(string s, vector<vector<string>>& knowledge) {
string ans,temp;
int f=0;//f=1开启替换
for(int i=0;i<s.length();i++)
{
if(f==0&&s[i]=='(')
{
temp="";
f=1;
}
else if(f)
{
if(s[i]==')')
{
int f2=1;
for(int j=0;j<knowledge.size();j++)
{
if(temp==knowledge[j][0])
{
ans+=knowledge[j][1];
f2=0;
break;
}
}
if(f2)
ans+="?";
f=0;
}
else
{
temp+=s[i];
}
}
else
ans+=s[i];
}
return ans;
}
};
因为看了给的一个例子
knowledge太多了,线性一个个找超级慢
我们可以用map记录下下表,然后每次查表有无
找到就用下标替换就可以
时间还是太长。。。不过内存居然是用的差不多最少的
我好奇其他解是空间换时间了吗
class Solution {
public:
string evaluate(string s, vector<vector<string>>& knowledge) {
string ans,temp;
map<string,int>mp;
int f=0;//f=1开启替换
for(int j=0;j<knowledge.size();j++)
{
mp[knowledge[j][0]]=j+1;
}
for(int i=0;i<s.length();i++)
{
if(f==0&&s[i]=='(')
{
temp="";
f=1;
}
else if(f)
{
if(s[i]==')')
{
if(mp[temp])
ans+=knowledge[mp[temp]-1][1];
else
ans+="?";
f=0;
}
else
{
temp+=s[i];
}
}
else
ans+=s[i];
}
return ans;
}
};
!其实和官方解很像,天啊我傻了。。。如果能存下标为什么不直接存替换的字符串,其实这样好些算了继续写吧
看了其他语言真的是八仙过海各显神通
剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* p=head,*pre=nullptr,*t;
while(p)
{
t=p;
p=p->next;
t->next=pre;
pre=t;
}
return pre;
}
};
剑指 Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
第一眼看是简单题。。。但是突然发现有好多细节
比如链的顺序,还有数字可以重复
最重要的是random指向的问题
意味着不能一边创建一边random指针指向
所以我们需要将一个链表先创建出来再处理random
yeah!写出来了
链表复杂的是,有时候会用到空指针,还有处理链表的头和尾有些复杂
这题我们可以用map记录下表还有对应节点的指针
class Solution {
public:
Node* copyRandomList(Node* head) {
map<Node*,int>mp1;
map<int,Node*>mp2;
if(head==NULL)
return nullptr;
Node *newhead=new Node(head->val);
Node *p1,*p2,*pre=newhead;
int index=2;
p2=head->next;
mp1[head]=1;
mp2[1]=newhead;
while(p2)
{
mp1[p2]=index;
p1=new Node(p2->val);
mp2[index]=p1;
pre->next=p1;
pre=p1;
p2=p2->next;
index++;
}
pre->next=NULL;
//处理random指针
p2=head;
p1=newhead;
while(p2)
{
if(p2->random==NULL)
p1->random=NULL;
else
p1->random=mp2[mp1[p2->random]];
p2=p2->next;
p1=p1->next;
}
return newhead;
}
};
看了题解。。。原来可以更简单些用map结点和结点对应
学到了==
还有回溯拷贝真的绝
第一次见回溯的用法学习到了
2283. 判断一个数的数字计数是否等于数位的值
给你一个下标从 0 开始长度为 n 的字符串 num ,它只包含数字。
如果对于 每个 0 <= i < n 的下标 i ,都满足数位 i 在 num 中出现了 num[i]次,那么请你返回 true ,否则返回 false 。
写水题休息一下
遍历即可
class Solution {
public:
bool digitCount(string num) {
map<char,int>mp;
for(char i:num)
mp[i]++;
for(int i=0;i<num.length();i++)
if(mp[char(i+'0')]!=num[i]-'0')
return 0;
return 1;
}
};
剑指 Offer 14- II. 剪绳子 II
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
2 <= n <= 1000
动态规划背包好像不太方便了
直接使用数学方法然后不断取余就ok
emmm参考题解写的
class Solution {
public:
int cuttingRope(int n) {
if(n==2)
return 1;
else if(n==3)
return 2;
else if(n==4)
return 4;
else
{
long ans=1;
while(n>4)
{
ans=ans*3%1000000007;
n-=3;
}
return int(ans%1000000007*n%1000000007);
}
}
};
剑指 Offer 15. 二进制中1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。
位运算
遍历
移位运算即可
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans=0;
while(n)
{
if(n&1)
ans++;
n>>=1;
}
return ans;
}
};
剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
emmm感觉有坑啊,幂次这里有负数
稍稍尝试一下快速幂
负数的话先计算正数的时候最后再计算倒数1/x
class Solution {
public:
double myPow(double x, int n) {
int f=0;
double a=x,ans=1;
long num=n;//处理-2^31*-1之后越界了。。。我沉默了直接开大
if(num<0)
{
f=1;
num*=-1;
}
while(num)
{
if(num&1)
ans=a*ans;
a=a*a;
num>>=1;
}
if(f)
return 1.0/ans;
else
return ans;
}
};
剑指 Offer 17. 打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
用logx/log10来判断位数
class Solution {
public:
vector<int> printNumbers(int n) {
int i=1;
vector<int>v;
while((log(i)/log(10))<=n)
{
v.push_back(i);
i++;
}
v.pop_back();
return v;
}
};
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
睡前最后一题水题
好困
睡觉了T~T感觉写最后几题的时候脑子不怎么动了
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode *p,*pre;
if(head->val==val)
return head->next;
p=head->next;
pre=head;
while(p)
{
if(p->val==val)
{
pre->next=p->next;
return head;
}
pre=p;
p=p->next;
}
return head;
}
};