20221226
剑指 Offer 35. 复杂链表的复制
解题思路:首先,复制链表节点插入到原节点的后面,例如构建A->A*->B->B*->C->C*。2.将random与的关系连接到复制的节点上。3.将next的关系加上,将链表分割为2个链表。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==nullptr)
return nullptr;
for(Node* ptr=head;ptr!=nullptr;ptr=ptr->next->next) //将复制链表节点插入到原节点的后面
{
Node* newNode=new Node(ptr->val);
newNode->next=ptr->next;
ptr->next=newNode;
}
for(Node* ptr=head;ptr!=nullptr;ptr=ptr->next->next) //将random与的关系连接到复制的节点上。
{
Node* tmp=ptr->next;
if(ptr->random!=nullptr)
tmp->random=ptr->random->next;
else
tmp->random=nullptr;
}
Node* newHead=head->next;
for(Node* node=head;node!=nullptr;node=node->next) //3.将next的关系加上,将链表分割为2个链表。
{
Node* nodeNew=node->next;
node->next=node->next->next;
if(nodeNew->next!=nullptr)
nodeNew->next=nodeNew->next->next;
else
nodeNew->next=nullptr;
}
return newHead;
}
};
剑指 Offer 05. 替换空格
首先,我们先遍历一边字符数组,找到其中空格的个数,因为一个空格用三个字符表示,所以一个空格我们需要增加2个字符,因为加上空格后就是3个字符。所以我们把数组的长度增加为s.resize(s.size()+2*n);n为空格数量。
分别设置2个指针i,j指向原来数组的最后一个字符和新数组的最后一个字符,实现修改。当i指针不为空格式时,让s[j]=s[i];
当i指针为空格时,让s[j]=‘0’; s[j-1]=‘2’; s[j-2]=‘%’;
class Solution {
public:
string replaceSpace(string s) {
int len=s.size();
int count=0;
for(int i=0;i<s.size();i++)
{
if(s[i]==' ')
count++;
}
s.resize(len+count*2);
for(int i=len-1,j=s.size()-1;i<j;i--,j--)
{
if(s[i]!=' ')
s[j]=s[i];
else{
s[j]='0';
s[j-1]='2';
s[j-2]='%';
j=j-2;
}
}
return s;
}
};
剑指 Offer 58 - II. 左旋转字符串
进行3次反转:
1.首先是0-----> n-1位置进行字母的反转。
2.从n---------> s.size()-1 位置进行字母的反转。
3.从0---------> s.size()-1 位置进行字母的反转。
class Solution {
public:
void reverses(string& s,int left,int right){
char temp;
while(left<right){
temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
}
string reverseLeftWords(string s, int n) {
reverses(s,0,n-1);
reverses(s,n,s.size()-1);
reverses(s,0,s.size()-1);
return s;
}
};
剑指 Offer 03. 数组中重复的数字
使用哈希表,unordered_map<int,bool> map,每遍历一个数据元素,将其对应的bool值赋值为true,当相同的元素出现时,这个元素对应的bool值为true,我们返回这个值即可。
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
unordered_map<int,bool> map;
for(int num:nums)
{
if(map[num])
return num;
map[num]=true;
}
return -1;
}
};
20221230
剑指 Offer 53 - I. 在排序数组中查找数字 I
使用二分查找方法。
class Solution {
public:
int search(vector<int>& nums, int target) {
int i=0;
int j=nums.size()-1;
while(i<=j)
{
int m=(i+j)/2;
if(nums[m]<=target)
i=m+1;
else
j=m-1;
}
int right=i;
i=0;
j-nums.size()-1;
while(i<=j)
{
int m=(i+j)/2;
if(nums[m]<target)
i=m+1;
else
j=m-1;
}
int left=j;
return right-left-1;
}
};
剑指 Offer 53 - II. 0~n-1中缺失的数字
解题思路:
方法一:使用哈希表,将数组当中的元素放入到哈希表中,遍历从0-n这n+1个元素,找出没有出现的那个元素。
class Solution {
public:
int missingNumber(vector<int>& nums) {
unordered_set<int> set;
for(int i=0;i<nums.size();i++)
{
set.insert(nums[i]);
}
int missing=-1;
for(int i=0;i<nums.size()+1;i++)
{
if(set.count(i)==0)
{
missing=i;
break;
}
}
return missing;
}
};
法二:
使用直接法
class Solution {
public:
int missingNumber(vector<int>& nums) {
int len=nums.size()+1;
int count;
for(int i=0;i<len-1;i++)
{
if(nums[i]!=i)
return i;
}
return len-1;
}
};
20230102
剑指 Offer 04. 二维数组中的查找
解题思路:首先找到二维矩阵的左下角元素,判断matrix[i][j]与target的大小,若matrix[i][j]>target,则i–,若matrix[i][j]<target,则j++,
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i=matrix.size()-1;
int j=0;
while(i>=0&&j<matrix[0].size()){
if(matrix[i][j]>target)
{
i--;
}
else if(matrix[i][j]<target)
{
j++;
}
else
return true;
}
return false;
}
};
剑指 Offer 11. 旋转数组的最小数字
解题思路:我们先找出mid=(left+right)/2,若numbers[mid]>numbers[right],则让left=mid+1,numbers[mid]<numbers[right],则让right=mid,若numbers[mid]==numbers[right],则让right-=1
class Solution {
public:
int minArray(vector<int>& numbers) {
int left=0;
int right=numbers.size()-1;
while(left<right)
{
int mid=left+(right-left)/2;
if(numbers[mid]<numbers[right])
right=mid;
else
if(numbers[mid]>numbers[right])
left=mid+1;
else
right-=1;
}
return numbers[left];
}
};
剑指 Offer 50. 第一个只出现一次的字符
解题思路:
使用哈希表存储存放的次数
我们可以对字符串进行2次遍历,使用哈希映射来统计每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回该字符,否则在遍历结束后返回空格。
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<int,int> map;
for(char c:s)
{
map[c]++;
}
for(char c:s)
{
if(map[c]==1)
return c;
}
return ' ';
}
};
20230103
剑指 Offer 32 - I. 从上到下打印二叉树
使用层序遍历的方法,利用队列来进行排序。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<int> nums;
TreeNode* tmp=root;
que.push(tmp);
if(root==nullptr)
return nums;
while(que.empty()==false)
{
int size=que.size();
for(int i=0;i<size;i++)
{
TreeNode *node=que.front();
nums.push_back(node->val);
que.pop();
if(node->left!=nullptr)
que.push(node->left);
if(node->right!=nullptr)
que.push(node->right);
}
}
return nums;
}
};
剑指 Offer 32 - II. 从上到下打印二叉树 II
采用层序遍历的方式来进行遍历二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> nums;
if(root==nullptr)
return nums;
queue<TreeNode*> que;
TreeNode* ptr=root;
que.push(ptr);
while(que.empty()==false)
{
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++)
{
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left!=nullptr)
que.push(node->left);
if(node->right!=nullptr)
que.push(node->right);
}
nums.push_back(vec);
}
return nums;
}
};
剑指 Offer 32 - III. 从上到下打印二叉树 III
创建一个标志isOrderLeft,当遍历的层数是奇数层时,按照从左到右的顺序遍历,当遍历的层数为偶数层时,按照从右到左的顺序遍历,创建一个双端队列,当遍历的层数是奇数层时,从下端进入队列,当遍历的层数为偶数层时,从上端进入队列。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
if(root==nullptr)
return ans;
queue<TreeNode*> que;
que.push(root);
bool isOrderLeft=true;
while(que.empty()==false)
{
deque<int> list;
int size=que.size();
for(int i=0;i<size;i++)
{
TreeNode* node=que.front();
que.pop();
if(isOrderLeft)
{
list.push_back(node->val);
}
else
list.push_front(node->val);
if(node->left!=nullptr)
que.push(node->left);
if(node->right!=nullptr)
que.push(node->right);
}
vector<int> vec(list.begin(),list.end());
ans.push_back(vec);
isOrderLeft=!isOrderLeft;
}
return ans;
}
};
剑指 Offer 27. 二叉树的镜像
可以使用前序和后序遍历进行交换,但不能使用中序遍历。将根节点的左子树和右子树进行交换。
后序遍历递归法:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void swap(TreeNode* root){
if(root==nullptr)
return;
swap(root->left);
swap(root->right);
TreeNode* temp;
temp=root->left;
root->left=root->right;
root->right=temp;
}
TreeNode* mirrorTree(TreeNode* root) {
swap(root);
return root;
}
};
前序遍历的方法:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr)
return root;
TreeNode* temp=root->left;
root->left=root->right;
root->right=temp;
invertTree(root->left);
invertTree(root->right);
return root;
}
};
20230104
剑指 Offer 28. 对称的二叉树
解题思路:
传递的参数为左子树与右子树
1.参数:因为我们要比较的是根节点的2个子树是否相互翻转,进而判断这个树是不是对称树,所以参数为左子树与右子树节点。
2.终止条件:要比较2个节点数值相不相同,节点为空的情况有:
左节点为空,右节点不为空,不对称,return false
左不为空,右为空,不对称 return false
左右都为空,对称,返回true
3.确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 右节点都不为空,且数值相同的情况。
比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
如果左右都对称就返回true ,有一侧不对称就返回false 。
完整代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool check(TreeNode* left,TreeNode* right){
// 首先排除空节点的情况
if(left==nullptr&&right!=nullptr)
return false;
else if(left!=nullptr&&right==nullptr)
return false;
else if(left==nullptr&&right==nullptr)
return true;
// 排除了空节点,再排除数值不相同的情况
else if(left->val!=right->val)
return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
else{
bool oneSide=check(left->left,right->right);
bool twoSide=check(left->right,right->left);
bool isSame=oneSide&&twoSide;
return isSame;
}
}
bool isSymmetric(TreeNode* root) {
bool isSames=check(root->left,root->right);
return isSames;
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
解题思路:从地面到第一层台阶有1种跳法,从地面到第二层台阶有2种跳法,我们创建dp数组,dp[i]用于表示从地面到第i层台阶的有多少种跳法,dp数组的长度设置为n+1,序列为从dp[0,1,2…n]。从地面到第三层的跳法种类即为从第一层跳2个台阶到第三层或从第二层跳1个台阶到第三层,即为dp[3]=dp[2]+dp[1]。那么从地面到第n层的跳法种类即为从第n-2层跳2个台阶到第n层或从第n-1层跳1个台阶到第n层,即为dp[n]=dp[n-2]+dp[n-1]。
代码:
class Solution {
public:
int numWays(int n) {
if(n<=1)
return 1;
vector<int> dp(n+1);
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++)
{
dp[i]=(dp[i-1]+dp[i-2])%(1000000007);;
}
return dp[n];
}
};
20230412
剑指 Offer 63. 股票的最大利润
贪心算法
我们来假设自己来购买股票。随着时间的推移,每天我们都可以选择出售股票与否。那么,假设在第 i 天,如果我们要在今天卖股票,那么我们能赚多少钱呢?
显然,如果我们真的在买卖股票,我们肯定会想:如果我是在历史最低点买的股票就好了!太好了,在题目中,我们只要用一个变量记录一个历史最低价格 minprice,我们就可以假设自己的股票是在那天买的。那么我们在第 i 天卖出股票能得到的利润就是 prices[i] - minprice。
因此,我们只需要遍历价格数组一遍,记录历史最低点,然后在每一天考虑这么一个问题:如果我是在历史最低点买进的,那么我今天卖出能赚多少钱?当考虑完所有天数之时,我们就得到了最好的答案。
c++
class Solution {
public:
int maxProfit(vector<int>& prices) {
int low=INT_MAX;
int result=0;
for(int i=0;i<prices.size();i++)
{
low=min(low,prices[i]);
result=max(result,prices[i]-low);
}
return result;
}
};
本文概述了解决剑指 Offer 中涉及的几个算法问题:复杂链表复制、字符串替换空格、左旋转字符串、查找数字、缺失数字、二维数组查找、最小数字、唯一字符等。通过递归和哈希表等方法,详细展示了如何实现这些经典面试题目的解决方案。
2073

被折叠的 条评论
为什么被折叠?



