队列和栈的相关算法题

LeetCode20——有效的括号

题目:

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true
示例 2:

输入: "()[]{}"
输出: true
示例 3:

输入: "(]"
输出: false
示例 4:

输入: "([)]"
输出: false
示例 5:

输入: "{[]}"
输出: true

思路:

用栈的存储括号,左右同种类型的括号相匹配才可以顺利出栈,注意判断栈的容量为空,以及首字符为 右括号的情况。

代码:

class Solution {
public:
	bool isValid(string s) {
		stack<char>t;
		for (int i = 0; i < s.length(); i++)
		{
			if (s[i] == '(' || s[i] == '[' || s[i] == '{')
				t.push(s[i]);
			else if (s[i] == ')')
			{
				if (i == 0 || t.size() == 0 || t.top() != '(')
					return false;
				else
					t.pop();
			}
			else if (s[i] == ']')
			{
				if (i == 0 || t.size() == 0 || t.top() != '[')
					return false;
				else
					t.pop();
			}
			else if(s[i]=='}')
			{
				if (i == 0 || t.size() == 0 || t.top() != '{')
					return false;
				else
					t.pop();
			}
		}
		return t.empty();

	}
};

LeetCode150——逆波兰表达式

题目:

根据逆波兰表示法,求表达式的值。

有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入: ["2", "1", "+", "3", "*"]
输出: 9
解释: ((2 + 1) * 3) = 9
示例 2:

输入: ["4", "13", "5", "/", "+"]
输出: 6
解释: (4 + (13 / 5)) = 6
示例 3:

输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
输出: 22
解释: 
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

思路:

读入数字的时候放入栈中,遇到运算符号就取出栈顶的前两个元素进行运算。两个非常好用的函数 string转int -> stoi(s,0,10),以及int转整型 to_string。

代码:

class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		int len = tokens.size();
		if (len == 0) return 0;
		stack<string>st;
		for (int i = 0; i < len; i++)
		{
			if (tokens[i] != "+" &&tokens[i] != "-"&&tokens[i] != "/"&& tokens[i] != "*")
			{
				st.push(tokens[i]);
			}
 			else if (tokens[i] == "+")
			{
				string num1 = st.top();
				st.pop();
				string num2 = st.top();
				st.pop();//string 字符串转数字,stoi(s,p,b)	把字符串s从p开始转换成b进制的int
				int n1 = stoi(num1, 0, 10);
				int n2 = stoi(num2, 0, 10);
				int result = n2 + n1;
				string res = to_string(result);
				st.push(res);
			}
			else if (tokens[i] == "-")
			{
				string num1 = st.top();
				st.pop();
				string num2 = st.top();
				st.pop();//string 字符串转数字,stoi(s,p,b)	把字符串s从p开始转换成b进制的int
				int n1 = stoi(num1, 0, 10);
				int n2 = stoi(num2, 0, 10);
				int result = n2 - n1;
				string res = to_string(result);
				st.push(res);
			}
			else if (tokens[i] == "*")
			{
				string num1 = st.top();
				st.pop();
				string num2 = st.top();
				st.pop();//string 字符串转数字,stoi(s,p,b)	把字符串s从p开始转换成b进制的int
				int n1 = stoi(num1, 0, 10);
				int n2 = stoi(num2, 0, 10);
				int result = n2 * n1;
				string res = to_string(result);
				st.push(res);
			}
			else 
			{
				string num1 = st.top();
				st.pop();
				string num2 = st.top();
				st.pop();//string 字符串转数字,stoi(s,p,b)	把字符串s从p开始转换成b进制的int
				int n1 = stoi(num1, 0, 10);
				int n2 = stoi(num2, 0, 10);
				int result = n2/n1;
				string res = to_string(result);
				st.push(res);
			}
			
		}
		return stoi(st.top());
	}
};

LeetCode145——二叉树先序遍历的非递归实现:https://www.cnblogs.com/kangna/p/11846156.html

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		stack<TreeNode*>st;
		vector<int>ans;
		if (root == nullptr)
			return ans;
		st.push(root);
		while (!st.empty())
		{
			TreeNode* temp = st.top();
			st.pop();
			ans.push_back(temp->val);
			if (temp->right != nullptr)
				st.push(temp->right);
			if (temp->left != nullptr)
				st.push(temp->left);
		}
		return ans;
	}
};

 

