栈混洗

420 阅读2分钟

摘自《数据结构(C++语言版)》

描述
考查三个栈A、B和S,其中A含有n个元素,自顶而下构成输入序列: { a1, a2, …, an }
B和S初始为空。
若只允许通过S.push(A.pop())弹出栈A的顶元素并压入栈S中,或通过B.push(S.pop())弹出S的顶元素并压入栈B中,则在经过一系列这样的操作后,当栈A和S均为空时,原A中的元素应均已转入栈B。
此时,若将B中元素自底而上构成的序列记作: { ak1, ak2, …, akn }
则该序列称作原输入序列的一个栈混洗(stack permutation)

如图所示,设初始状态下栈A = { 1, 2, 3, 4 },栈S和B均为空;经过“随机的”8
次操作,A中的元素全部转入栈B中。此时,栈B中元素自底而上对应的序列{ 3, 2, 4, 1 },
即是原序列{ 1, 2, 3, 4 }的一个栈混洗。除了“实施出栈操作的栈不得为空”,以上过程并
无更多限制,操作序列具有很大的随机性,故同一输入序列的栈混洗并不唯一。比如就上例而言,
{ 1, 2, 3, 4 }、{ 4, 3, 2, 1 }以及{ 3, 2, 1, 4 }等也是栈混洗。

从图也可看出,一般地对于长度为n的输入序列,每一栈混洗都对应于由栈S的n次push
和n次pop构成的某一合法操作序列,比如{ 3, 2, 4, 1 }即对应于操作序列:
{
push,
push,
push, pop, pop,
push, pop, pop }
反之,由n次push和n次pop构成的任何操作序列,只要满足“任一前缀中的push不少于
pop”这一限制,则该序列也必然对应于某个栈混洗

应用
括号匹配

  • 递归版本
void trim(const char exp[], int& lo, int& hi) { //初除表达式exp[lo, hi]不含括号的最长前缀、后缀
	while ((lo <= hi) && (exp[lo] != '(') && (exp[lo] != ')')) lo++; //查找第一个和
	while ((lo <= hi) && (exp[hi] != '(') && (exp[hi] != ')')) hi--; //最后一个括号int divide(const char exp[], int lo, int hi) { //切分表达式exp[lo, hi],使exp匹配仅当它们匹配
int mi = lo; int crc = 1; //crc为[lo, mi]范围内左、右括号数目之差
while ((0 < crc) && (++mi < hi)) //逐个检查各字符,直到左、右括号数目相等,或者越界
	{ if (exp[mi] == ')') crc--; if (exp[mi] == '(') crc++; } //左、右括号分别计数
	return mi; //若mi <= hi,则为合法切分点;否则,意味着局部不可能匹配
}

bool paren(const char exp[], int lo, int hi) { //检查表达式exp[lo, hi]是否括号匹配(递归版)
	trim(exp, lo, hi); if (lo > hi) return true; //清除不含括号的前缀、后缀
	if (exp[lo] != '(') return false; //首字符非左括号,则必不匹配
	if (exp[hi] != ')') return false; //末字符非右括号,则必不匹配
	int mi = divide(exp, lo, hi); //确定适当的切分点
	if (mi > hi) return false; //切分点不合法,意味着局部以至整体不匹配
	return paren(exp, lo + 1, mi - 1) && paren(exp, mi + 1, hi); //分别检查左、右子表达式
}
  • 迭代版本
bool paren(const char exp[], int lo, int hi) { //表达式括号匹配检查,可兼顾三种括号
	Stack<char> S; //使用栈记录弽已出现但尚未匹配的左括号
	for (int i = 0; exp[i]; i++) /* 逐一检查当前字符 */
	switch (exp[i]) { //左括号直接进栈;右括号若与栈顶失配,则表达式必不匹配
	case '(': case '[': case '{': S.push(exp[i]); break;
	case ')': if ((S.empty()) || ('(' != S.pop())) return false; break;
	case ']': if ((S.empty()) || ('[' != S.pop())) return false; break;
	case '}': if ((S.empty()) || ('{' != S.pop())) return false; break;
	default: break; //非括号字符一率忽略
	}
	return S.empty(); //整个表达式扫描过后,栈中若仍残留(左)括号,则不匹配;否则(栈空)匹配
}