数据结构--链式栈、顺序栈的基本实现与简单应用:进制转换

来源:互联网 发布:mac开机按option选恢复 编辑:程序博客网 时间:2024/05/19 13:20

一、栈的基本概念:

1、栈(Stack),是基本数据结构中比较重要的一种,其遵循的基本原则是:先进后出(First In Last Out,FILO);我们编程时操作系统为函数参数压栈,其系统内核栈的实现就是基于该数据结构。我们以顺序栈来介绍起基本结构。下图为一个基本的栈的结构(这是一个顺序栈):

这里写图片描述

我们所建的栈以及内核中实现,在结构上其实并非该图所示,因为内存地址从高到低分配,所以栈应该是“口朝下”的,但是为了方便理解(现实中我们认为“底”应该在下面)所以我们用这种图来表示。栈有两个指针来标识其“头”与“尾”,其中,栈的高地址指针叫栈底指针(pBottom),栈的低地址指针叫做栈顶指针(pTop)。数据存入栈中称为数据入栈(或者压栈),数据从栈中取出来称为出栈(或者推栈)。在该图中,入栈的顺序是num1->num2->num3->num4,而出栈顺序是num4->num3->num2->num1;最先入栈的数据最后才能被推栈出来,所以叫先入后出式存储结构。

2、关于栈的口朝下还是口朝上问题,我们用一个小例子来测试一下:

# include<stdio.h>int add(int x,int y)/*函数传参压栈顺序为从右向左*/{    printf("address_x = %p\naddress_y = %p\n",&x,&y);    return (x+y);}int main(void){    int sum = add(3,5);    printf("sum = %d\n",sum);}

我们将x和y的地址打印输出:

这里写图片描述

由于y先压栈,x后压栈,我们打印输出的结果显示,address_y为高地址,而address_x为低地址,所以由此我们可以得出,操作系统在栈的实现中是高地址为栈底,低地址为栈顶。因此我们还是将栈的结构图再翻过来吧:

这里写图片描述

3、满“栈”与空“栈”,听到这两个名字,也许你会认为,“满栈”就是栈已经存满了数据,而“空栈”则是栈中没有数据,那你就大错特错了。首先,我们画出两张栈的结构图:

这里写图片描述

在该图中,我们发现满栈与空栈中都没有存放数据,但是满栈pTop指针并没有指向第一块栈内存,而空栈的pTop指针指向了第一块栈内存。所以,这就是满栈与空栈的区别:
满栈:满栈的栈顶指针指向栈顶数据(没有数据时,和pBottom都指向NULL),对于满栈的操作则是,先移动栈顶指针,然后在栈顶指针所指向的内存中存入数据;
空栈:空栈的栈顶指针指向一块空内存(没有数据时,pBottom指向NULL,pTop不指向NULL),该内存的上一块内存才是最顶层的数据,对于空栈的操作则是,先在栈顶指针所指向的内存中存入数据,然后移动栈顶指针;
今天我们采用满栈来进行一系列操作。

二、顺序栈(借助数组)的实现与操作:

# include <stdio.h># include <stdlib.h># include <string.h># define MAX_STACK_LEN 5    /*栈最大元素个数*//*顺序栈的结构体定义*/typedef struct stack_arr{    int data[MAX_STACK_LEN];    int top;//数组下标作为栈顶指针,而栈底指针不需要指定}STACKARR;void init_stack(STACKARR *);//初始化栈int empty_stack(STACKARR * pS);//栈判空int full_stack(STACKARR * pS);//栈判满void push_stack(STACKARR *);//压栈void traverse_stack(STACKARR *);//遍历void pop_stack(STACKARR *);//出栈void clear_stack(STACKARR * pS);//栈清空int main(void){    STACKARR Stack;/*创建了一个含有MAX_STACK_LEN个数据的顺序栈*/    init_stack(&Stack);/*对该栈初始化*/    push_stack(&Stack);    printf("压栈完毕,栈中元素依次为(栈顶->栈底):\n");    traverse_stack(&Stack);    pop_stack(&Stack);    printf("出栈完毕,栈中元素依次为(栈顶->栈底):\n");    traverse_stack(&Stack);    clear_stack(&Stack);    printf("栈已清空");    traverse_stack(&Stack);    return 0;}void init_stack(STACKARR * pS){    memset(pS->data, 0, sizeof(pS->data));    pS->top = -1;/*满栈让栈定指针初始化在第一个地址的前一个地址*/}int empty_stack(STACKARR * pS){    return ( (pS->top == - 1) ? (-1) : (0) );}int full_stack(STACKARR * pS){    return ( (pS->top == MAX_STACK_LEN - 1) ? (-1) : (0) );}void push_stack(STACKARR * pS){    int num = 0;    int choose = 0;    while(1){        if(full_stack(pS) == -1){            printf("栈已满,不能压栈!\n");            exit(EXIT_FAILURE);        }        printf("请输入要压栈的数据:");        scanf("%d",&num);        pS->top++;/*满栈先移动指针,再存储*/        pS->data[pS->top] = num;            printf("入栈成功,继续入栈?(继续输入1,结束输入2):");        scanf("%d",&choose);        if(choose != 1){            break;        }    }}void traverse_stack(STACKARR * pS){    int i = 0;    if(empty_stack(pS) == -1){        printf("栈为空!\n");        exit(EXIT_FAILURE);    }    /*从栈顶部开始遍历*/    for(i = pS->top; i >= 0; i-- ){        printf("%d\t",pS->data[i]);    }    printf("\n");}void pop_stack(STACKARR * pS){    int choose = 0;    while(1){        if(empty_stack(pS) == -1){            printf("栈为空!\n");            exit(EXIT_FAILURE);        }        printf("出栈元素为:%d\n",pS->data[pS->top]);/*先出栈栈顶指针再自减1*/        pS->top--;        printf("出栈成功,继续出栈?(继续输入1,结束输入2):");        scanf("%d",&choose);        if(choose != 1){            break;        }    }}void clear_stack(STACKARR * pS){    memset(pS->data, 0, sizeof(pS->data));    pS->top = -1;}

