数据结构之栈定义及基本操作实现

  终于有可以有时间写点数据结构的学习总结了,前段时间一直在紧张的忙一些项目,都没有空出时间来学习数据结构,现在终于可以稍微喘口气了,还是数据结构有意思,这两天看了点栈的东西,写下来总结一下,有错误的地方希望看到的朋友指出来,感激不尽。

  根据学习,栈就是一种线性数据结构,栈的运算只能在表的一段进行,所以这种数据结构具有“后进先出”的特点。

  接下来是栈的c语言实现。其中栈由一个top节点和bottom节点组成,这两个节点分别指向栈的顶部和底部。其中栈的组成结点是由结构体实现,结构体由数据库和指向下一个结点的指针域构成,下面是结点的c语言实现:

1 /*
2 定义实现栈的结点 
3 */
4 typedef struct Node{
5     int data;
6     struct Node * pNext;
7 }NODE,* PNODE;

  有了实现栈的结点,那么接下来就是该如何实现栈(具体的解释看代码):

1 /*
2 定义栈的结构,此处采用链表实现静态栈,所以仅仅需要给出栈的头部指针和底部指针即可 
3 */
4 typedef struct Strack{
5     PNODE pTop;
6     PNODE pBottom;
7 }STRACK,* PSTRACK;

  栈的基本操作:栈的初始化,压栈、栈的遍历、出栈等操作  

 1 /*
 2 对栈初始化的函数
 3 对栈初始化仅仅是令栈中的top和bottom指针指向一个确定的地址,并且此地址指向的是栈的头结点(头结点的引入可以
 4 大大方便对栈的操作)。 
 5 */
 6 void init_strack(PSTRACK pS){
 7     pS->pTop=(PNODE)malloc(sizeof(NODE));   //定义一个新结点,这个结点就是栈的头结点,并且让pTop指向这个结点
 8     if(pS->pTop==NULL){
 9         printf("内存分配失败");
10         exit(-1);
11     } 
12     else{
13         pS->pBottom=pS->pTop;    //令bottom和top都指向头结点,则初始化栈完成,栈中没有任何有效结点
14         pS->pTop->pNext=NULL; 
15     }
16      
17 }
18 
19 /*
20 压栈函数
21 因为栈具有“先进后出、后进先出”的特性,所以,在将元素压入栈时,也只能将栈压入栈的顶部
22 由此,压栈函数就是令栈的top指针指向新结点,新结点的指针域指向未压入栈时的最顶部的元素。 
23 */
24 int push(PSTRACK pS,int val){
25     PNODE pNew=(PNODE)malloc(sizeof(NODE));  //定义一个新结点
26     if(pNew->pNext==NULL){
27         printf("内存分配失败");
28         exit(-1);
29     }
30     pNew->data= val;
31     pNew->pNext=pS->pTop;  //指针赋值的顺序不可以乱,为保证top所指向的地址不丢失,所以首先让新结点的指针域指向pS->pTop所指向的结点
32     pS->pTop=pNew;  //令top指针指向新的结点
33     return 0; //0表示当前push成功 
34 }
35 
36 /*
37 栈的遍历函数
38 因为只是遍历输出,所以不能破坏栈原有的结构,所以我们只有定义一个新的结点p,让其指向栈顶元素,
39 然后遍历下移一个输出其指向的元素的值 ,循环条件就是p=ps->pBottom 
40 */
41 void traverse(PSTRACK ps){
42     PNODE p=ps->pTop;  //让新定义的结构体指针指向栈顶元素
43     while(p!=ps->pBottom){
44         printf("%d ", p->data);
45         p=p->pNext;  //让当前指针指向当前节点的下一个结点 
46     } 
47     printf("
");
48     return;
49 }
50 
51 
52 /*
53 判断当前栈是否为空的函数
54 若为空,返回true
55 否则,返回false 
56 */
57 bool isEmpty(PSTRACK ps){
58     if(ps->pTop==ps->pBottom){
59         return true;
60     }
61     else{
62         return false;
63     }
64 }
65 
66 /*
67 弹栈函数:弹栈简单的说就是将第一个数值弹出,然后将ps->pTop指向原第一个结点的下一个结点(即弹栈后的第一个结点),
68 然后在将被弹出的元素在内存中释放掉。 
69 */
70 bool pop(PSTRACK ps,int *pVal){
71     if(isEmpty(ps)){  //判断当前栈是否为空,若为空,则返回false 
72         return false;
73     }
74     else{   
75         PNODE p=ps->pTop;  //定义一个结点,这个结点在每次弹栈时都是指向栈顶,这样可以保证不会造成被弹出元素的丢失 
76         ps->pTop=p->pNext;  //让top结点指向栈顶元素 
77         *pVal=p->data;
78         free(p);  //将被弹出元素释放 
79         p=NULL;
80         return true;
81     }     
82 }

  上述就实现了对栈的基本操作,当然具体的应用还要看具体的算法要求。

上述是在大学时学习的,下面的是工作两年后再次学习时的理解,可能会有不同的见解,都写出来吧。

1、栈的定义:一种可以实现“先进后出”的数据存储结构,数据的插入和删除只能在数据的一端进行。
内存分为静态内存和动态内存。
静态内存在栈中分配;动态内存在堆中分配。
栈:由操作系统自动分配释放,存放函数的参数值,局部变量的值等。
堆:一般由程序猿分配释放,若程序猿不释放,程序结束时由OS回收(类似于c语言中的malloc函数分配的空间)。
 
2、栈的分类:
(1)静态栈:以数组作为数据的存储。
(2)动态栈:以链表作为数据的存储方式。
 
3、栈的相关操作(该处采用链表的动态栈):
一点说明:
     因为用链表实现栈,其实其本质就是一个链表,只不过对该链表的插入(push)和删除(pop)操作都在该链表的一端进行(该段被称为栈顶,且人为限制就只在该栈顶进行操作),所以该链表就会具有了“先进后出”的特性,则被称为栈结构。
     所以,严格来说,栈仅仅需要一个栈顶,该栈顶指向该链表的被称为栈顶端的节点(所以,该栈顶也是一个该Node类型的指针)。
(1)栈中每个节点的结构:
数据结构之栈定义及基本操作实现
数据结构之栈定义及基本操作实现
 
(2)栈的结构:
因为通过栈顶可以找到该栈的所有元素,所以,该栈对应的链表应该是一个自上而下指向的链表。
数据结构之栈定义及基本操作实现
数据结构之栈定义及基本操作实现
该栈仅需要一个stop指针,指向栈顶元素。
数据结构之栈定义及基本操作实现
  数据结构之栈定义及基本操作实现数据结构之栈定义及基本操作实现

数据结构之栈定义及基本操作实现

(3)栈的初始化操作:
数据结构之栈定义及基本操作实现
 数据结构之栈定义及基本操作实现
(4)栈的push操作:
数据结构之栈定义及基本操作实现
 数据结构之栈定义及基本操作实现
(5)栈的pop操作:
数据结构之栈定义及基本操作实现
 数据结构之栈定义及基本操作实现
(6)栈是否为空及栈的遍历操作:
数据结构之栈定义及基本操作实现
 数据结构之栈定义及基本操作实现
其实在栈中可以增加一个PNODE类型的指针sbuttom,让该指针用于指向栈低节点,在判断非空时,只要判断sc->stop == sc->sbuttom时,就表示top和buttom重合了,指向了最底部的节点。其实,该buttom节点可以通过最后一个节点的next指针域是否为NULL来判断(next指针域为NULL时,就表示该节点没有下一个节点了,是栈低节点)。 
 
代码:
  1 #include <stdio.h>
  2 #include <sys/malloc.h>
  3 #include <stdlib.h>
  4 
  5 /*
  6 栈结构及其操作:
  7 当前采用链表进行栈中数据的存储。
  8 */
  9 
 10 
 11 /*
 12 定义栈中每一个节点的结构
 13 */
 14 typedef struct Node{
 15     int data;  //数据域
 16     struct Node * pnext;  //指针域,指向栈中下一个节点
 17 }NODE, * PNODE;  //定义两个别名,方便操作
 18 
 19 /*
 20 定义栈的结构:
 21 因为用链表实现栈,其实其本质也是一个链表,只不过该链表的插入(push)和删除(pop)操作只能在链表的一端(该端被称作栈顶)进行操作,所以该链表具有“先进后出”的特性,被称作栈(个人理解)。
 22 所以,严格来说,栈仅仅需要一个指向栈顶(链表的一端)struct Node *类型的指针就可以知道该栈中的所有数据。
 23 */
 24 typedef struct Stack{
 25     struct Node * stop;  //栈的栈顶(指向栈中最顶部的那个元素)
 26 } STACK;
 27 
 28 //栈的初始化操作
 29 struct Stack init_stack();
 30 
 31 //push操作
 32 void stack_push(STACK *sc, int data);
 33 
 34 //pop操作
 35 int pop(STACK *sc);
 36 
 37 //遍历操作
 38 void trvel_stack(STACK *sc);
 39 
 40 //判断是否为空操作
 41 int is_empty(STACK *sc);
 42 
 43 int main(int argc, char const *argv[])
 44 {
 45     STACK sc;
 46     sc = init_stack();
 47 
 48     //push几个数据
 49     stack_push(&sc, 1);
 50     stack_push(&sc, 3);
 51     stack_push(&sc, 7);
 52     stack_push(&sc, 2);
 53     stack_push(&sc, 10);
 54 
 55     //遍历
 56     trvel_stack(&sc);
 57 
 58     //pop一个数据出来
 59     pop(&sc);
 60 
 61     trvel_stack(&sc);
 62 
 63     return 0;
 64 }
 65 
 66 /*
 67 栈的初始化操作
 68 */
 69 struct Stack init_stack(){
 70     //先定义一个栈
 71     STACK sc;
 72 
 73     //初始化栈中的第一个节点(栈低节点),该节点并没有什么实际意义,不存放数据
 74     PNODE pbottom = (PNODE)malloc(sizeof(NODE));
 75     if (pbottom == NULL) {
 76         printf("内存分配失败
");
 77         exit(1);
 78     }
 79     pbottom->pnext = NULL;
 80 
 81     //将栈顶指向该元素
 82     sc.stop = pbottom;
 83 
 84     return sc;
 85 }
 86 
 87 /*
 88 栈的push操作
 89 */
 90 void stack_push(STACK *sc, int data){
 91     printf("调用了!
");
 92 
 93     //新创建一个节点
 94     PNODE pnew = (PNODE)malloc(sizeof(NODE));
 95     printf("%p
", pnew);
 96     if (pnew == NULL) {
 97         printf("内存分配失败
");
 98         exit(1);
 99     }
100     pnew->data = data;
101 
102     //将新节点指向原top节点,将栈的top指向该新节点
103     pnew->pnext = sc->stop;
104     sc->stop = pnew;
105 }
106 
107 /*
108 栈的pop操作
109 */
110 int pop(STACK *sc){
111     int res;
112     //判断栈是否为空
113     if (is_empty(sc) == 1){
114         printf("栈为空,不可pop
");
115         exit(1);
116     } else {
117         PNODE ptmp = sc->stop;  //先将需要被pop出的节点存储起来
118         sc->stop = sc->stop->pnext;  //将栈的top指向下一个节点
119         res = ptmp->data;
120         free(ptmp);  //将地址释放
121         return res;
122     }
123 }
124 
125 /*
126 判断栈是否为空的操作
127 */
128 int is_empty(STACK *sc){
129     PNODE p = sc->stop;
130     if (p == NULL || p->pnext == NULL) {
131         return 1;  //true表示为空
132     } else {
133         return 0;  //false表示非空
134     }
135 }
136 
137 /*
138 遍历栈
139 */
140 void trvel_stack(STACK *sc){
141     if (is_empty(sc) == 1) {
142         printf("栈为空哟,无法遍历呢~
");
143         exit(1);
144     } else {
145         printf("遍历调用了!
");
146         PNODE ptmp = sc->stop;
147         while (ptmp->pnext != NULL) {  //直到最后一个为NULL的节点,该节点就是最后一个节点
148             printf("%d
", ptmp->data);
149             ptmp = ptmp->pnext;
150         }
151     }
152     
153 }