数据结构复习之栈


比较基础,但很重要,一般写程序时,都会用到或多或少的数据结构。

栈(Stack):是限定在表的一端进行插入和删除运算的线性表,通常将插入、删除的一端称为栈项(top),另一端称为栈底(bottom)。不含元素的空表称为空栈。
栈的修改是按后进先出的原则进行的,因此,栈又称为后进先出(Last In First Out)的线性表,简称为LIFO表。

栈的基本运算

(1)置空栈InitStack(&S):构造一个空栈S。
(2)判栈空StackEmpty(S):若栈S为空栈,则返回TRUE,否则返回FALSE。
(3)判栈满StackFull(S):若栈S为满栈,则返回TRuE,否则返回FALSE。
(4)进栈(又称入栈或插入):Push(&S,x):将元素x插入S栈的栈顶。
(5)退栈(又称出栈或删除)Pop(&s):若栈S为非空,则将S的栈顶元素删除,并返回栈顶元素。
(6)取栈顶元素GetTop(S):若S栈为非空,则返回栈顶元素,但不改变栈的状态。

顺序栈

栈的顺序存储结构称为顺序栈。顺序栈也是用数组实现的,栈底位置是固定不变的,将栈底位置设置在数组的最低端(即下标为0);栈顶位置是随着进栈和退栈操作而变化的,一个整型量top来指示当前栈顶位置,通常称top为栈顶指针,是一个假指针。

顺序栈数据结构描述

#define stackSize 100 //栈空间的大小应根据实际需要来定义,这里假设为100
tvpedef char DataType;//DataType的类型可根据实际情况而定,这里假设为char
typedef Struct {
    DataType data[stackSize]; //数组data用来存放表结点
    int top; //表示栈顶指针
} SeqStack; //栈的数据类型
SeqStack s; //s为栈类型的变量

顺序栈运算实现

置空栈
void InitStack(SeqStack *s)
{
s->top=-1;//也就是数组下标0,-1代表了数组不可能的元素。
}
判栈空
int StackEmpty(SeqStack *S)
{
 return S->top==-1;
)
判栈满
int StackFull(SeqStack *S)
{
 return S->top==StackSize-1;//如果栈满则S->top== StackSize -1的值为1,返回1,反之,返回0
}
入栈
void Push(SeqStack *S,DataType x)
{
    if (StackFull(S)) //调用判满函数
        printf("stack overflow");
    else {
        S->top = S->top + 1; //栈顶指针加1
        S->data[S->top] = x; //将x入栈
    }
}
出栈
DataType Pop(SeqStack *S)
{
    if (StackEmpty(S)) { //调用判空函数
        printf("Stack underflow");
        exit(0); //出错退出处理
    } else
        return S->data[S->top--];//返回栈顶元素,栈顶指针减1
}
取栈顶元素
DataType GetTop(SeqStack *S)
{
    if (StackEmpty(S)) { //调用判空函数
        printf("stack empty");
        exit(0); //出错退出处理
    } else
        return S->data[S->top] //返回栈顶元素,注意:栈顶指针不动
}
设计技巧

可以一个数组空间装两个栈,栈底在数组两端,栈顶各自向中间移动。

链栈

栈的链式存储结构称为链栈,它是运算受限的单链表,其插入和删除操作仅限制在表头位置上(栈顶)进行,因此不必设置头结点,将单链表的头指针head改为栈顶指针top即可。

链栈数据结构描述

typedef struct stacknode {
    DataType data;
    struct stacknode *next;
} StackNode;
定义结点
typedef StackNode *Linkstack;
LinkStack top;//定义栈顶指针top

链栈上运算实现

判栈空
int StackEmpty(LinkStack top)
{
    return top == NULL ; // 栈空top==NULL的值为1,返回1,否则返回0
}
入栈
LinkStack Push(LinkStack top,DataType x) //将x插入栈顶
{
    //无需判满
    StackNode *P;
    p = (StackNode *)malloc(Sizeof(StackNode)); //申请新的结点
    p->data = x; //申请新的结点
    p->next = top; //新结点*p插入栈顶
    top = p; //更新栈顶指针top
    return top;
}
出栈
LinkStack Pop(LinkStack top, DataType *x)
{
    StackNode *p = top; //保存栈顶指针
    if (StackEmpty(top)) { //栈为空
        printf("stack empty"); //出错退出处理
        exit(0);
    } else {
        *x = p->data; //保存删除结点值,并带回
        top = p->next; //栈顶指针指向下一个结点
        free(p); //删除P指向的结点
        return top; //并返删除后的栈顶指针
    }
}
取栈顶元素
DataType GetTop(LinkStack top)
{
    if (StackEmpty(top)) { //栈为空
        printf("stack empty");
        exit(0); //出错退出处理
    }
    else
        return top->data; //返回栈项结点值
}

