目录
一、栈
逆波兰表达式求值
什么是逆波兰表达式?
逆波兰表达式,也就是后缀表达式,就是将操作符都放到最后
例如1+2*3 的逆波兰表达式就是 1 2 3 + *
解题思路
利用栈st,将tokens依次入栈,
首先入栈2,但是要将字符串2转为整数2,因此用到stoi()这个字符串方法,
再将1入栈
后面检查到操作符"+"
就把栈中的st依次出栈,(出栈顺序为从右往左),将运算结果再压入栈中,再继续把没压入栈st中的tokens剩余字符串压入栈,直到遍历完整个tokens,最后的结果就留在了栈st中。
代码
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto str : tokens)
{
if(str=="+"||str=="-"||str=="*"||str=="/")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
//注意这里不能用str
switch(str[0])
{
case '+':
st.push(right+left);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
二、二叉树
(一)根据二叉树创建字符串
这里需要注意的是,当左子树为空,就要判断右子树是不是存在,如果存在则左子树要有括号,如果右子树不存在则不需要括号,
解题思路
这里的关键是怎么遍历这个树,然后这里要的是前序遍历,那么访问顺序就是中左右,利用递归可以很好的遍历这个树,
class Solution {
public:
string tree2str(TreeNode* root) {
string s1="";
if(root==nullptr)
{
return "";
}
s1+=to_string(root->val);
if(root->left)
{
s1+="(";
s1+=tree2str(root->left);
s1+=")";
}
else if(root->right)
{
s1+="()";
}
if(root->right)
{
s1+="(";
s1+=tree2str(root->right);
s1+=")";
}
return s1;
}
};
(二)二叉树的层序遍历
怎么垂直遍历这个树
利用一个队列queue<TreeNode*> q
先看这个树是不是为空如果是则直接返回
否则将根压入队列
然后循环这个队列看队列是否是空,不是空那么弹出这个队列,计算队列中的大小,也就相当于每一层有多少个元素,
利用vector<int> v 装这层的数据,
然后如果该节点的左右子树存在那么压入队列
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> vv;
queue<TreeNode*> q;
if(!root)
{
return vv;
}
if(root)
{
q.push(root);
}
while(!q.empty())
{
int size=q.size();
vector<int> v;
for(int i=0;i<size;i++)
{
TreeNode* front=q.front();
v.push_back(front->val);
q.pop();
if(front->left)
{
q.push(front->left);
}
if(front->right)
{
q.push(front->right);
}
}
vv.push_back(v);
}
return vv;
}
};
(三)二叉树的最近公共祖先
解题思路
(1)如果p和q在root的左右子树中说明root就是他们的最近祖先
(2)如果p和q都在左子树中,那么找离顶最近的那个就是祖先
class Solution {
public:
bool Find(TreeNode* root,TreeNode* x)
{
if(root==NULL)
return false;
return root == x
|| Find(root->left,x)
|| Find(root->right,x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
bool pInRight,pInLeft,qInRight,qInLeft;
if(root==NULL)
{
return NULL;
}
if(root==p||root==q)
{
return root;
}
pInRight=Find(root->right,p);
pInLeft=!pInRight;
qInRight=Find(root->right,q);
qInLeft=!qInRight;
if((pInLeft&&qInRight)||(qInLeft&&pInRight))
{
return root;
}
if(pInLeft&&qInLeft)
{
return lowestCommonAncestor(root->left,p,q);
}
if(pInRight&&qInRight)
{
return lowestCommonAncestor(root->right,p,q);
}
return NULL;
}
};
(四)搜索二叉树与双向链表
解题思路
一个二叉树的中序遍历是有序的,并且遍历的实现是递归,但是递归并不知道上一个节点,因此就要想办法找到上一个节点,因此我们使用指针的引用,TreeNode* & prev 需要注意这里是引用,用来修改prev的值,
class Solution {
public:
//这里是指针的引用
void ConvertList(TreeNode* cur,TreeNode*& prev)
{
if(cur==NULL)
{
return ;
}
//一直找到最第一个节点,因为是中序就是最左边的那个先遍历所以先找到最左边那个
ConvertList(cur->left,prev);
//让当前的左节点指向上一个节点
cur->left=prev;
//如果是第一次prev为NULL那么不做操作
if(prev)
{
//让上一次的节点的右节点指向下一个节点
prev->right=cur;
}
//把prev指向上一个节点
prev=cur;
//然后遍历右边的子树
ConvertList(cur->right,prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
TreeNode* prev=NULL;
ConvertList(pRootOfTree,prev);
//找到链表的头,链表的头就是最左边子树
TreeNode* head=pRootOfTree;
//这里为什么要判断head!=NULL是因为如果这个树为空那么不操作
while(head&&head->left)
{
head=head->left;
}
return head;
}
};
(五)二叉树的前序遍历
解题思路
递归实现
递归实现很简单就是遍历完根后遍历左子树,再遍历右子树
class Solution {
public:
void _preorderTraversal(TreeNode* root,vector<int> & v)
{
if(!root)
{
return;
}
v.push_back(root->val);
_preorderTraversal(root->left,v);
_preorderTraversal(root->right,v);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
_preorderTraversal(root,v);
return v;
}
};
进阶:非递归实现
其实递归就像栈一样,因此非递归就是让我们模拟栈,
/**
* 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:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> st;
TreeNode* cur=root;
//如果栈为空说明遍历完了,如果cur为空说明这个节点为空
while(cur||!st.empty())
{
//一直往左遍历,
while(cur)
{
//先把当前的值存入v
v.push_back(cur->val);
//把现在的cur入栈
st.push(cur);
cur=cur->left;
}
//说明左边的节点访问完了,那么就开始右节点的访问先弹出栈顶元素,就是最后一个左节点
TreeNode* tp=st.top();
st.pop();
//然后让当前的cur指向右节点去访问,再进行大循环,遍历这个节点的左节点
cur=tp->right;
}
return v;
}
};
(六)二叉树的中序遍历
解题思路
递归实现
就是正常的左中右遍历递归思路
class Solution {
public:
void _inorderTraversal(TreeNode* root,vector<int>& v)
{
if(!root)
{
return ;
}
_inorderTraversal(root->left,v);
v.push_back(root->val);
_inorderTraversal(root->right,v);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
_inorderTraversal(root,v);
return v;
}
};
非递归实现
思路和前序遍历一样,只不过是在遍历完左节点之后插入的值
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> st;
TreeNode* cur=root;
while(cur||!st.empty())
{
while(cur)
{
st.push(cur);
cur=cur->left;
}
TreeNode* tp=st.top();
st.pop();
v.push_back(tp->val);
cur=tp->right;
}
return v;
}
};
(七)二叉树的后序遍历
解题思路
递归实现
思路和前面的前序遍历和中序遍历一样但是顺序改变了
class Solution {
public:
void _postorderTraversal(TreeNode* root,vector<int>& v)
{
if(!root)
{
return ;
}
_postorderTraversal(root->left,v);
_postorderTraversal(root->right,v);
v.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
_postorderTraversal(root,v);
return v;
}
};
(八)前K个高频单词
解题思路
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
map<string,int> countMap;
vector<string> v;
//遍历这个字符串容器,看看每个单词出现的次数
for(auto e: words)
{
countMap[e]++;
}
//根据次数然后排序,如果用sort的话可能不能正确实现字符的大小正确,因为sort的底层是快排
//multimap可以解决这个问题,
//但是multimap<int,string,less<int>>默认这里是升序的,所以输出的结果是
//coding , leetcode , i , love
//很明显不满足要求
//因此把less<int>按照键的要求从大往小排
multimap<int,string,greater<int>>sortMap;
for(auto kv:countMap)
{
sortMap.insert(make_pair(kv.second,kv.first));
}
auto it=sortMap.begin();
while(it!=sortMap.end())
{
if(k==0)
{
break;
}
v.push_back(it->second);
++it;
--k;
}
return v;
}
};