概述
目录
剑指 Offer 41. 数据流中的中位数
剑指 Offer 42. 连续子数组的最大和
剑指 Offer 43. 1~n 整数中 1 出现的次数
剑指 Offer 44. 数字序列中某一位的数字
剑指 Offer 45. 把数组排成最小的数
剑指 Offer 46. 把数字翻译成字符串
剑指 Offer 47. 礼物的最大价值
剑指 Offer 48. 最长不含重复字符的子字符串
剑指 Offer 49. 丑数
剑指 Offer 50. 第一个只出现一次的字符
剑指 Offer 52. 两个链表的第一个公共节点
剑指 Offer 53 - I. 在排序数组中查找数字 I
剑指 Offer 53 - II. 0~n-1中缺失的数字
剑指 Offer 54. 二叉搜索树的第k大节点
剑指 Offer 55 - I. 二叉树的深度
剑指 Offer 55 - II. 平衡二叉树
剑指 Offer 56 - I. 数组中数字出现的次数
剑指 Offer 56 - II. 数组中数字出现的次数 II
剑指 Offer 57. 和为s的两个数字
剑指 Offer 57 - II. 和为s的连续正数序列
剑指 Offer 58 - I. 翻转单词顺序
剑指 Offer 58 - II. 左旋转字符串
剑指 Offer 59 - I. 滑动窗口的最大值
剑指 Offer 59 - II. 队列的最大值
剑指 Offer 61. 扑克牌中的顺子
剑指 Offer 62. 圆圈中最后剩下的数字
剑指 Offer 63. 股票的最大利润
剑指 Offer 64. 求1+2+…+n
剑指 Offer 65. 不用加减乘除做加法
剑指 Offer 66. 构建乘积数组
剑指 Offer 67. 把字符串转换成整数
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
剑指 Offer 68 - II. 二叉树的最近公共祖先
剑指 Offer 41. 数据流中的中位数
题解:
代码:
结果:
剑指 Offer 42. 连续子数组的最大和
题解:
首先,通过一个循环遍历数组,我们需要保证当前位置的数值前面的数是正数,才值得累加,否则就以当前位置作为连续数组的起始点。
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size()==1) return nums[0];
int pre=nums[0];
int maxv=pre;
for(int i=1;i<nums.size();++i){
if(pre<0)
pre=nums[i];
else
pre+=nums[i];
maxv=max(maxv,pre);
}
return maxv;
}
};
结果:
剑指 Offer 43. 1~n 整数中 1 出现的次数
解:
代码:
结果:
剑指 Offer 44. 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
题解:找规律
数字个数和数位个数之间可以表示成:
0~9:10 10~99:2x10x9 100~999:3x10^2x9 10000~9999:4x10^3x9.........
所以,我们可以依次从左往右找,先定位到一个区间,再定位到一个数,再定位到数中的位数。
代码:
class Solution {
public:
int findNthDigit(int n) {
if(n<=9) return n;
long count=9;
long start=0;
int i=1;
while(n>count){
n-=count;
start=pow(10,i);
++i;
count=i*9*start;
}
start=start+(n-1)/i;
return to_string(start).at((n-1)%i)-'0';
}
};
结果:
剑指 Offer 45. 把数组排成最小的数
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
题解:排序
这题的精髓在于compare函数,字符串排序,先比较首个字符,如果相等再比较第二个字符,依次下去。所以,3肯定会排在30前面,但是我们最后得到的数330是大于303的,这样不可取,需要将30排再3前面。
所以可以通过a+b<b+a的比较,让30排在3的前面。
代码:
class Solution {
public:
static bool compare(const string &a,const string &b){
return a+b<b+a;
}
string minNumber(vector<int>& nums) {
string str;
vector<string> svec;
for(auto i:nums)
svec.push_back(to_string(i));
sort(svec.begin(),svec.end(),compare);
for(auto i:svec)
str+=i;
return str;
}
};
结果:
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法
题解:动态规划
这题的思路和青蛙跳台阶是一样的。
这边用从低位到高位的思路,用两个整型dp1和dp2存储可翻译的结果个数。
举个例子,如12258这个数,①8,只有一种翻译结果,dp1=1,dp2=0,②5,只有一种翻译结果,dp1=1,dp2=1,③2,和5组成25为一种,单独翻译为一种,再根据dp1和dp2,有两种,dp1=2,dp2=1。④2,同③,dp1=2+1=3,dp2=dp1=2。⑤1同上,dp1=2+3=5,dp2=dp1=3。
最后输出dp1。
代码:
class Solution {
public:
int translateNum(int num) {
int dp1=1,dp2=0;
int nums=0,prenums=9;
while(num!=0){
int temp=0;
nums=num%10;
if(nums==1||(nums==2&&prenums<6))
temp=dp1+dp2;
else
temp=dp1;
dp2=dp1;
dp1=temp;
prenums=nums;
num/=10;
}
return dp1;
}
};
结果:
剑指 Offer 47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
题解:动态规划
首先,排除特殊情况,矩阵长度为0的时候。
然后,先将矩阵的行列计算完,因为行和列的值只能由它左边那个数或者上面那个数决定。
最后,遍历剩余的矩阵,判断当前位置的上一位和左一位哪个值更大,进行累加求和。
返回矩阵最右小的角的元素。
代码:
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
if(grid.size()==0) return 0;
int m=grid.size();
int n=grid[0].size();
for(int i=1;i<m;++i)
grid[i][0]+=grid[i-1][0];
for(int i=1;i<n;++i)
grid[0][i]+=grid[0][i-1];
for(int i=1;i<m;++i)
for(int j=1;j<n;++j)
grid[i][j]+=max(grid[i-1][j],grid[i][j-1]);
return grid[m-1][n-1];
}
};
结果:
剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
题解:双指针
设置一左一右两个指针,右指针用于遍历数组,左指针用于判断当前右指针指向的数是否在子串中出现过。
值得注意的是,如果出现字符串为“aaaa”的情况,那么最大子字符串的长度是1不是0。
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int maxlength=0;
int nlength=0;
int temp=0,left;
for(int right=0;right<s.size();++right){
for(left=temp;left<right;++left){
if(s[left]==s[right]){
temp=left+1;
break;
}
}
nlength=(right==temp)?1:right-temp+1;
maxlength=(nlength>maxlength)?nlength:maxlength;
}
return maxlength;
}
};
结果:
剑指 Offer 49. 丑数
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数
题解:动态规划
一个丑数可以通过另一个丑数 * 2 3 5得到。
所以可以用动态规划来做!
代码:
class Solution {
public:
int Min(int a,int b,int c){
int temp=a<b?a:b;
return temp<c?temp:c;
}
int nthUglyNumber(int n) {
if(n==1) return 1;
vector<int>dp(n);
dp[0]=1;
int num2=0;
int num3=0;
int num5=0;
for(int i=1;i<n;++i){
dp[i]=Min(dp[num2]*2,dp[num3]*3,dp[num5]*5);
while(dp[num2]*2==dp[i]) ++num2;
while(dp[num3]*3==dp[i]) ++num3;
while(dp[num5]*5==dp[i]) ++num5;
}
return dp[n-1];
}
};
结果:
剑指 Offer 50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
题解:数组记录
首先,开辟一个长度为26的数组。
第一遍,将字符串s中的字符出现的次数记录下来。
第二遍,遍历字符串,从数组中找到第一个值为1的数,其下标就是要求的数。
代码:
class Solution {
public:
char firstUniqChar(string s) {
vector<int>vec(26);
for(auto c:s) ++vec[c-'a'];
for(auto c:s)
if(vec[c-'a']==1)
return c;
return ' ';
}
};
结果:
剑指 Offer 52. 两个链表的第一个公共节点
题解:
浪漫相遇
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//听闻远方有你
ListNode *l1=headB;
ListNode *l2=headA;
//动身跋涉千里
while(l1!=l2){
//我吹过你吹过的风
l2= l2==nullptr?headA:l2->next;
//你走过我走过的路
l1= l1==nullptr?headB:l1->next;
}
//我们终会相逢
return l1;
}
};
结果:
剑指 Offer 53 - I. 在排序数组中查找数字 I
题解:二分查找
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size()==0) return 0;
int res=0;
int left=0,right=nums.size()-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]<target)
left=mid+1;
else if(nums[mid]>target)
right=mid-1;
else{
int i=mid;
++res;
while(i>=0&&i<nums.size()-1)
if(nums[++i]==target) ++res;
i=mid;
while(i>0&&i<=nums.size()-1)
if(nums[--i]==target) ++res;
return res;
}
}
return 0;
}
};
结果:
剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字
题解:二分法
这题用遍历可能好做,但时间复杂度太高了。
用二分法,如果mid等于nums[mid],说明mid之前都是顺序的,缺失的在mid之后,left=mid+1;
如果mid不等于nums[mid],说明缺失的在mid之前,right=mid-1;循环,最后得到的值就是缺失的。
代码:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]==mid)
left=mid+1;
else
right=mid-1;
}
return left;
}
};
结果:
剑指 Offer 54. 二叉搜索树的第k大节点
题解:后续遍历
因为是搜索二叉树,所以可以用右根左的方式进行遍历查找。
代码:
class Solution {
public:
int res=0;
void dfs(TreeNode* root, int &k){
if(root==nullptr) return;
dfs(root->right,k);
if(k--==1)
res=root->val;
dfs(root->left,k);
}
int kthLargest(TreeNode* root, int k) {
dfs(root,k);
return res;
}
};
结果:
剑指 Offer 55 - I. 二叉树的深度
题解:层次遍历
通过一个队列,将二叉树一层一层推入队列中,每次循环的个数表示二叉树有多少层。
代码:
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==nullptr) return 0;
queue<TreeNode*>que;
que.push(root);
int res=0;
while(!que.empty()){
int sz=que.size();
while(sz--){
root=que.front();
que.pop();
if(root->left!=nullptr)
que.push(root->left);
if(root->right!=nullptr)
que.push(root->right);
}
++res;
}
return res;
}
};
结果:
剑指 Offer 55 - II. 平衡二叉树
题解:
方法一:遍历+深度计算
一边遍历一边计算左子树和右子树的差值是否大于2
代码:
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(root==nullptr) return true;
if(abs(getdeep(root->left)-getdeep(root->right))<2)
return isBalanced(root->left)&&isBalanced(root->right);
return false;
}
int getdeep(TreeNode* root){
if(root==nullptr) return 0;
return max(getdeep(root->left),getdeep(root->right))+1;
}
};
结果:
方法二:遍历
对上面的方法进行了优化,只需要遍历一次即可,也就是说去掉了计算机二叉树深度的那部分,减少了时间复杂度。
代码:
class Solution {
public:
bool isBalanced(TreeNode* root) {
return deep(root)!=-1;
}
int deep(TreeNode* root){
if(root==nullptr) return true;
int left=deep(root->left);
if(left==-1) return -1;
int right=deep(root->right);
if(right==-1) return -1;
return abs(left-right)<2?max(left,right)+1:-1;
}
};
结果:
剑指 Offer 56 - I. 数组中数字出现的次数
题解:方法一:哈希表
代码:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
unordered_map<int,int>map;
vector<int> res;
for(auto i:nums)
++map[i];
for(auto it=map.begin();it!=map.end();++it)
if(it->second==1)
res.push_back(it->first);
return res;
}
};
结果:
方法二:按位异或
哈希虽然简单,但是时间和空间复杂度都太高了,所以采用另一种策略。
因为两个相同的数按位异或等于0,那么把上面的数都异或,剩下的数便是我们要得到的两个数的异或的结果。
为了区分这两个数,找到二进制中值为1的位,说明该位一个数是1,一个数是0。所以,将数组中所有数通过这个条件分成两组,进行异或。
代码:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int res=0,res2=0;
int n=0;
for(auto i:nums)
res^=i;
while((res&(1<<n))==0)++n;
res=0;
for(auto i:nums){
if((i&(1<<n))==0)
res^=i;
else
res2^=i;
}
return {res,res2};//注意这边要用花括号
}
};
结果:
剑指 Offer 56 - II. 数组中数字出现的次数 II
题解:三进制
参考力扣某位大佬的方法,确实厉害!
代码:
class Solution {
public int singleNumber(int[] nums) {
// 可以设计一种逻辑,使数字出现 3 次时,该逻辑的结果为 0(即只有 0,1,2 三种状态)
// 其实就是一个 三进制
// 一位二进制数只能存储 0 和 1 两种状态,所以我们需要用到两位二进制
// 设两位二进制数的高位为 A,低位为 B。C 是输入变量
// 表示的三种情况为 : 0次:00(A=0,B=0), 1次:01(A=0,B=1), 2次:10(A=1,B=0)
// 注:11(A=1,B=1) 为无效输入
// 画出关于 A 的卡诺图(AB为11的结果是不重要的,用 x 表示):
// ABC | 0 | 1
// =================
// 00 | 0 | 0
// 01 | 0 | 1 ====> 得到 A = BC + AC'
// 11 | x | x
// 10 | 1 | 0
// 画出关于 B 的卡诺图
// ABC | 0 | 1
// =================
// 00 | 0 | 1
// 01 | 1 | 0 ====> 得到 B = BC' + A'B'C
// 11 | x | x
// 10 | 0 | 0
// 很明显啊,我们需要的就是只出现一次的情况 01(A=0,B=1),即 B 的结果
int A = 0, B = 0;
for (int C : nums) {
int tmp = A;
A = (B & C) | (A & ~C);
B = (B & ~C) | (~tmp & ~B & C);
}
return B;
}
}
结果:
剑指 Offer 57. 和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
题解:双指针
定义一个头指针left和尾指针right,当两者指向的数值之和大于target,--right;当两者指向的数值之和小于target,++left;直到找到等于target为止。
代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<right){
if(nums[left]+nums[right]>target)
--right;
else if(nums[left]+nums[right]<target)
++left;
else
return {nums[left],nums[right]};
}
return {0};
}
};
结果:
题解:哈希
遍历的时候在哈希表中找是否存在target-num的值,一次遍历,时间复杂度为O(N)
代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,bool>map;
for(int num:nums){
if(map[target-num])
return{num,target-num};
map[num]=true;
}
return {0};
}
};
结果:
剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
题解:
从数的一半开始找,如9,n从4开始到直至2为止。
建立一个双指针,left=right=target/n,左指针向左移动,右指针向右移动,将长度大小为n的区间【left,right】的数都累加起来,如果累加结果sum等于target,可得到一个区间。
代码:
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> res;
int left=0,right=0;
for(int n=target/2;n>1;--n){
left=right=target/n;
int n2=n/2,sum=0;
if(n%2){
sum+=left;
while(n2--){
sum+=--left;
sum+=++right;
if(left<=1)break;
}
}else{
while(n2--){
sum+=left--;
sum+=++right;
if(left<=0)break;
}
++left;
}
if(sum==target){
n2=n;
vector<int>row;
while(n2--){
row.push_back(left++);
}
res.push_back(row);
}
}
return res;
}
};
结果:
剑指 Offer 58 - I. 翻转单词顺序
题解:方法一 栈
建立一个栈,将字符串中的每一个单词都压入栈中,遍历完字符串后,再将栈中的数字弹出来。时间复杂度O(N+M)
代码:
class Solution {
public:
string reverseWords(string s) {
stack<string>T;
string res;
int i=0;
while(s[i]==' ') ++i;
while(i<s.size()){
string str;
while(i<s.size()&&s[i]!=' '){
str+=s[i++];
}
if(str!="")
T.push(str);
++i;
}
while(!T.empty()){
res+=T.top();
T.pop();
if(!T.empty())res+=' ';
}
return res;
}
};
结果:
方法二:遍历
从后向前遍历,跳过空格,只需遍历一遍即可,时间复杂度O(N)
代码:
class Solution {
public:
string reverseWords(string s) {
string res;
int i=s.size()-1;
int n=0;
while(i>=0){
while(i>=0&&s[i]==' ') --i;
while(i>=0&&s[i]!=' ') {
--i;
++n;
}
if(n){
if(!res.empty()) res+=' ';
res+=s.substr(i+1,n);
n=0;
}
}
//res.erase(res.size()-1,1);
return res;
}
};
结果:
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
题解:方法一:子串
建立一个字符串,把前n个字符暂存,然后删除再添加到尾部即可。
代码:
class Solution {
public:
string reverseLeftWords(string s, int n) {
string str=s.substr(0,n);
s.erase(0,n);
s.insert(s.size(),str);
return s;
}
};
结果:
方法二:反转
1.反转前n的字串。2.反转n到末尾的字串。3.反转全部字符串。
代码:
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
结果:
剑指 Offer 59 - I. 滑动窗口的最大值
题解:
首先,找到前k个数值中的最大值和最大值的下标。
因为滑动窗口的大小不会变化,接下来就是遍历剩下的n-k个数,例如:1,2,3,4(k=3),前三个最大数为3,向右滑动一个后,4只需与最大值比较,得到下一个最大值为4。如果3,2,1,4(k=3),因为最大值不再下一个窗口里面,所以下一个最大值需要重新选选出来。
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size()==0) return{};
vector<int> res;
int maxval=INT_MIN,maxindex=0;
for(int i=0;i<k;++i){
if(nums[i]>maxval){
maxindex=i;
maxval=nums[i];
}
}
res.push_back(maxval);
for(int i=k;i<nums.size();++i){
if(maxindex==i-k){
maxval=INT_MIN;
for(int j=maxindex+1;j<=i;++j){
if(nums[j]>maxval){
maxindex=j;
maxval=nums[j];
}
}
res.push_back(maxval);
}
else{
if(nums[i]>maxval){
maxindex=i;
maxval=nums[i];
}
res.push_back(maxval);
}
}
return res;
}
};
结果:
方法二:单调队列
这边用了双端队列,更像是面试官会考察的内容。
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int>que;
for(int i=0;i<k;++i){
while(!que.empty()&&nums[i]>nums[que.back()])
que.pop_back();
que.push_back(i);
}
vector<int> res{nums[que.front()]};
for(int i=k;i<nums.size();++i){
while(!que.empty()&&nums[i]>nums[que.back()])
que.pop_back();
que.push_back(i);
if(que.front()==i-k)
que.pop_front();
res.push_back(nums[que.front()]);
}
return res;
}
};
结果:
剑指 Offer 59 - II. 队列的最大值
题解:单调队列
和上一题其实没有太大区别
最大值函数:
如果maxque为空,返回-1;否则,返回队首元素。
push_back(int value):
1.压入队列que;2.
代码:
class MaxQueue {
public:
queue<int>que;
deque<int>maxque;
MaxQueue() {
}
int max_value() {
if(maxque.empty())
return -1;
return maxque.front();
}
void push_back(int value) {
que.push(value);
while(!maxque.empty()&&maxque.back()<value)
maxque.pop_back();
maxque.push_back(value);
}
int pop_front() {
if(que.empty()) return -1;
int res=que.front();
if(que.front()==maxque.front()){
maxque.pop_front();
}
que.pop();
return res;
}
};
结果:
题解:
代码:
结果:
剑指 Offer 61. 扑克牌中的顺子
题解:方法一,哈希+遍历
遍历数组,遇到0跳过,找到最大和最小值,如果有重复立马就跳过,最后比较最大值和最小值的差是否大于5
代码:
class Solution {
public:
bool isStraight(vector<int>& nums) {
//sort(nums.begin(),nums.end());
unordered_map<int,bool>map;
int minval=14,maxval=-1;
for(auto i:nums){
if(i==0) continue;
if(i<minval) minval=i;
if(i>maxval) maxval=i;
if(map[i]) return false;
map[i]=true;
}
return maxval-minval<5?true:false;
}
};
结果:
方法一,排序+遍历
思路和方法一一样,只不过用排序,这样就不用哈希了
代码:
class Solution {
public:
bool isStraight(vector<int>& nums) {
sort(nums.begin(),nums.end());
int zero=0;
for(int i=0;i<nums.size();++i){
if(nums[i]==0) ++zero;
else if(i>0&&nums[i-1]==nums[i]) return false;
}
return nums[4]-nums[zero]<5?true:false;
}
};
结果:
剑指 Offer 62. 圆圈中最后剩下的数字
题解:
代码:
class Solution {
public:
int lastRemaining(int n, int m) {
int res=0;
for(int i=2;i<=n;++i){
res=(res+m)%i;
}
return res;
}
};
结果:
剑指 Offer 63. 股票的最大利润
题解:
暂存最小值,遍历后面的数组,得到一个最大值,最大值-最小值就是最大增益。
代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minbuy=INT_MAX;
int res=0;
for(auto p:prices){
minbuy=min(minbuy,p);
res=max(res,p-minbuy);
}
return res;
}
};
结果:
剑指 Offer 64. 求1+2+…+n
求 1+2+...+n
,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
题解:递归短路
因为条件限制,所有想到的方法只有递归,但是如何终止呢?用到同或(&&)的一个性质,就是第一个条件一旦为假的时候,后面的就不会去判断了。
代码:
class Solution {
public:
int sumNums(int n) {
int sum=n;
// if(n>0)//题目不允许的
bool flag=(n>0 && (sum+=sumNums(n-1))>0);
return sum;
}
};
结果:
剑指 Offer 65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
题解:
代码:
class Solution {
public:
int add(int a, int b) {
int res=a^b;
int carry=(unsigned int)(a&b)<<1;
while(carry){
a=res;
b=carry;
res=a^b;
carry=(unsigned int)(a&b)<<1;
}
return res;
}
};
结果:
剑指 Offer 66. 构建乘积数组
给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
题解:遍历
首先,遍历数组,把除零外的数都相乘得到一个值。排除有两个零以上的结果
再次遍历数组,当前数组中没有0,就得到sum/i。如果有0,i=0的时候为sum/i,其它一律为0。
代码:
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int sum=1;
int zero_flag=0;
for(auto i:a){
if(i==0)
++zero_flag;
else
sum=sum*i;
}
sum=zero_flag>1?0:sum;
for(auto &i:a){
if(i==0)
i=sum;
else
i=zero_flag?0:sum/i;
}
return a;
}
};
结果:
剑指 Offer 67. 把字符串转换成整数
题解:
首先,排除前面有空格的情况;第二步,可能有正负号,需要确定一下;第三步,首位是0的话需要将其去掉;第四步,正式处理阿拉伯数字字符串,分为两种情况,一种是负数的时候,一种是正数的时候,不能越界,具体方法可以看代码
代码:
class Solution {
public:
int strToInt(string str) {
int pos=0,sum=0;
bool flag=false;
while(str[pos]==' ') ++pos;
if(str[pos]=='-'){
++pos;
flag=true;
}
else if(str[pos]=='+')
++pos;
while(str[pos]=='0') ++pos;
while(str[pos]>='0'&&str[pos]<='9'){
if(!flag){
if(INT_MAX/10<sum || (str[pos]>'7'&&INT_MAX/10==sum))//防止越界
return INT_MAX;
else
sum=sum*10+(str[pos++]-'0');
}
else{
if(sum>0) sum*=-1;//变成负数
if(INT_MIN/10>sum||(str[pos]>'8'&&INT_MIN/10==sum))//防止越界
return INT_MIN;
else
sum=sum*10-(str[pos++]-'0');
}
}
return sum;
}
};
结果:
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
题解:递归
利用二叉搜索树的性质,如果两个值都小于root,那么就在树的左子树,如果都大于,就在树的右子树,如果出现一大一小,说明当前的节点就是它们的祖先节点。
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root->val>p->val&&root->val>q->val)
return lowestCommonAncestor(root->left,p,q);
if(root->val<p->val&&root->val<q->val)
return lowestCommonAncestor(root->right,p,q);
return root;
}
};
结果:
剑指 Offer 68 - II. 二叉树的最近公共祖先
题解:递归
思路和上题差不多
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr || root == p || root == q) return root;
TreeNode *left = lowestCommonAncestor(root->left, p, q);
TreeNode *right = lowestCommonAncestor(root->right, p, q);
if(left == nullptr) return right;
if(right == nullptr) return left;
return root;
}
};
结果:
最后
以上就是怕黑摩托为你收集整理的LeetCode力扣(剑指offer 41-68)剑指 Offer 41. 数据流中的中位数剑指 Offer 42. 连续子数组的最大和剑指 Offer 43. 1~n 整数中 1 出现的次数剑指 Offer 44. 数字序列中某一位的数字剑指 Offer 45. 把数组排成最小的数剑指 Offer 46. 把数字翻译成字符串剑指 Offer 47. 礼物的最大价值剑指 Offer 48. 最长不含重复字符的子字符串剑指 Offer 49. 丑数剑指 Offer 50. 第一个只出现一次的字符剑的全部内容,希望文章能够帮你解决LeetCode力扣(剑指offer 41-68)剑指 Offer 41. 数据流中的中位数剑指 Offer 42. 连续子数组的最大和剑指 Offer 43. 1~n 整数中 1 出现的次数剑指 Offer 44. 数字序列中某一位的数字剑指 Offer 45. 把数组排成最小的数剑指 Offer 46. 把数字翻译成字符串剑指 Offer 47. 礼物的最大价值剑指 Offer 48. 最长不含重复字符的子字符串剑指 Offer 49. 丑数剑指 Offer 50. 第一个只出现一次的字符剑所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复