• 栈——C语言模拟


    栈——C语言模拟

    一、定义
    根据wiki百科,栈(stack),是一种特殊的串列形式的数据结构,是线性表的一种,一种_限制访问端口_的线性表。

    栈的特殊之处在于只能在链接串列的一端,即栈顶(top)进行操作,在栈顶进行存入数据(push),取出(pop).

    推入:将数据放入栈的顶端,top+1.
    弹出:将顶端数据资料输出,top-1.
    也就是所谓的先进后出(LIFO,Last In First Out).

    stack

    栈的存在价值
    虽然说,栈实现的功能用数组和链表也可以实现,不过你不得不分神到考虑数组的下标增减、链表的指针指向等问题。

    引用原作者的例子,就像我们明明有两只脚可以走路,算上游泳,理论上可以去到全球任何一个地方,那么为什么我们还要有汽车、火车、飞机一样。在向未来的发展过程中,我们需要考虑的更多的是在在抵达目的地之后的发展,这个抵达的过程已经成了我们继续发展的拖累。这时,优化移动方式就成了必然,让我们得以关注结果之后。

    所以说,栈的引入简化了程序设计的问题,划分了不同关注层次,让人更聚焦于问题本身。比如C++,JAVA等的标准库都封装了栈的结构。

    栈分为顺序栈和链式栈
    两者的差别在哪里呢?为什么要特别用两种方法模拟呢?
    时间复杂度上,两者都没有任何其他的操作,均为O(1);
    而空间复杂度上,顺序栈只存储了数据和表头标记;而链式栈对每个数据都建立了一个节点,存储数据之外还存储了指向下一个节点的指针,空间较顺序栈多了一倍。

    所以顺序栈优点,空间占用小,存取定位还是比较方便的,在使用小空间的;而链式栈可以

    以下是栈的抽象数据类型的定义:

    
    ADT Stack{
        数据对象:D={ai|ai in ElemSet,i = 1,2,...,n, n >= 0}
        数据关系:R1={<a(i-1),ai>|a(i-1),ai in D, i = 2,..,n}
            约定an端为栈顶,ai端为栈底.
        基本操作:
            InitStack(&S)
            操作结果:构造一个空栈S
    
            Destroy(&S)
            初始条件:栈S已存在。
            操作结果:栈S被销毁。
    
            ClearStack(&S)
            初始条件:栈S已存在。
            操作结果:将S清为空栈。
    
            StackEmpty(S)
            初始条件:栈S已存在。
            操作结果:若栈S为空栈,则返回TRUE,否则FALSE。
    
            StackLength(S)
            初始条件:栈S已存在。
            操作结果:返回S的元素个数,即栈的长度。
    
            GetTop(S,&e)
            初始条件:栈S已存在且非空。
            操作结果:用e返回S的栈顶元素。
    
            Push(&S,e)
            初始条件:栈S已存在。
            操作结果:插入元素e为新的栈顶指针。
    
            Pop(&S,&e)
            初始条件:栈S已存在且非空。
            操作结果:删除S的栈顶元素,并用e返回其值。
    
            StackTraverse(S,visit())
            初始条件:栈S已存在且非空。
            操作结果:从栈底到栈顶依次对S的每个数据元素调用函数visit()。一旦visit()失败,则操作失效。
    }ADT Stack
    

    二、顺序栈

    定义:

    typedef struct{
        SElemType *base;
        SElemType *top;
        int stacksize;
    }SqStack;
    

    接下来是顺序栈的模块说明

    #define STACK_INIT_SIZE 100 //存储空间初始分配量
    #define STACKINCREMENT 10   //存储空间分配增量
    #define ERROR 0
    #define OK 1
    #define OVERFLOW -1
    
    typedef struct{
        SElemType *base;    //在栈构造之前和压栈之后,base的值为NULL
        SElemType *top;     //栈顶指针,指向栈顶的下一个位置
        int stacksize;      //当前已分配的存储空间,以元素为单位
    }SqStack;
    //------基本操作的函数原型说明------
    Status InitStack(SqStack &S);
    //构造一个空栈S
    
    Status DestroyStack(SqStack &S);
    //销毁栈S,S不再存在
    
    Status ClearStack(SqStack &S);
    //把S置为空栈
    
    Status StackEmpty(SqStack S);
    //若栈S为空栈,则返回TRUE,否则返回FALSE
    
    int StackLength(SqStack S);
    //返回S的元素个数,即栈的长度
    
    Status GetTop(SqStack S,SElemType &e);
    //若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
    
    Status Push(SqStack &S,SElemType e);
    //插入元素e为新的栈顶元素
    
    Status Pop(SqStack &S,SElemType &e);
    //若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
    
    Status StackTraverse(SqStack S,Stack (*visit()));
    //从栈底到栈顶一次对栈中每个元素调用函数visit().一旦visit()失败,则操作失败
    
    /***************基本操作的算法描述**********/
    Status InitStack(SqStack &S){
        //构造一个空栈
        S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
        if(!S.base) exit(OVERFLOW);
        S.top = S.base;
        S.stacksize = STACK_INIT_SIZE;
        return OK;
    }//InitStack
    
    Status GetTop(SqStack S, SElemType &e){
    //若站不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
        if(S.top == S.base) return ERROR;
        e = *(S.top-1);
        return OK;
    }//GetTop
    
    Status Push(SqStack &S,SElemType e){
    //插入元素e为新的栈顶元素
        if(S.top-S.base >= S.stacksize){
            //栈满,追加存储空间
            S.base=(SElemType *)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
            if(!S.base) exit(OVERFLOW);//存储分配失败
            S.top=S.base+S.stacksize;
            S.stacksize+=STACKINCREMENT;
        }
        *S.top++ = e;
        return OK;
    }//push_back
    
    Status Pop(SqStack &S,SElemType &e){
    //若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
        if(S.top == S.base) return ERROR;
        e = * --S.top;
        return OK;
    }//pop
    

    顺序栈是为栈预先分配一个大小固定且较合适的空间,最常见的做法是Stack结构中含一个数组。

    struct stack{
    	int data[10];
    	int top;
    }; 
    

    以下是数组实现的各函数代码

    #include <stdio.h>
    #include <stdlib.h>
    #define STACK_POP_ERR 42
    #define stack struct Stack;
    
    struct Stack{
    	int val[10]; 
      	int top; //栈顶
    };
    
    /* 检查栈是否为空 */
    int empty(stack *stk) {
    	return stk->top == 0; 
    }
    
    /* 推入资料 */
    void push(stack *stk, int x){
    	stk->top=stk->top+1;
    	stk->val[stk->top]=x;
    }
    
    /* 弹出并返回资料 */
    int pop(stack *stk) {
    	if(empty(stk)) 
    		return STACK_POP_ERR; // 不能弹出
    	else{
    		stk->top=stk->top-1;
    		return stk->val[stk->top+1];//这里使用了复用技术
      	}
    }
    
    int main(){
    	// 宣告并初始化空间
    	stack stk;
    	stk.top=0;
    	// 推入四个
    	push(&stk, 3);
    	push(&stk, 4);
    	push(&stk, 1);
    	push(&stk, 9);
    	// 弹出三个
    	printf("%d ", pop(&stk));
    	printf("%d ", pop(&stk));
    	printf("%d ", pop(&stk));
    	return 0;
    }
    

    三、链式栈
    链式栈是定义一个结构体,去掉表头(表头是指一个空节点只带有一个指向存储栈顶数据的空间的指针,即首节点就开始存储数据)

    链式栈这里我用二进制转八进制的程序来说明

    #include <stdio.h>
    #include <stdlib.h>
    #define ERROR 0
    #define OK 1
    
    typedef struct node{
    	int data;
    	struct node* nextPtr;
    }Stack,*LinkStack;
    
    int StackDel(LinkStack &s){
    	if (NULL == s)	return ERROR;//判断栈是否出现了意外的空的状态
    	LinkStack temp = s;//定义一个存储即将被释放的空间的指针
    	s = s->nextPtr;//
    	free(temp);
    	return OK;
    }
    
    int pop(LinkStack &s){
    	int e;
    	if (NULL == s)
    		return ERROR;
    	e = s->data;//存储从栈中取出的值
    	StackDel(s);//删除已取出的栈的空间
    	return e;
    }
    
    int push(LinkStack &s, int e){
    	LinkStack p = (LinkStack)calloc(1, sizeof(Stack));//动态分配空间并初始化
    	if (!p){
    		printf("Error calloc_push");
    		return ERROR;
    	}
    	p->data = e;
    	p->nextPtr = s;//将新数据链接到链表头部
    	s = p;
    	return OK;
    }
    
    
    void transform(LinkStack &s){
    	int e, temp = 0, pow = 1;
    	while (NULL != s){//直到栈空
    		e = pop(s);
    		temp += e*pow;//进行二进制转十进制的计算
    		pow *= 2;//进行位权的累乘
    	}
    	printf("The octonary answer is:(");
        printf("%o", temp);//输出八进制结果
    	printf(")8
    ");
    }
    
    void input(LinkStack &s){
    	char ch;
    	printf("Please input the binary string:");
    	while (scanf("%c", &ch) && ch != '#'){
    		push(s, ch - '0');//将字符型二进制数以整型输入
    	}
    	return;
    }
    
    int main(){
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
    	LinkStack s = NULL;//栈的头结点指针
    	input(s);
    	transform(s);
    	return 0;
    }
    

    四、栈的应用
    1.表达式求值
    2.由递归到非递归
    3.深度优先搜索(bfs)

    参考资料:
    [1].kelinlin.为什么要使用栈这种数据结构.
    [2]Wikipedia Definition.
    [3]《数据结构》,严蔚敏

    如发现问题,敬请指出,互相提高。
  • 相关阅读:
    【摘】IIS修复工具,怎么完全卸载IIS
    开启@yahoo.cn邮箱POP和SMTP的方法
    IT男士专用餐单(养生)
    使用wxWidgets编程——第一步
    搭建跨平台编程环境Code::Blocks+wxWidgets
    TANGO是一个开源的分布控制系统
    【摘】在Window下右键方式打开Dos命令窗口右键菜单支持DOS命令提示符号
    【原】美是永远的追求
    flascc移植问题流水账
    战棋游戏三国志英杰传分析
  • 原文地址:https://www.cnblogs.com/sean10/p/4939240.html
Copyright © 2020-2023  润新知