今日三道剑指offer,一道LeetCode
1.重建二叉树,根据前序遍历和中序遍历重建二叉树,二叉树中不包含重复数字。
主要思路:前序遍历第一个点为根节点,而中序遍历中根节点位置前面的数字为左子树,右边的位置为右子树。然后递归递归!
a:首先申请一个根节点,根节点的值等于当前前序遍历的第一个数,左右子树为空。
b:判断根节点是不是前序遍历最后一个元素,如果是,判断一个特殊情况,此时中序遍历应该也是只有一个元素,且两个元素相等,不等抛出异常,等于的话,直接返回根节点即可。
c:如果还有其他元素,遍历中序数组,找到根节点的位置,计算左子树的长度。(再遍历过程中要注意下表不能超过数组的最大长度,所以跳出循环除了找到根节点外还可能是查询完所有的元素而跳出的循环,所以要判断下此时查找到的位置是否等于根节点,不等于,抛出异常)
d:如果左子树长度大于0,递归建立左子树。如果右子树长度大于0,递归建立右子树。最后返回根节点。
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size()<=0||vin.size()<=0)
{
return nullptr;
}
return ConstructBinaryTree(pre,vin);
}
TreeNode* ConstructBinaryTree(vector<int> pre,vector<int> vin)
{
TreeNode* root=new TreeNode(0);
root->val=pre[0];
root->left=nullptr;
root->right=nullptr;
if(root->val==pre[pre.size()-1])
{
if(vin[0]==vin[vin.size()-1]&&vin[0]==pre[0])
{
return root;
}
else
{
//throw std::exception("");
}
}
int i=0;
while(i<vin.size()&&vin[i]!=root->val)
{
i++;
}
if(vin[i]!=root->val)
{
//throw std::exception("");
}
int leftlength=i;
if(leftlength>0)
{
vector<int>leftpre;
vector<int>leftvin;
leftpre.assign(pre.begin()+1,pre.begin()+i+1);
leftvin.assign(vin.begin(),vin.begin()+i);
root->left= ConstructBinaryTree(leftpre,leftvin);
}
if(leftlength<pre.size()-1)
{
vector<int>rightpre;
vector<int>rightvin;
rightpre.assign(pre.begin()+i+1,pre.end());
rightvin.assign(vin.begin()+i+1,vin.end());
root->right= ConstructBinaryTree(rightpre,rightvin);
}
return root;
}
2.两个栈实现队列的插入和弹出操作。
a:只要插入元素就往栈1里插入
b:要弹出元素,先判断栈2是否为空,不为空,直接从栈2弹出,如果栈2为空,且栈1不为空,把栈1中的元素依次弹出,压入栈2,再把栈2的栈顶元素弹出并返回。如果两个都为空,抛出异常。
void push(int node) {
stack1.push(node);
}
int pop() {
if(!stack2.empty())
{
int num=stack2.top();
stack2.pop();
return num;
}
else if(!stack1.empty()&&stack2.empty())
{
while(!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
int num=stack2.top();
stack2.pop();
return num;
}
else if(stack1.empty()&&stack2.empty())
{
return 0;
}
}
3.旋转数组找最小值。
思路:要谨记查找过程中左边的数一定大于等于右边的数。
a:申请两个下标L和R,如果两者相差为1,那么最小值一定等于右边的数。
b:计算中间下标mid=(L+R)/2,如果左边的数等于中间值等于右边的数,那么只能通过遍历寻找最小值(非递减数列,不是严格的递增数列)
c:如果中间的数大于等于左边的数,左下标等于mid,如果中间的数小于右边的数,右下标等于mid。
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.size()<=0)
{
return 0;
}
int l=0,r=rotateArray.size()-1;
int mid=0;
while(rotateArray[l]>=rotateArray[r])
{
if(r-l==1)
{
mid=r;
break;
}
mid=(l+r)/2;
if(rotateArray[l]==rotateArray[mid]&&rotateArray[mid]==rotateArray[r])
{
int res=0;
res=bianli(rotateArray);
return res;
}
else if(rotateArray[mid]>=rotateArray[l])
{
l=mid;
}
else if(rotateArray[mid]<=rotateArray[r])
{
r=mid;
}
}
return rotateArray[mid];
}
int bianli(vector<int> rotateArray)
{
int maxvalue=0;
for(int i=0;i<rotateArray.size();i++)
{
if(rotateArray[i]>maxvalue)
{
maxvalue=rotateArray[i];
}
}
return maxvalue;
}
4.逆波兰表达式。就是把后缀表达式的值计算出来。
主要思路:遇到数值就压栈,遇到运算符就弹出栈的两个元素并计算,再把结果压栈。
一共有三个需要注意的点:遇到运算符时,弹出栈的两个元素,首先要保证栈的容量大于等于2;其次再计算“/”时,保证分母不为0;最后返回结果时,要保证栈中只有1个元素,如果有多余元素也要报错。
写程序时,运算操作记住是第二个弹出的数字在前,在字符串转数字过程,直接利用stringstream。
int evalRPN(vector<string> &tokens) {
if(tokens.size()<0)
{
return 0;
}
stack<int>nums;
for(int i=0;i<tokens.size();i++)
{
if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/")
{
if(nums.size()<2)return 0;
int num1=nums.top();
nums.pop();
int num2=nums.top();
nums.pop();
int res;
if(tokens[i]=="+")
{
res=num2+num1;
}
else if(tokens[i]=="-")
{
res=num2-num1;
}
else if(tokens[i]=="*")
{
res=num2*num1;
}
else if(tokens[i]=="/")
{
res=num2/num1;
}
nums.push(res);
}
else
{
stringstream s;
s<<tokens[i];
int num;
s>>num;
nums.push(num);
}
}
return nums.size()==1?nums.top():0;
}