LeetCode146——二叉树中序遍历的非递归实现:https://www.cnblogs.com/kangna/p/11846156.html

class Solution {
 public:
	 vector<int> inorderTraversal(TreeNode* root) {
		 stack<TreeNode*>st;
		 vector<int>ans;
		 if (root == nullptr)
			 return ans;
		 TreeNode* p = root;
		 while (!st.empty()||p)//注意这个条件“||”
		 {
			 if (p)
			 {
				 st.push(p);
				 p = p->left;
			 }
			 else
			 {
				 
				 TreeNode* temp = st.top();
				 ans.push_back(temp->val);
				 st.pop();
				 p = temp->right;

			 }
		 }
		 return ans;
	 }
 };

LeetCode147——二叉树的层序遍历:

class Solution {
public:
	vector<vector<int>> levelOrder(TreeNode* root) {
		vector<vector<int>>result;
		if (root == NULL)
			return result;
		vector<int>temp;
		queue<TreeNode*>record;
		record.push(root);
		
		while (record.size()!=0)
		{
			int size = record.size();
			while (size--)
			{
				TreeNode* tmp = record.front();
				record.pop();
				temp.push_back(tmp->val);
				if (tmp->left != NULL)
					record.push(tmp->left);
				if (tmp->right != NULL)
					record.push(tmp->right);
			}
			result.push_back(temp);
			temp.clear();
		
		}
		return result;
	}
};

LeetCode107——二叉树的层次遍历II

 

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其自底向上的层次遍历为:

[
  [15,7],
  [9,20],
  [3]
]

思路:

其实就是把层序遍历的每层结果先放到栈里面,然后出栈在放入容器中,调换了一下输出顺序而已。

代码:

class Solution {
public:
	vector<vector<int>> levelOrderBottom(TreeNode* root) {
		vector<vector<int>>ans;
		if (root == nullptr)
			return ans;
		queue<TreeNode*>q;
		stack<vector<int>>st;
		vector<int>layer;

		TreeNode* pNode = root;
		q.push(pNode);
		while (!q.empty())
		{
			int size = q.size();
			while (size--)
			{
				TreeNode* p = q.front();
				q.pop();
				layer.push_back(p->val);
				if (p->left)
					q.push(p->left);
				if (p->right)
					q.push(p->right);
			}
			st.push(layer);
			layer.clear();
		}
		while (!st.empty())
		{
			ans.push_back(st.top());
			st.pop();
		}
		return ans;

	}
};

 

LeetCode103——二叉树的锯齿形层次遍历

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

 

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回锯齿形层次遍历如下:

[
  [3],
  [20,9],
  [15,7]
]

思路:

其实还是正常的进行层序遍历,只不过设置了一个标志为flag,相邻两层的标志位相反,第一层正常入向量,第二层先入一个临时的栈,最后再入向量中,这样上下两层的节点顺序就相反了。

代码:

class Solution {
public:
	vector<vector<int>>  zigzagLevelOrder(TreeNode* root) {
		vector<vector<int>>ans;
		if (root == nullptr)
			return ans;
		queue<TreeNode*>q;
		stack<vector<int>>st;
		vector<int>layer;

		TreeNode* pNode = root;
		q.push(pNode);
		bool flag = true;
		stack<int>rev;
		while (!q.empty())
		{
			int size = q.size();
			while (size--)
			{
				TreeNode* p = q.front();
				q.pop();
				if(flag==true)
					layer.push_back(p->val);
				else
				{
					rev.push(p->val);
				}
				
				if (p->left)
					q.push(p->left);
				if (p->right)
					q.push(p->right);
			}

			if (flag == true)
			{
				flag = false;
			}
			else
			{
				while (!rev.empty())
				{
					int temp = rev.top();
					layer.push_back(temp);
					rev.pop();
				}
				flag = true;
			}
			ans.push_back(layer);
			layer.clear();

			
			
		}
		
		return ans;

	}
};