测试结果:
这里写图片描述

三、链式栈(借助链表)的实现与操作:

我们今天使用双向链表来实现(单向链表更简单,但是双向链表的复杂会使我们对栈这种结构认识更加清晰),基本结构如下:

这里写图片描述

测试代码:

/**    *2016年12月13日10:52:39    *Create Stack of Link**/#include <stdio.h>#include <stdlib.h>#ifndef bool#define bool inttypedef struct stack_node{    int data;    struct stack_node * pDown;    struct stack_node * pUp;}STACKNODE;/*栈的节点结构体*/typedef struct stack_link{    STACKNODE * pTop;    STACKNODE * pBottom;}STACKLINK; /*栈顶指针与栈底指针结构体*/enum{false,true};void init_stack(STACKLINK *);/*初始化*/void push_stack(STACKLINK *);/*压栈*/void travsal_stack(STACKLINK *);/*遍历*/void pop_stack(STACKLINK *);/*出栈*/bool empty_stack(STACKNODE * pS);/*栈判空,链式栈不存在满栈情况,无需判满*/int main(void){    STACKLINK Stack;    init_stack(&Stack);/*初始化栈*/    push_stack(&Stack);/*压栈*/    travsal_stack(&Stack);/*从栈顶开始遍历*/    pop_stack(&Stack);/*出栈*/    travsal_stack(&Stack);    return 0;}void init_stack(STACKLINK * pS){    pS->pTop = NULL;    pS->pBottom = NULL;/*初始化将栈顶指针与栈底指针均赋空*/    printf("初始化成功!\n");}void push_stack(STACKLINK * pS){    int num, choose;    STACKNODE * pNew = NULL;    while(1){        printf("请输入压栈数据:");        scanf("%d",&num);        pNew = (STACKNODE *)malloc(sizeof(STACKNODE));        if(NULL == pNew){            printf("内存分配失败!\n");            exit(EXIT_FAILURE);        }        pNew->data = num;        pNew->pUp = NULL;        pNew->pDown = NULL;/*入栈元素初始化*/        if(empty_stack(pS->pTop) == -1){/*栈为空*/            pS->pBottom = pS->pTop = pNew;        }        else{/*栈不为空*/            pS->pTop->pUp = pNew;/*上一个为新元素*/            pNew->pDown = pS->pTop;/*新元素的下一个为原有栈顶元素*/            pS->pTop = pNew;/*栈顶指针向上移到*/        }        printf("继续压栈?(继续请选1,结束请选2):");        scanf("%d",&choose);        if(choose != 1){            break;        }    }}void travsal_stack(STACKLINK * pS){    STACKNODE * pMove;    pMove = pS->pTop;    while(1){        if(pMove == NULL){            break;        }        printf("%d\t",pMove->data);        pMove = pMove->pDown;    }    printf("\n");}void pop_stack(STACKLINK * pS){    STACKNODE * pDelete;    int choose = 0;    while(1){        pDelete = pS->pTop;        if(empty_stack(pDelete) == -1){            printf("栈为空,出栈失败!\n");            exit(EXIT_FAILURE);        }        printf("出栈元素为:%d\n",pDelete->data);        pS->pTop = pDelete->pDown;/*栈顶指针向下移*/        if(pS->pTop != NULL)/*注意当pS->pTop为空,即栈为空时,无pS->pTop->pUp,若不判断会出现段错误*/            pS->pTop->pUp = NULL;/*栈顶指针的上一个(前驱指针)为空*/        free(pDelete);        printf("继续出栈?(继续请选1,结束请选2):");        scanf("%d",&choose);        if(choose != 1){            break;        }    }}bool empty_stack(STACKNODE * pN){    return ((pN == NULL) ? -1 : 0);}#endif

四、栈的应用——借助栈实现进制转换:

/*用数组创建一个栈*/# include<stdio.h># include<string.h># include<stdlib.h># define SIZE 32typedef struct Stack{    int sta_arr[SIZE];    int top;}STACK;void init_stack(STACK *);void init_stack(STACK * pS){    memset(pS->sta_arr, 0, sizeof(pS->sta_arr));    pS->top = -1;}int is_full(STACK * pS){    return ((pS->top == SIZE-1) ? 0 : 1);}int is_empty(STACK * pS){    return ((pS->top == -1) ? 0 : 1);}void input_stack(STACK * pS, int value){    if(is_full(pS)){        pS->top++;        pS->sta_arr[pS->top] = value;    }}void output_stack(STACK * pS){    if(is_empty(pS))        printf("%d",(pS->sta_arr)[pS->top]);}void invert(STACK * pS, int value, int type){    int tmp,num = value;    init_stack(pS);    while(num)    {        /*进制转化:取余之后压栈,再取整,低位就在栈底,自然最后输出*/        tmp = num % type;           input_stack(pS,tmp);            num = num / type;    }    /*出栈*/    while(pS->top != -1)    {        output_stack(pS);        pS->top--;    }    printf("\n");}int main (void){    STACK S;    int value, type;    init_stack(&S);    printf("请输入你要转换的十进制数字:\n");    scanf("%d",&value);    printf("请选择你要转换成的进制格式(2~9,如:2代表二进制):\n");    scanf("%d",&type);    if(type > 9 || type < 2){        printf("选择的进制格式暂时无法处理!\n");        exit(EXIT_FAILURE);    }    invert(&S,value,type);    return 0;}

测试:

这里写图片描述

1 0
原创粉丝点击