美文网首页
数据结构之栈的实现

数据结构之栈的实现

作者: 大橘猪猪侠 | 来源:发表于2020-04-11 10:54 被阅读0次

    栈的定义:

    栈又名堆栈,是一种受限制的线性表,只在表尾进行插入和删除操作的线性表。这一端被称为栈顶,另一端称为栈底。向一个栈插入新元素称为进栈、入栈或压栈,将新元素放到栈顶,从栈顶删除一个元素,称为出栈或者退栈,相邻的元素作为新的栈顶。

    栈是一种数据结构,只能从栈顶进行插入和删除的。在我们常见的开发当中,栈的使用很多。例如手机页面push的跳转,只能从最前面显示的界面一个一个退出。

    栈的示意图

    截屏2020-04-12上午10.30.19.png

    链式栈结构

    截屏2020-04-12上午10.30.41.png

    进栈操作

    截屏2020-04-12上午10.30.49.png

    出栈操作

    截屏2020-04-12上午10.31.09.png

    接下来,我们用数组和链表的方式,来演示一下对栈的一些操作。

    一、数组实现

    先创建一个栈的结构体

    typedef int Status;
    typedef int Elemtype;
    
    typedef struct {
        Elemtype data[MAXSIZE];
        int top;
    }Stack;
    
    

    首先先建立一个空栈,判断一个栈是否为空或者将栈置空,只需要判断top为-1来表示,
    获取栈的长度,判断top的值就可以知道栈的长度。

    //1.1构建一个空栈
    Status InitStack(Stack *S){
        S->top = -1;
        return OK;
    }
    //1.2将栈置空
    Status  ClearStack(Stack *S){
        S->top = -1;
        return OK;
    }
    //1.3判断顺序栈是否为空
    Status StackEmpty(Stack S){
        if(S.top == -1) return TRUE;
        else    return FALSE;
    }
    //1.4返回栈的长度
    Status StackLength(Stack S){
        return S.top+1;
    }
    

    获取栈顶:在获取栈顶时,需要先判断栈是否为空,不为空则将数组中的第top个元素赋值即可

    //1.5获取栈顶
    Status GetTopStack(Stack S,Elemtype *e){
        if(S.top == -1)
            return ERROR;
        else
            *e = S.data[S.top];
        
        return OK;
    }
    

    push操作:先对栈进行判断,判断栈是否已满,没有满则在数组第top个元素插入,top++
    pop操作:先对栈进行判空,不为空则将数组中第top个元素删除,top--
    遍历操作,利用循环,将数组中的每一个值打印输出

    //1.6插入元素e为新栈顶元素
    Status PushDataInStack(Stack *S,Elemtype e){
        if(S->top == MAXSIZE-1) return ERROR;
        S->top++;
        S->data[S->top] = e;
        return OK;
    }
    //1.7删除s栈顶元素,并且用e带回
    Status PopDataInStack(Stack *S,Elemtype *e){
        if(S->top == -1)    return ERROR;
        *e = S->data[S->top];
        S->top--;
        return OK;
    }
    //1.8从栈底到栈顶一次对栈中的每个元素打印
    Status StackDisPlay(Stack S){
        int i = 0;
        while (i<=S.top) {
            printf("%d   ",S.data[i++]);
        }
        printf("\n");
        return OK;
    }
    

    main函数

       Stack S;
        int e = 0;
        if(InitStack(&S) == OK){
            for (int i = 1; i<10; i++) {
                PushDataInStack(&S, i);
            }
        }
        printf("顺序栈元素为:\n");
        StackDisPlay(S);
        
        PopDataInStack(&S, &e);
        printf("弹出的是:%d \n",e);
        
        StackDisPlay(S);
        printf("是否为空栈:%d(0为假,1为真)\n",StackEmpty(S));
        
        GetTopStack(S, &e);
        
        printf("栈顶元素为:%d \n",e);
        
        ClearStack(&S);
        printf("清空是否已经清空,%d(0为假,1为真),栈的长度为:%d\n",ClearStack(&S),StackLength(S));
        
        
    

    打印结果


    15865728980098.png

    链表实现栈的操作

    链表实现栈的操作比数组实现更复杂一点

    先对节点和栈进行定义

    //定义节点结构体
    typedef struct StackNode{
        Elemtype data;
        struct StackNode *next;
    }StackNode;
    
    typedef struct StackNode *LinkStackPtr;
    
    //定义栈
    typedef struct {
        LinkStackPtr top;
        int count;
    }LinkStack;
    
    

    创建空栈,只需要判断栈的top为空,count 为0
    而将栈置空,则需要对链表的每一个节点一一释放
    判断空栈和判断栈中的个数,都只需要判断conut的数量,为0则栈为空,不为0,则栈中有元素。

    //1.1构建一个空栈
    Status InitStack(LinkStack *s){
        s->top = NULL;
        s->count = 0;
        return OK;
    }
    //1.2把链表s置为空栈
    Status ClearStack(LinkStack *s){
        LinkStackPtr p,q;
        p = s->top;
        while (p) {
            q = p;
            p = p->next;
            free(q);
        }
        s->count = 0;
        return OK;
    }
    //1.3判断栈是否为空,若栈s为空,返回true,否则返回false
    Status StackEmpty(LinkStack s){
        if(s.count == 0)
            return TRUE;
        else
            return FALSE;
    }
    //1.4返回s的元素个数
    Status StackCount(LinkStack s){
        return s.count;
    }
    

    用链表实现栈的操作,有点像前插法一样,将最后插入的元素放在指针top的前面,将新创建的节点的next指向top,再将指针top前移,count++;删除时,则将最前面的指针指向下一个元素,将第一个节点释放,最后将count--,遍历栈时,用新创建的节点指向top,用循环对栈中的每一个元素进行打印输出。

    //1.5取栈顶
    Status GetStackTop(LinkStack s,Elemtype *e){
        if(s.count == 0)
            return ERROR;
        else
            *e = s.top->data;
        return OK;
    }
    //1.6插入元素到栈顶
    Status pushData(LinkStack *s,Elemtype e){
        LinkStackPtr temp;
        temp = (LinkStackPtr)malloc(sizeof(LinkStackPtr));
        temp->data = e;
        temp->next = s->top;
        s->top = temp;
        s->count++;
        return OK;
    }
    //1.7删除栈顶
    Status pop(LinkStack *s,Elemtype *e){
        LinkStackPtr p;
        if(StackEmpty(*s)){
            return ERROR;
        }
        *e = s->top->data;
        p = s->top;
        s->top = s->top->next;
        free(p);
        s->count--;
        return OK;
        
    }
    

    main 函数

      LinkStack s;
        int e=0,i=0;
        if(InitStack(&s) == OK)
            for (i = 1; i<10; i++) {
                pushData(&s, i);
            }
        printf("栈中的元素:");
        StackDisplay(s);
        pop(&s, &e);
        printf("弹出栈顶元素为:%d\n",e);
        StackDisplay(s);
        printf("栈是否为空:%d(1:空,0:不空)\n",StackEmpty(s));
        GetStackTop(s, &e);
        printf("栈顶元素为:%d,栈的长度为:%d\n",e,StackCount(s));
        ClearStack(&s);
        printf("清空栈后,栈是否为空:%d(1:空,0:不空)\n",StackEmpty(s));
        return 0;
    

    打印结果

    15865735382258.png

    栈与递归

    递归的定义:编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。

    在高级语言程序中,调用函数和被调用函数之间的链接与信息交换都是通过栈来进行的,通常,当一个函数的运行期间调用另一个函数时,在运行被调用函数之前,需要完成3件事情:
    1、将所有的实参,返回地址等信息调用传递被调用函数保存;
    2、为被调用函数的局部变量分配存储空间;
    3、将控制转移到被调用函数入口;

    而从被调用函数返回调用函数之前,系统同样需要完成3件事情:
    1、保存被调用函数的计算结果;
    2、释放被调用函数的数据区;
    3、依照被调用函数保存的返回地址将控制移动到调用函数;

    当多个函数构成嵌套调用时,按照“先调用后返回”的原则,上述函数之间的信息传递和控制转移必须通过栈来实现,即系统将整个程序运行时所需要的数据空间都安排在一个栈中,每当调用一个函数时,就在它的栈顶分配一个存储区,每当这个函数退出时,就释放它的存储区,则当前运行时的函数的数据区必在栈顶。

    下面通过一个题目来理解递归与栈的关系
    爬楼梯问题:假设你正在爬楼梯,需要n阶你才能到达楼顶,每次可以爬1或2个台阶,问有多少种不同的方法可以爬上楼顶(给定n为正整数)。

    首先来分析一下这个题目:
    当只有一阶或者2阶时,我们只有1种和2种方法可以爬上楼顶

    当有3层或者3层时,你会发现分别有3种和5种方法可以爬到楼顶,你是不是已经发现了什么?

    我们可以用递归来解决这个问题

    int ClimbStairs(int n){    
        if(n == 0||n == 1||n ==2)
            return n;
        else
            return ClimbStairs(n-1)+ClimbStairs(n-2);
    }
    
    

    当然如果楼梯的阶层很大,那么达到楼顶的方法也就有很多,那就要考虑性能和精度的问题,在这里,我们对程序进行一些优化。可以节省因递归次数过多,而导致的性能问题的影响。

    int ClimbStairs(int n){
        double Stairs[1000];
        Stairs[0]=0;
        Stairs[1]=1;
        Stairs[2]=2;
        for (int i = 3; i<=n; i++) {
            Stairs[i] = Stairs[i-1]+Stairs[i-2];
        }
        return dp[n];
    }
    
    

    相关文章

      网友评论

          本文标题:数据结构之栈的实现

          本文链接:https://www.haomeiwen.com/subject/qaoemhtx.html