5栈与单调栈

用栈实现进制转换


2020CSP真题--优秀的拆分

题目
传送门

思路一
看到这种2的各个幂之和的形式,想到了二进制转十进制的实现方法与之十分相似,所以可以将所给十进制化为二进制,二进制位为1的输出相应的数字,二进制位为0的不输出。另外,可以在输入十进制数字之后先特判一下 ,如果是奇数或者0直接输出-1(奇数不可能实现优秀的拆分)。

注意点

  1. 在进行奇偶性的判断时,可以用n & 1 == 1作为条件,位运算的效率更高
  2. n /= 2换成n >> 1效率更高。
  3. 第一次提交出错,发现当输入为9999998时输出为科学计数法,故要禁用科学计数法。输出禁用科学计数法:cout << fixed << num;
  4. 输出设置小数点位数:cout << setprecision(i) << num;

思路一代码

#include
using namespace std;

int n;
stack <int> st;
int len;


int main()
{
	cin >> n;
	if (n & 1 ==  1 || n == 0) { //按位与的效率更高,如果是奇数或者0则输出-1
		cout << -1 <<endl;
		return 0; 
	}
	while (n) { //压栈 
		st.push(n % 2);
		len++;
		n = n >> 1; //右移比除法效率更高 
	}
	int t = 0; 
	while (!st.empty()) { //弹栈,在此过程中输出 
		if (st.top() == 1) { //如果二进制位为1 
			cout << fixed << setprecision(0) << pow(2, len - 1 - t) << " ";
			st.pop();
		}
		else { //如果二进制位为0 
			st.pop();
		}
		t++; 
	}
	return 0; 
}

思路二及代码

传送门

表达式括号匹配

题目
传送门

思路
遍历字符串,
遇到"(“压栈,
遇到”)“看栈是否为空或者栈顶元素是否不为”(",如果满足其一,那么就输出"NO";

遍历结束后,
如果栈非空,输出"NO",
否则输出"YES"。

注意点
在遇到")“时,要先看栈是否为空,而不只是简单地看st.top()是否为”(",这样会避免栈越界访问。举个例子,如果我只是简单地看st.top()是否为"(",那么在输入为")"时就不会正确输出。

代码

#include
using namespace std;

string s;
stack <char> st;

int main()
{
	cin >> s;
	for (int i = 0; i < s.length(); i++) {
		if (s[i] == '(') {
			st.push('(');
		}
		else if (s[i] == ')') {
			if (st.empty() || st.top() != '(') {
			//这里是个坑,如果只写 st.top() != '(',并且栈又是空的情况下,就会越界访问
			//所以要加上st.empty()的判断 
				cout << "NO" <<endl;
				return 0; 
			}
			else st.pop();
		}
	}
	if (st.empty()) cout << "YES" << endl;
	else cout << "NO" << endl;
	return 0;
}

最小栈


题目
设计一个支持push,pop,top操作,并能在常数时间内检索到最小元素的栈。

思路
如果我们只是一个普通的栈,寻找最小元素需要我们遍历整个栈,时间复杂度是O(n),而题目要求为O(1),故我们需要引入一个辅助栈,称为最小栈
我在知乎上看到一篇比较好的文章,可以通过这篇文章简单理解最小栈。最小栈
通过最小栈,我们实现了时间O(1)+空间O(n),属于牺牲空间降时间。

注意点
push操作中,如果栈空,直接压栈,不需要比较待压栈元素和栈顶元素的大小,防止栈的越界访问。我发现,一旦涉及到top()和pop(),就要考虑到栈的判空。

代码

#include
using namespace std;

int n, num;
stack <int> st, helper; //普通栈和辅助栈(最小栈)

void push(int num) {
	st.push(num);
	if (helper.empty()) { //这里要注意,要先判空,不能直接和栈顶元素比较 
		helper.push(num);
	}
	else {
		helper.push(min(num, helper.top()));
	}
}

void pop() {
	if (!st.empty()) st.pop();
	if (!helper.empty()) helper.pop();
}

int getMin() {
	return helper.top();
}

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> num;
		push(num);
	}
	cout << "最小元素为:" << getMin() << endl;
	return 0;
}

