数据结构—三种结构的栈的实现

数据结构—三种结构的栈的实现

昨天学习了数据结构中的栈,其实说白了,栈和队列都是对插入和删除数据元素有具体要求的线性表,所以在掌握了前面的线性表的知识后,很容易就可以理解这种数据结构的实现模式。


栈:是限定仅在表尾进行插入和删除操作的线性表。

其过程可以理解为网页的打开与后退,当你打开一个网页时,这是你最新打开的界面,显示在最上层,这里其实就类似于插入的操作。但当你要后退到上一个网页时,你退出了当前界面,回到了之前打开的界面,这其实就类似于删除的操作。

特别的,我们把对栈的插入叫做压栈,对栈的删除叫做弹栈。
接下来,我要介绍三种栈的结构的实现。


1.栈的顺序存储

说到顺序存储,我们一定能想到数组。栈的顺序存储其实就是用数组来实现的,但是我们还需要一个值来表示栈顶的位置,因为压栈和弹栈的操作都是在栈顶实现的,所以我们引用了top这个变量,当为空栈时,top = -1;其他时候top等于栈顶元素的下标。
以下为栈的顺序存储的代码实现:

#include<stdio.h>
#include<stdlib.h>
#define MAX 20

typedef struct{
    int date[MAX];
    int top;
}SqStack;

SqStack *InitStack(SqStack *stack){                 //初始化栈,申请内存空间,并给top赋-1 
    stack = (SqStack *)malloc(sizeof(SqStack));           
    stack->top = -1;
    printf("初始化成功!\n");
    return stack;
}     

void push(SqStack *stack){
    int index = stack->top+1;           
    if(index == MAX-1){                     //满栈返回 
        printf("目前栈满!\n");
        return;
    }
    printf("请输入要压栈的数据:");
    scanf("%d",&stack->date[index]);
    stack->top++;                       //压栈成功,栈顶标记加一 
    printf("压栈成功!\n");
}

void pop(SqStack *stack){
    printf("%d\n",stack->top);
    int index = stack->top;
    if(index == -1){                    //空栈返回 
        printf("目前为空栈!\n");
        return;
    }
    printf("要弹栈的数据为:%d ",stack->date[index]);
    stack->top--;                   //弹栈成功,栈顶标记减一 
    printf("弹栈成功!\n");
}

void print_stack(SqStack *stack){           //遍历栈内数据 
    printf("%d\n",stack->top);
    int index = stack->top;
    if(index == -1 ){
        printf("目前为空栈!\n");
        return;
    }
    for(int i = 0; i <= index; i++){
        printf("第%d个数据为:",i+1);
        printf("%d\n",stack->date[i]);
    }
}

int main(){
    SqStack *stack;
    stack = InitStack(stack);
    int choice = -1;
    while(choice != 0){
        printf("1. 压栈\n");
        printf("2. 弹栈\n");
        printf("3. 输出\n");
        scanf("%d",&choice);
        switch(choice){
            case 1: push(stack);
                break;
            case 2: pop(stack);
                break;
            case 3: print_stack(stack);
                break;
        }
    }
    return 0;
} 

2.两栈共享空间

有没有可能有这种情况:两个同类型的栈,我们各自为他们开辟了数组空间,但是有可能第一个栈已经满了,再压栈就溢出了,而另一个栈还有很多存储空间空闲。所以为了最大限度地利用其事先开辟的存储空间进行操作,我们可以两栈共享空间。
所谓两栈共享空间,其实就是指事先开辟一个数组,其两端分别作为栈顶,即top1和top2,top1 = -1 表示 1栈空栈,top2 = MAX 表示 2栈空栈。**top1从前向后累加元素,top2从后往前累加元素,直到两指针相遇,也就是**top1+1 == top2时,表示栈满。
以下为两栈共享空间的代码实现:

#include<stdio.h>
#include<stdlib.h>
#define MAX 20

typedef struct{
    int date[MAX];
    int top1;
    int top2;
}doublestack;

doublestack *Initstack(doublestack *stack){                 //初始化两栈数组 
    stack = (doublestack *)malloc(sizeof(doublestack));     //令top1 = -1,top2 = MAX 
    stack->top1 = -1;
    stack->top2 = MAX;
    return stack;
}

void push(doublestack *stack){                  //选择压1栈或2栈 
    int index;
    while(1){
        if(stack->top1 + 1 == stack->top2){
            printf("目前栈满!无法继续压栈\n");
            return;
        } 
        int choice;
        printf("请选择压1栈或压2栈?\n");
        scanf("%d",&choice);
        if(choice == 1){
            index = ++stack->top1;
            printf("输入压1栈的数据:");
            scanf("%d",&stack->date[index]);
            printf("压栈成功!\n");
            return;
        }
        else if(choice == 2){
            index = --stack->top2;
            printf("请输入压2栈的数据:");
            scanf("%d",&stack->date[index]);
            printf("压栈成功!\n");
            return;
        }
        else{
            printf("输入错误!");
            continue;
        }
    }
}