应用

圆括号匹配的检验

对于输入的一个算术表达式字符串,试写一算法判断其中圆括号是否匹配,若匹配则返回TRUE,否则返回FALSE。

分析

利用栈的操作来实现:循环读入表达式中的字符,如遇左括号"(“就进栈;遇右括号”)"则判断栈是否为空,若为空,则返回FALSE,否则退栈;循环结束后再判断栈是否为空,若栈空则说明括号匹配,否则说明不匹配。

算法实现

int Expr()
{
    SeqStack S;
    DataType ch, x;
    InitStack(&S); //初始化栈S
    ch = getchar();
    while (ch != '\n') {
        if (ch == '(')
            Push(&S, ch); //遇左括号进栈
        else if (ch == ')')
            if (StackEmpty(&S)) //遇右括号如果栈空,说明不匹配,返回0
                return 0;
            else
                x = Pop(&s); //遇右括号退栈
        ch = getchar(); //读入下一个字符
    }//end of while
    if (StackEmpty(&S)) return 1; //最后栈空,说明匹配,返回1
    else return 0;
}

字符串回文的判断

利用顺序栈的基本运算,试设计一个算法,判断一个输入字符串是否具有中心对称(也就是所谓的"回文",即正读和反读均相同的字符序列),例如ababbaba和abcba都是中心对称的字符串

分析

从中间向两头进行比较,若完全相同,则该字符串是中心对称,否则不是。这就要首先求出字符串串的长度,然后将前一半字符入栈,再利用退栈操作将其与后一半字符进行比较。

算法实现

int symmetry(char str[ ])
{
    SeqStack S;
    int j, k, i = 0;
    InitStack(&S);
    while (Str[i] != '\0') i++; //求串长度
    for (j = 0; j < i / 2; j++)
        Push(&s, str[j]); //前一半字符入栈
    k = (i + 1)/2; //后一半字符在串中的起始位置,下标从0开始的话,奇数的话就是中间,两个字符个数相等
    //特别注意这条命令怎么处理奇偶数个字符的。              偶数的话,中间位置下一个位置,所以这个(i+1)/2很关键
    for (j = k; j < i; j++) //后一半字符与栈中字符比较
        if (str[j] != Pop(&s))
            return 0; //有不相同字符,即不对称
    return 1; //完全相同,即对称
}

将一个非负的十进制整数N转换成d进制

分析

将一个非负的十进制整数N转换成d进制的方法:N除以d,求出每一步所得的余数,然后将所有余数逆序书写就是十进制整数N对应的d进制数。

算法实现

void conversion(int N, int d)
{
    //将一个非负的十进制数N转换成任意的d进制数
    SeqStack S;
    InitStack(&S)
    while (N) {
        Push(&S, N % d); //N除以d的余数入栈
        N = N / d; //N除以d的商
    }
    while (!StackEmpty(&S)) {
        i = Pop(&S);
        printf("%d", i); //出栈完成余数倒序输出
    }
}

递归与栈

栈还有一个非常重要的应用就是在程序设计语言中实现递归。一个直接调用自己或间接调用自己的函数,称为递归函数。
递归是程序设计中一个强有力的工具,递归算法有两个关键条件:一是有一个递归公式;二是有终止条件。

阶乘

求n的阶乘可递归地定义为
在这里插入图片描述

算法实现

#include <stdio.h>
long int fact(int n)
{
    int temp;
    if (n == 0)
        return 1; //递归终止条件
    else
        temp = n * fact(n - 1); //递归公式
r12:
    return temp;
}
//void main() //主函数
int main(int argc, char const *argv[])
{
    long int n;
    n = fact(5); //调用fact()函数求5!
r11:
    printf("5!=%ld", n);
    return 0;
}

过程分析

调用层次调用参数n返回地址temp结果退栈时计算结果
↑5fact(0)0r121
↑4fact(1)1r121* fact(0)1*1=1↓
↑3fact(2)2r122*fact(1)2*1=2↓
↑2fact(3)3r123*fact(2)3*2=6↓
↑1fact(4)4r124*fact(3)4*6=24↓
↑0fact(5)5r115*fact(4)5*24=120返回
进栈主函数打印5!=120 退栈
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值