DS3.1

文章探讨了栈的操作,包括出栈序列的确定、栈操作的合法性检查、链表中心对称性的判断,以及共享存储区的顺序栈设计。重点介绍了如何利用栈实现对链表中心对称性的高效检测算法。
摘要由CSDN通过智能技术生成

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值