剑指 offer:栈的压入、弹出序列

博客围绕判断栈的压入和弹出顺序展开。先给出题目,强调编程时思考思想的重要性。分析中提到最初找元素进栈出栈规律的方法复杂度高,后采用模拟进栈与出栈的方法,还反思了自己代码存在的问题,如用map数组判断元素是否在栈中没必要等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

前言
现在我越来越觉得变成的时候思想很重要,不是编完了就完事了,而应该清楚自己是怎么想的,有没有更好的想法,自己的思维漏洞在哪里。看别人写的代码的时候,更应该思考别人的思想是什么,或者简单一点,思路是什么,下次碰到一个类似的题目应该怎么去想。如果不去思考这些东西,那么进步是微小的,好一点的结果也只是你记住了这道题的解法,是无法融会贯通的。而如果你懂了编程背后的思想,就可以举一反三,触类旁通。这样收获才是巨大的。当然了,冰冻三尺,非一日之功。我会慢慢来,总有一天,我会变成编程高手。

分析
判断弹出序列是否对应压栈序列,就要看其有没有违反栈的定义:后进先出。那么就要找到一个元素 a 在另一个元素 b 之前进栈,却在其之后进栈。直觉是根据它们在两个数组中的下标来判断,不过发现这样行不通,需要加入一个第三者元素 c 来辅助判断。于是就开始了尝试的过程,然后我找到了方法:假设有三个元素进栈的顺序为x, y, z, 且满足 x < y < z, 但出栈的顺序却为 z,x, y, 按照数组下标排序的话,进栈为:小,中,大,出栈为:大,小,中,那么,就说明出错了。

这个做法需要找到这个规律,但有些人不一定能找到,且时间复杂度为 O(n)2O(n)^2O(n)2, 偏高。后来我看了别人的做法,知道了 模拟 这种方法,即一件事的发生是有过程的,那么就编程取模拟这个过程,就能发现结果是对是错。发现了这个方法后,我不急于去看别人的代码,想自己先实现一边,再和别人的代码对照。
模拟进栈与出栈:我的思路是遍历 popV 中的每一个元素,判断其是否已压入栈中,若没有,那么对 pushV 进行压入,一直到 pushV[i]=popV[j]pushV[i] = popV[j]pushV[i]=popV[j] 为止,然后去除栈顶元素;若已在栈中,那么判断栈顶元素是否等于 popV[j], 正确的答案是应该相等的。若出错,结束,否则重复上述过程。后来,与别人的代码进行比较,发现我的代码写得真是太烂了。

  1. 为了判断 popV[j]popV[j]popV[j] 是否在栈中,我用了 map 数组,即若元素进栈,则将其值置 1,实际上这是没必要的,只需判断在栈顶就可以了,因为 popV[j]popV[j]popV[j] 是当前要出栈的元素。
  2. 我将pushV[i]pushV[i]pushV[i]popV[j]popV[j]popV[j] 作比较,这样走了弯路,因为有栈了,popV[j]popV[j]popV[j] 应该直接与栈顶元素作比较,有多个可比较元素的时候,与谁比较是不一样的。

这个题目之后有必要再做一遍,应该尽量做到用最简洁的方式 来做。

下面就贴一下我的不尽如人意的代码吧。

//方法一
bool IsPopOrder(vector<int> pushV, vector<int> popV) {
	if (pushV.empty() || popV.empty()) {
		return 0;
	}
	map<int, int> Map;
	for (int i = 0; i < pushV.size(); ++i) {
		Map[pushV[i]] = i;
	}
	if (pushV.size() == 1 && pushV[0] != popV[0]) {
		return 0;
	}
	for (int i = 1; i < popV.size() - 1; ++i) {
		for (int j = 0; j < i; ++j) {
			if (!Map.count(popV[j]) || !Map.count(popV[i]))
				return 0;
			if (Map[popV[j]] > Map[popV[i]]) {
				for (int k = i + 1; k < popV.size(); ++k) {
					if (!Map.count(popV[k])) {
						return 0;
					}
					if (Map[popV[k]] > Map[popV[i]] && Map[popV[k]] < Map[popV[j]]) {
						return 0;
					}
				}
			}
		}
	}
	return 1;
}

//方法二
bool IsPopOrder2(vector<int> pushV, vector<int> popV) {
	vector<int> s;
	int n = pushV.size();
	map<int, int> in;
	int i = 0;
	int j = 0;
	if (pushV.size() == 1 && pushV[0] != popV[0]) {
		return 0;
	}
	for (i = 0; i < n; ++i) {
		if (in.count(popV[i])) {
			if (!s.empty() && s[s.size() - 1] == popV[i]) {
				s.pop_back();
			}
			else {
				return 0;
			}
		}
		else { // 栈中没有该元素,那么一直进栈
			
			for (; j < n; ++j) {
				if (pushV[j] != popV[i]) {
					s.push_back(pushV[j]);
					in[pushV[j]] = 1; //代表该元素已进栈
				}
				else {
					s.push_back(pushV[j]);
					in[pushV[j]] = 1;
					++j;
					break;
				}
			}
			//没有该元素 popV[i]
			/*if (j == n) {
				return 0;
			}*/
			s.pop_back();
		}
	}
	if (i == n) {
		return 1;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值