LeetCode199——二叉树的右视图

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例:

输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

思路:

其实还是二叉树的层序遍历,只不过保留的是每一层节点的最后一个节点的值。

代码:

class Solution {
public:
	vector<int> rightSideView(TreeNode* root) {
		vector<int>ans;
		if (root == NULL)
			return ans;
		TreeNode* pNode = root;
		queue<TreeNode*>q;
		q.push(pNode);

		while (!q.empty())
		{
			int size = q.size();
			while (size--)
			{
				TreeNode* temp = q.front();
				q.pop();
				if (size == 0)
					ans.push_back(temp->val);
				if (temp->left)
					q.push(temp->left);
				if (temp->right)
					q.push(temp->right);
			}
		}
		return ans;
	}
};

 

LeetCode279——完全平方数的和

题目:

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.
示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

 

思路:

其实动态规划的做法比较好想到,这里是用了图论的方法,比如从10开始有10->9,10->6,10->1,这三条路径,然后把这三个点入队列,把step+1,依次取出这些节点,然后放入他们能够到达的下一个节点,step继续加一。注意这里有很多重复计算,可以设置一个visited数组记录访问过的元素,然后跳过计算。直到出现值为0的节点,返回step就可以了。

class Solution {
public:
	int numSquares(int n) {

		queue<pair<int, int>>q;
		q.push(make_pair(n, 0));
		vector<bool>visited(n + 1, false);
		visited[n] = true;

		while (!q.empty())
		{
			int num = q.front().first;
			int step = q.front().second;
			q.pop();

			for (int i = 1;; i++)
			{
				int a = num - i * i;
				if (a < 0)
					break;
				if (a == 0)
					return step + 1;
				if (!visited[a])
				{
					q.push(make_pair(a, step + 1));
					visited[a] = true;
				}
			}
		}
        throw invalid_argument("No Solution");
	}
};

 

//动态规划的做法
class Solution {
public:
	int numSquares(int n) {
		vector<int>dp(n + 1);
		dp[0] = 0;
		
		for (int i = 1; i <= n; i++)
		{
			int min = 9999;

			for (int j = 1; j <= int(sqrt(n));j++)
			{
				if (i-j*j >=0)
				{
					int temp = dp[i - j * j];
					if (temp + 1 < min)
					{
						dp[i] = temp + 1;
						min = dp[i];
					}
						

				}
			}
		}
		return dp[n];
	}
};

 

优先级队列的底层是“最大堆”,用法要特别注意,常用来解决topK的问题。

LeetCode347——前k个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

输入: nums = [1], k = 1
输出: [1]

思路:

首先还是用map获得对应数字的出现频率,然后创建一个大小为k的优先级队列,底层选择最小堆实现,然后放入pair(频率,数字),每当一个新的map中的pair过来的时候(注意这个时候两个pair里面的元素位置是反过来的),比较频率的大小,如果新的频率大就加入优先级队列中,不大就不做操作,这样最后优先级队列里面的就是出现频率最多的k元素。

代码:

class Solution {
public:
	vector<int> topKFrequent(vector<int>& nums, int k) {
		int len = nums.size();
		vector<int>res;
		unordered_map<int,int>mp;//元素对应的频率;
		for (int i = 0; i < len; i++)
		{
			mp[nums[i]]++;
		}

		priority_queue<pair<int, int>, vector < pair<int, int>>, greater<pair<int, int>>> pq;
		//降序队列
		//priority_queue <int, vector<int>, greater<int> > q;
	    //升序队列(默认)
	    //priority_queue <int, vector<int>, less<int> >q;
		for (unordered_map<int, int>::iterator it = mp.begin(); it != mp.end(); it++)
		{
			if (pq.size() == k)
			{
				if (it->second > pq.top().first)
				{
					pq.pop();
					pq.push(make_pair(it->second, it->first));
				}
            }
            else
                pq.push(make_pair(it->second, it->first));
			
		}
		
		while (pq.size()!=0)
		{
			res.push_back(pq.top().second);
			pq.pop();
		}
		return res;
	}
};

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值