void pop(doublestack *stack){
    int index;
    while(1){
        int choice;
        printf("请选择弹1栈或弹2栈?\n");
        scanf("%d",&choice);
        if(choice == 1){
            index = stack->top1;
            if(index == -1){
                printf("1栈为空栈!\n");
                continue;
            }
            printf("要弹栈的数据为:%d",stack->date[index]);
            stack->top1--;
            printf("弹栈成功!\n");
            return;
        }
        else if(choice == 2){
            index = stack->top2;
            if(index == MAX){
                printf("2栈为空栈!\n");
                continue;
            }
            printf("要弹栈的数据为:%d",stack->date[index]);
            stack->top2++;
            printf("弹栈成功!\n");
            return;
        }
        else{
            printf("输入有误!\n");
            continue;
        }

    }
}

void print_stack(doublestack *stack){
    int index;
    index = stack->top1;
    if(index == -1)
        printf("1栈为空栈!\n");
    else{
        printf("1栈:\n");
        for(int i = 0; i <= index; i++){
            printf("第%d个数据为:",i+1);
            printf("%d\n",stack->date[i]);
        }
    }
    index = stack->top2;
    if(index == MAX)
        printf("2栈为空栈!\n");
    else{
        printf("2栈:\n");
        for(int i = MAX-1; i >= index; i--){
            printf("第%d个数据为:",MAX-i);
            printf("%d\n",stack->date[i]);
        }
    }
    return;
}

int main(){
    doublestack *stack;
    stack = Initstack(stack);
    int choice = -1;
    while(choice != 0){
        printf("1. 压栈\n");
        printf("2. 弹栈\n");
        printf("3. 输出\n");
        scanf("%d",&choice);
        switch(choice){
            case 1: push(stack);
                break;
            case 2: pop(stack);
                break;
            case 3: print_stack(stack);
                break;
        }
    }
    return 0;
}

3.栈的链式存储结构

不管是栈顺序存储结构还是两栈共享空间,因为都是通过定义数组来实现,所以总会有栈满的情况,这时,我们想起来线性表中的单链表可以实现动态分配内存空间,基本不存在栈满的情况。
因为只能在栈顶进行压栈和弹栈,所以此处不需要头节点。当空栈时,栈结构中的栈顶指针指向NULL,并且count记录了数据元素的个数
以下是栈的链式存储结构的代码实现:

#include<stdio.h>
#include<stdlib.h>

typedef struct STACKNODE{           //栈结构的单链表 
    int date;
    struct STACKNODE *next;
}stacknode,*linkstack;

typedef struct LinkStack{           //栈结构 
    linkstack top;
    int count;
}link;

link *Initstack(link *stack){                       //初始化栈,给栈顶指针赋值NULL 
    stack = (link *)malloc(sizeof(struct LinkStack));
    stack->count = 0;
    stack->top = NULL;
    return stack;
}

void push(link *stack){
    linkstack pnew = (linkstack)malloc(sizeof(stacknode));
    printf("请输入压栈数据:");
    scanf("%d",&pnew->date);
    pnew->next = stack->top;
    stack->top = pnew;
    stack->count++;
    printf("压栈成功!\n");
}

void pop(link *stack){
    linkstack temp;
    if(stack->top == NULL){
        printf("目前为空栈!\n");
        return;
    }
    printf("你要弹栈的数据为:%d",stack->top->date);
    temp = stack->top;
    stack->top = stack->top->next;
    free(temp);
    stack->count--;
    printf("弹栈成功!\n");
}

void print_stack(link *stack){
    linkstack temp;
    if(stack->top == NULL){
        printf("目前为空栈!\n");
        return;
    }
    temp = stack->top;
    int index = 1;
    while(temp != NULL){
        printf("第%d个数据:",index++);
        printf("%d\n",temp->date);
        temp = temp->next;
    }
}

int main(){
    link *stack;
    stack = Initstack(stack);
        int choice = -1;
    while(choice != 0){
        printf("1. 压栈\n");
        printf("2. 弹栈\n");
        printf("3. 输出\n");
        scanf("%d",&choice);
        switch(choice){
            case 1: push(stack);
                break;
            case 2: pop(stack);
                break;
            case 3: print_stack(stack);
                break;
        }
    }
}

比较三种栈的结构,如果栈的使用过程中元素变化不可预料,有时很小,有时很大,那么最好是用链栈。反之,如果它的变化在可控范围之内,使用顺序栈会好一点。

猜你喜欢

转载自blog.csdn.net/wintershii/article/details/80271455