后缀(逆波兰)表达式求值


5栈与单调栈_第1张图片

中缀表达式转后缀表达式


5栈与单调栈_第2张图片

编程,用栈模拟八皇后问题


后续讲解了dfs之后再来讲解。

单调栈


刚我们学习了最小栈,我们再来看一下单调栈。
单调递增栈:栈中元素从栈底到栈顶是递增的。
单调递减栈:栈中元素从栈底到栈顶是递减的。

单调栈用途不太广泛,只处理一种典型的问题,叫做 Next Greater Element
这里给出两个讲的比较好的文章文章一、文章二,文章中有动画演示,帮助理解单调栈解决下一更大元素问题
5栈与单调栈_第3张图片代码

#include
using namespace std;

int n;
int num[999]; //存放输入数字 
int res[999]; //存放res[i]走到下一个比res[i]大的元素的步数 
struct node {
	int data; //下一更大数字的值 
	int no;   //下一更大数字的下标 
};
node t;
stack <node> st; //栈中存放的是node型 

void nextGreaterElement() {
  for (int i = n - 1; i >= 0; i--) {
    //出栈 
    while (!st.empty() && num[i] >= st.top().data) st.pop();
    
    //记录
    res[i] = st.empty() ? -1 : st.top().no - i;
    
    //入栈
    t.data = num[i];
    t.no = i;
    st.push(t); 
  }
}

int main()
{
  cin >> n;
  for (int i = 0; i < n; i++) {
    cin >> num[i];
  }
  nextGreaterElement();
  for (int i = 0; i < n; i++) {
    cout << res[i] <<" ";
  }
  return 0;
} 

力扣上有人整理了单调栈相应的题,如果有时间有能力可以练习一下(哎既没时间又没能力的我…):
5栈与单调栈_第4张图片

课后作业


牛的视野

题目
一群高度不完全相同的牛从左到右站成一排,每头牛只能看见它右边的比它矮的牛的发型,若遇到一头高度大于或等于它的牛,则无法继续看到这头牛和后面的其他牛的发型。给出这些牛的高度,要求每头牛可以看到的牛的数量的和。

我的思路
本质还是下一更大元素问题。最终的总数 = res数组中非-1元素全部减1最后加到一起 + (-1的个数 - 1)

注意点
-1的处理。

代码

#include 
using namespace std;

int	n;
int	num[999];       /* 存放输入数字 */
int	res[999];       /* 存放res[i]走到下一个比res[i]大的元素的步数 */
struct node {
	int	data;   /*下一更大数字的值 */
	int	no;     /*下一更大数字的下标 */
};
node		t;
stack <node>	st;     /* 栈中存放的是node型 */
int		sum;    /* 牛能看到的发型个数之和 */

void nextGreaterElement()
{
	for ( int i = n - 1; i >= 0; i-- )
	{
		/* 出栈 */
		while ( !st.empty() && num[i] >= st.top().data )
			st.pop();

		/* 记录 */
		res[i] = st.empty() ? -1 : st.top().no - i;

		/* 入栈 */
		t.data	= num[i];
		t.no	= i;
		st.push( t );
	}
}


int main()
{
	cin >> n;
	for ( int i = 0; i < n; i++ )
	{
		cin >> num[i];
	}
	nextGreaterElement();
	//计算出res数组,答案就好求了
	//最终的总数 = res数组中非-1元素全部减1最后加到一起 + (-1的个数 - 1)
	//举例
	//输入数字:                 10 3 7 4 12  2
	//res数组:                  4  1 2 1 -1 -1
	//res数组中非-1元素全部减1: 3  0 1 0 -1 -1
	//res数组中非-1元素全部减1最后加到一起, 3 + 1 = 4 
	//-1的个数再减1:                        2 - 1 = 1
	//最终答案就是                           4 + 1 = 5 
	for ( int i = 0; i < n; i++ )
	{
		if ( res[i] != -1 )
			sum += res[i] - 1;
		else {
			sum += n - i - 1;
		}
	}
	cout << sum << endl;
	return(0);
}

老师的思路
5栈与单调栈_第5张图片代码

传送门

你可能感兴趣的:(麦克算法,算法,c++)