01.有5个元素,其入栈次序为A,B,C,D,E,在各种可能的出栈次序中,第一个出栈元素
为C且第二个出栈元素为D的出栈序列有哪几个?
CD出栈后的状态如下图所示。
此时有如下3种操作:①E进栈后出栈,出栈序列为CDEBA;②B出栈,E进栈后出栈,出
栈序列为CDBEA:③B出栈,A出栈,E进栈后出栈,出栈序列为CDBAE。
所以,以CD开头的出栈序列有CDEBA、CDBEA、CDBAE三种。
02.若元素的进栈序列为A,B,C,D,E,运用栈操作,能否得到出栈序列B,C,A,E,D和D,B,
A,C,E?为什么?
能得到出栈序列BCAED。可由A进,B进,B出,C进,C出,A出,D进,E进,E出,
D出得到。不能得到出栈序列DBACE。若出栈序列以D开头,说明在D之前的入栈元素是A、B
和C,三个元素中C是栈顶元素,B和A不可能早于C出栈,所以不可能得到出栈序列DBACE。
03.栈的初态和终态均为空,以I和0分别表示入栈和出栈,则出入栈的操作序列可表
示为由I和O组成的序列,可以操作的序列称为合法序列,否则称为非法序列。
1)下面所示的序列中哪些是合法的?
A.IOIIOIOO B.IOOIOIIO C.IIOIOIO D.IIOOIOO
2)通过对1)的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回
true,否则返回false(假定被判定的操作序列已存入一维数组中)。
1)A、D合法,而B、C不合法。在B中,先入栈1次,再连续出栈2次,错误。在C中,
入栈和出栈次数不一致,会导致最终的栈不空。A、D均为合法序列,请自行模拟。注意:在操
作过程中,入栈次数一定大于或等于出栈次数:结束时,栈一定为空。
2)设被判定的操作序列已存入一维数组A中。算法的基本设计思想:依次逐一扫描入栈出
栈序列(即由“1”和“O”组成的字符串),每扫描至任意一个位置均需检查出栈次数(即“O”
的个数)是否小于入栈次数(“I”的个数),若大于则为非法序列。扫描结束后,再判断入栈和出
栈次数是否相等,若不相等则不合题意,为非法序列。
bool Judge(char A[]) {
int i = 0;
int j = k = 0; // i为下标,方和k分别为字母1和O的个数
while (A[i] != '\0') { //未到字符数组尾
switch (A[i]) {
case 'I':
j++;
break; // 入栈次数增1
case 'O':
k++;
if (k > j) {
printf("序列非法\n");
exit(0);
}
}
i++; // 不论A[i]是I还是O,指针i均后移
}
if (j != k) {
printf("序列非法\n");
return false;
} else {
printf("序列合法\n"):
return true;
}
}
【另解】入栈后,栈内元素个数加1:出栈后,栈内元素个数减1,因此可将判定一组出入栈
序列是否合法转化为一组由+1、-1组成的序列,它的任意前缀子序列的累加和不小于0(每次出
栈或入栈操作后判断)则合法:否则非法。
04.设单链表的表头指针为L,结点结构由data和next两个域构成,其中data域为字符型。
试设计算法判断该链表的全部n个字符是否中心对称。例如xyx、xyyx都是中心对称。
算法思想:使用栈来判断链表中的数据是否中心对称。让链表的前一半元素依次进栈。在处
理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,
若相等,则将链表中的下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若栈是空栈,
则得出链表中心对称的结论:否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非
中心对称,结束算法的执行。
int dc(LinkList L, int n) {
int i;
char s[n / 2]; // s字符栈,链表前一半元素进栈
LNode *p = L->next; // 工作指针p,指向待处理的当前元素
for (i = 0; i < n / 2; i++) {
s[i] = p->data;
p = p->next;
}
i--; // 恢复最后的1值
if (n % 2 == 1) {
p = p->next; // 若n是奇数,后移过中心结点
}
while (p != NULL && s[i] == p->data) {
i--; // 检测是否中心对称,i充当栈顶指针
p = p->next;
}
if (i == -1) {
return 1; // 栈为空栈,链表中心对称
} else {
return 0; // 链表不中心对称
}
}
算法先将“链表的前一半”元素(字符)进栈。当为偶数时,前一半和后一半的个数相同:
当为奇数时,链表中心结点字符不必比较,移动链表指针到下一字符开始比较。比较过程中遇
到不相等时,立即退出while循环,不再进行比较。
本题也可以先将单链表中的元素全部入栈,然后扫描单链表工并比较,直到比较到单链表工
尾为止,但算法需要两次扫描单链表工,效率不及上述算法高。
05.设有两个栈S1、S2都采用顺序栈方式,并共享一个存储区[0,…,maxsize-1],为了
尽量利用空间,减少溢出的可能,可采用栈顶相向、迎面增长的存储方式。试设计S1、
S2有关入栈和出栈的操作算法。
两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,S1栈顶指针为-1,S2栈顶指
针为maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。
#define maxsize 100 // 两个栈共享顺序存储空间所能达到的最多元素数,初始化为100
#define elemtp int // 假设元素类型为整型
typedef struct {
elemtp stack[maxsize]; // 栈空间
int top[2]; // top为两个栈顶指针
} stk;
stk s; // s是如上定义的结构类型变量,为全局变量
本题的关键在于,两个栈入栈和退栈时的栈顶指针的计算。S1栈是通常意义下的栈;而S2
栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。
此外,对于所有栈的操作,都要注意“入栈判满、出栈判空”的检查。
(1)入栈操作
int push(int i, elemtp x) {
// 入栈操作。1为栈号,i=0表示左边的S1栈,i=1表示右边的S2栈,x是入栈元素。
// 入栈成功返回1,否则返回0
if (i < 0 || i > 1) {
printf("栈号输入不对");
exit(0);
}
if (s.top[1] - s.top[0] == 1) {
printf("栈已满\n");
return 0;
}
switch (i) {
case 0:
s.stack[++s.top[0]] = x;
return 1;
break;
case 1:
s.stack[--s.top[1]] = x;
return 1;
}
}
(2)退栈操作
elemtp pop(int i) {
// 退栈算法。i代表栈号,i=0时为S1栈,i=1时为S2栈。退栈成功返回退栈元素,否则返回-1
if (i < 0 || i > 1) {
printf("栈号输入错误\n");
exit(0);
}
switch (i) {
case 0:
if (s.top[0] == -1) {
printf("栈空\n");
return -1;
} else
return s.stack[s.top[0]--];
break;
case 1:
if (s.top[1] == maxsize) {
printf("栈空\n");
return -1;
} else
return s.stack[s.top[1]++];
break;
}
}