• 顺序栈和链栈


    <!--author--Kang-->

    <!--time--2020/8/30-->

    一、栈

    栈的定义与特点:

    stack)是限定仅在表尾进行插入或删除操作的线性表。因此,对于栈来说,可以进行插入或删除的表尾端称为栈顶(top),另一端的表头称为 栈底(bottom)

    空栈:若栈中无元素,则称为 空栈。

    进栈(PUSH):元素插入到栈中的操作,也叫入栈,压栈。

    出栈(POP):删除栈顶元素的操作,也称为退栈、弹栈。

    栈的操作符合 后进先出* 原则,因此栈又称为 后进先出的线性表(Last In First Out , LIFO)。

    栈的抽象数据类型定义:

    ADT 栈{

    数据对象:D={ai | ai∈ElemSet, i=1,2,…n,n≥0}

    数据关系:S={<ai-1,ai> | ai-1,ai∈D,i=2,…,n}

    约定a1为栈底,an为栈顶

    基本操作:

    init_stack(*stack)

    操作结果:初始化栈,构造一个空的栈

    get_length(stack)

    初始条件:栈stack已存在

    操作结果:返回栈的长度,即stack中元素个数

    is_empty(stack)

    初始条件:栈stack已存在

    操作结果:若stack为空栈则返回1,否则返回0

    get_top(stack, *e)

    初始条件:栈stack已存在且不为空

    操作结果:用e返回栈顶元素值,不出栈

    push(*stack, e)

    初始条件:栈已存在

    操作结果:插入元素e为新的栈顶元素

    pop(*stack, *e)

    初始条件:栈已存在,且不为空

    操作结果:删除栈顶元素,并用e返回其值

    }ADT 栈

    栈为线性表,有两种存储方式,分别为:顺序栈和链栈

    二、顺序栈的代码实现

    顺序栈的定义及初始化

    须知:1、栈遵循后进先出原则,因此栈顶变化较多,我们需要定义一个栈顶标记来始终指向栈顶元素

    2、初始化时让 top初始化为 -1 ,标记为空栈。当压栈时,top会随着数组下标开始从0变化。即 :进入一个元素,top++一次

    3、获取栈的长度操作,实际为获取栈中有效元素个数,即top所指元素的数组下标 + 1

    #include <stdio.h>
    //顺序栈

    #define MAX_SIZE 100//顺序栈的容量
    typedef int SElemType;//栈中元素的类型,以int为例

    //顺序栈定义
    typedef struct seq_stack {

    SElemType data[MAX_SIZE];//使用数组存储顺序栈的元素
    int top;//栈顶标记,对用数组下标;

    }SEQ_STACK;

    //初始化
    int init_stack(SEQ_STACK *S) {

    S->top = -1;//栈顶标记初始化为 -1 ,此时对应为空栈
    return 1;
    }

    //获取栈的长度(栈中有效元素个数)
    int get_length(SEQ_STACK S) {

    return S.top + 1;
    }

    //判断是否为空栈
    int is_empty(SEQ_STACK S) {
    return S.top == -1;
    }

    入栈与出栈操作:

    注意:

    1.若进行入栈操作时,先判断栈是否满,若栈满则不可继续入栈。

    2.若进行出栈操作时,先判断栈是否为空,若栈空则不可继续出栈操作。

    3.取栈顶元素类似于出栈操作,只不过不删除栈顶元素(即栈顶标记无需自减)

    实现代码如下:

    //入栈和出栈
    /*
    一、入栈:判断栈是否栈满
    二、出栈:判断是否空栈
    三、取栈顶:类似出栈,但不是删除栈顶元素
    */
    int push(SEQ_STACK *S,SElemType e) {
    if (S->top == MAX_SIZE - 1)
    {
    return 0;//操作失败
    }
    S->top++;//栈顶标记 +1 ,指向新的栈顶   这里注意,压栈操作先 栈顶指针 S->top++ 再进元素
    S->data[S->top] = e;
    return 1;
    }

    int get_pop(SEQ_STACK *S, SElemType *e) { //获得栈顶元素
    if (S->top == -1)
    {
    return 0;//空栈,操作失败
    }
    *e = S->data[S->top];//将栈顶元素赋值给指针e指向的变量,

    return 1;
    }

    int pop(SEQ_STACK *S,SElemType *e) {
    if (S->top == -1)
    {
    return 0;//空栈,操作失败
    }
    *e = S->data[S->top];//将栈顶元素赋值给指针e指向的变量,
    S->top--;//栈顶标记 -1 ;这里注意,出栈操作先 取出元素,再栈顶指针 S->top-- 操作
    return 1;
    }

    三、顺序栈源码:

    #include <stdio.h>
    //顺序栈
    #define MAX_SIZE 100//顺序栈的容量
    typedef int SElemType;//栈中元素的类型,以int为例

    //顺序栈定义
    typedef struct seq_stack {

    SElemType data[MAX_SIZE];//使用数组存储顺序栈的元素
    int top;//栈顶标记,对用数组下标;

    }SEQ_STACK;

    //初始化
    int init_stack(SEQ_STACK *S) {

    S->top = -1;//栈顶标记初始化为 -1 ,此时对应为空栈
    return 1;
    }

    //获取栈的长度
    int get_length(SEQ_STACK S) {

    return S.top + 1;
    }

    //判断是否为空栈
    int is_empty(SEQ_STACK S) {
    return S.top == -1;
    }

    //入栈和出栈
    /*
    一、入栈:判断栈是否栈满
    二、出栈:判断是否空栈
    三、取栈顶:类似出栈,但不是删除栈顶元素
    */
    int push(SEQ_STACK *S,SElemType e) {
    if (S->top == MAX_SIZE - 1)
    {
    return 0;//操作失败
    }
    S->top++;//栈顶标记 +1 ,指向新的栈顶   这里注意,压栈操作先 栈顶指针 S->top++ 再进元素
    S->data[S->top] = e;
    return 1;
    }

    int get_pop(SEQ_STACK *S, SElemType *e) { //获得栈顶元素
    if (S->top == -1)
    {
    return 0;//空栈,操作失败
    }
    *e = S->data[S->top];//将栈顶元素赋值给指针e指向的变量,

    return 1;
    }

    int pop(SEQ_STACK *S,SElemType *e) {
    if (S->top == -1)
    {
    return 0;//空栈,操作失败
    }
    *e = S->data[S->top];//将栈顶元素赋值给指针e指向的变量,
    S->top--;//栈顶标记 -1 ;这里注意,出栈操作先 取出元素,再栈顶指针 S->top-- 操作
    return 1;
    }

    int main() {
    SEQ_STACK stack;
    init_stack(&stack);
    //入栈
    push(&stack,1);
    push(&stack, 3);
    push(&stack, 5);

    SElemType e;
    while (stack.top != -1)
    {
    int rlt1 = pop(&stack, &e);
    if (rlt1 == 1)
    {
    printf("出栈:%d ", e);
    }
    else {
    printf("出栈失败!");
    }
    }
    //出栈顺序 5、3、1 先入后出

    return 0;
    }

    调试结果:

     

    四、链栈的代码实现

    链栈的定义:

    链栈通常用单链表表示,其结点的结构与单链表的结构基本相同。区别在与:单链表中为了结点操作方便而设置了一个“头结点”,而链栈中不需要设置头结点。

    P.S:头结点的作用:

    1、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.如果当链表为空的时候,头结点的指针域的数值为NULL.

    2、是为了方便单链表的特殊操作,插入在表头或者删除第一个结点.这样就保持了单链表操作的统一性!

    3、单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会 [1] 。

    4、对单链表的多数操作应明确对哪个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表因为有头结点,首元结点、中间结点的操作相同,从而减少分支,使算法变得简单,流程清晰。对单链表进行插入、删除操作时,如果在首元结点之前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在TurboC算法的函数形参表中头指针一般使用指针的指针(在C++中使用引用&);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可,对初学者更易接受。

    我们知道,栈的特点是后进先出,而无论是入栈、出栈还是其他操作都只是在操作栈顶元素,用头指针指向栈顶即能直接操作栈顶元素,因此不需要头结点;不需用头结点;不需用头结点;

    //链栈
    /*
    头指针直接指向首元结点
    */
    typedef int SElemType;//定义栈元素的类型
    typedef struct stack_node {
    SElemType data;//数据域
    struct stack_node *next;
    c
    }STACK_NODE,*LINK_STACK;//STACK_NODE定义新结点,*LINK_STACK定义链栈

    链栈初始化:

    保证链栈初始为空栈

    int init_stack(LINK_STACK *S) {
    //单链表初始化需要给头结点分配空间,这里不需要头结点,直接置空
    *S = NULL;
    return 1;
    }

    判断是否为空栈:

    //判断是否为空
    int is_empty(LINK_STACK S) {
    return S == NULL;
    }

    获取链栈的长度:

    即链栈中有效结点个数

    1、定义结点指针指向栈顶元素

    2、判断该结点是否有效,即栈顶元素是否为空

    3、循环遍历,统计长度,注意栈顶指针后移

    //获取链栈长度:获取栈中有效结点个数
    int get_length(LINK_STACK S) {
    int length = 0;
    STACK_NODE *tmp = S;
    while (tmp!=NULL)
    {
    length++;
    tmp = tmp->next;
    }
    return length;
    }

    入栈:

    1、定义新结点,并给新结点分配空间

    2、判断新结点是否为空

    3、给新结点的数据域赋值

    4、将栈顶针指地址赋值给新结点的下一地址

    5、将新结点的地址给栈顶指针

    int push(LINK_STACK *S,SElemType e) {
    STACK_NODE *p_top = (stack_node *)malloc(sizeof(STACK_NODE));//定义并给新结点分配空间
    if (p_top ==NULL)
    {
    return 0;
    }
    p_top->data = e;
    p_top->next = *S;//新结点指针域指向当前的链栈栈顶
    *S = p_top;//修改栈顶指针为新的结点
    return 1;
    }

    出栈:

    与入栈操作类似

    注意要将出栈的结点空间释放掉

    1、判断栈顶指针是否为空

    2、通过指针变量返回栈顶元素的数据域

    3、定义指针p_top指向当前栈顶

    4、修改栈顶元素,将栈顶指针指向下一结点next

    5、释放原栈顶元素空间

    //出栈
    int pop(LINK_STACK *S,SElemType *e) {
    if (*S==NULL)//判断是否空栈
    {
    return 0;
    }
    *e = (*S)->data;//将栈顶元素赋值给指针e所指向的变量
    STACK_NODE *p_top = *S;
    *S = p_top->next;//将栈顶指针的下一结点地址赋值给栈顶指针*S  
    free(p_top);//空间释放

    return 1;
    }

    取栈顶:

    类似出栈,但不需要删除栈顶元素

    //取栈顶:取栈顶元素不会删除栈顶结点
    int get_pop(LINK_STACK *S, SElemType *e) {
    if (*S == NULL)//判断是否空栈
    {
    return 0;
    }
    *e = (*S)->data;//将栈顶元素赋值给指针e所指向的变量
    return 1;
    }

    五、链栈源码:

    #include <stdio.h>
    #include <malloc.h>
    //链栈
    /*
    头指针直接指向首元结点
    */
    typedef int SElemType;//定义栈元素的类型
    typedef struct stack_node {
    SElemType data;//数据域
    struct stack_node *next;

    }STACK_NODE,*LINK_STACK;
    //链栈初始化:保证栈为空
    int init_stack(LINK_STACK *S) {
    //单链表初始化需要给头结点分配空间,这里不需要头结点,直接置空
    *S = NULL;
    return 1;
    }
    //判断是否为空
    int is_empty(LINK_STACK S) {
    return S == NULL;
    }

    //获取链栈长度:获取栈中有效结点个数
    int get_length(LINK_STACK S) {
    int length = 0;
    STACK_NODE *tmp = S;
    while (tmp!=NULL)
    {
    length++;
    tmp = tmp->next;
    }
    return length;
    }
    //入栈、出栈、取栈顶
    int push(LINK_STACK *S,SElemType e) {
    STACK_NODE *p_top = (stack_node *)malloc(sizeof(STACK_NODE));//定义并给新结点分配空间
    if (p_top ==NULL)
    {
    return 0;
    }
    p_top->data = e;
    p_top->next = *S;//新结点指针域指向当前的链栈栈顶
    *S = p_top;//修改栈顶指针为新的结点
    return 1;
    }

    //出栈
    int pop(LINK_STACK *S,SElemType *e) {
    if (*S==NULL)//判断是否空栈
    {
    return 0;
    }
    *e = (*S)->data;//将栈顶元素赋值给指针e所指向的变量
    STACK_NODE *p_top = *S;
    *S = p_top->next;//将栈顶指针的下一结点地址赋值给栈顶指针*S  
    free(p_top);//空间释放

    return 1;
    }
    //取栈顶:取栈顶元素不会删除栈顶结点
    int get_pop(LINK_STACK *S, SElemType *e) {
    if (*S == NULL)//判断是否空栈
    {
    return 0;
    }
    *e = (*S)->data;//将栈顶元素赋值给指针e所指向的变量
    return 1;
    }



    int main() {
    LINK_STACK stack = NULL;//定义链栈并初始化为NULL,或者 STACK_NODE *stack;,stack指向栈顶元素,表示单链表的头指针
    //调用初始化函数(防止定义时忘记初始化)
    init_stack(&stack);

    //入栈
    push(&stack,1);
    push(&stack, 3);
    push(&stack, 5);
    push(&stack, 7);

    //出栈
    SElemType e;
    int rlt1 = pop(&stack,&e);
    printf("rlt = %d ,e = %d ",rlt1,e);
    rlt1 = pop(&stack, &e);
    printf("rlt = %d ,e = %d ", rlt1, e);
    rlt1 = pop(&stack, &e);
    printf("rlt = %d ,e = %d ", rlt1, e);
    rlt1 = pop(&stack, &e);
    printf("rlt = %d ,e = %d ", rlt1, e);
    rlt1 = pop(&stack, &e);
    printf("rlt = %d ,e = %d ", rlt1, e);

    return 0;
    }

    调试结果:

     

  • 相关阅读:
    关联本地代码的方式 HTTPS和SSH---Gitee码云
    详解elementUI表单的验证规则---vue(基本用法)
    vscode 设置缩进 4
    vue的图片懒加载
    A complete log of this run can be found in问题解决
    简单直观的搞懂Vue3的ref、reactive、toRef、toRefs
    vue-cli3.0 引入外部字体并使用
    迅为与龙芯强强联合匠心之作iTOP-2K1000开发板正式发布
    迅为RK3399开发板外接固态硬盘测试
    迅为i.MX6Q开发板Ubuntu20.04 Can通信
  • 原文地址:https://www.cnblogs.com/destiny-2015/p/13620984.html
Copyright © 2020-2023  润新知