可以说线性表的一个具体应用就是栈,可能看完以下代码你觉得这是什么嘛有什么卵用啊,但是栈在现实生活的中的应用,或者说这种概念的应用是非常广泛的。
比如说各种浏览器的返回上一级功能。
栈与线性表最大的差别就是它对数据的操控是受限的,遵守Last in First out(LIFO)“后进先出原则”的数据结构都可以叫栈。
栈一般长这个样子(美工欠下了3.5个亿带着他的小姨子跑了!)
右边是我们对线性表之单链表的正常理解,数据从表头到表尾依次排放。
左边是栈的特性,处于上层的元素(较新)叫top,处于底部的元素(最先放进去的那个)叫bottom。
这里注意一下top指针指向的位置,是一个空白的位置,而不是最顶层的那个数据。
而bottom指针则一直指向最底层的那个数据。
对栈的常见操作有push(入栈,将数据放入栈)和pop(出栈,将数据提取出来)。
这两个操作只能影响最顶层的元素。
以下是我的具体的代码实现,实现的方式有很多种,以下仅供参考。
首先是栈的头文件.h
Stack.h
声明一下结构体及函数。
链表一样,栈你可以搞成链式也可以搞成顺序的,这里我用了顺序存储结构,方便操作。
1 #ifndef _STACK_H_ 2 #define _STACK_H_ 3 4 #include<stdio.h> 5 #include<stdlib.h> 6 7 //以下两个数据大小视实际情况而定 8 //初始栈容量 9 #define STACK_INIT_SIZE 10 10 //每次扩容的增量 11 #define STACKINCREMENT 10 12 13 typedef int DataType; 14 15 struct stack 16 { 17 //指向栈最顶上的元素的接下来一个位置 18 //表示新入栈的值可以放在指向的地方 19 DataType *Top; 20 //指向栈底部,最里面的元素 21 DataType *Bottom; 22 //表示了当前栈的容量 23 int stacksize; 24 }; 25 typedef struct stack STACK; 26 27 //新建栈 28 int InitStack(STACK *S); 29 30 //销毁栈 31 int DestroyStack(STACK *S); 32 33 //获取栈长度 34 int StackLength(STACK S); 35 36 //获取栈顶部元素 37 int GetTop(STACK S, DataType *e); 38 39 //Push操作,入栈,压栈 40 int Push(STACK *S, DataType e); 41 42 //Pop操作,出栈 43 int Pop(STACK *S, DataType *e); 44 45 //打印整表 46 int StackTraverse(STACK S); 47 48 #endif
Stack.c
不知道为什么感觉栈的代码比链表的简单。。。直接都贴上来吧。
首先是初始化链表和销毁链表。
1 int InitStack(STACK *S) 2 { 3 //创建出设定长度的顺序表,地址赋给bottom指针 4 S->Bottom = (DataType*)malloc(STACK_INIT_SIZE*sizeof(DataType)); 5 if (!S->Bottom) 6 { 7 return 0; 8 } 9 S->stacksize = STACK_INIT_SIZE; 10 S->Top = S->Bottom; 11 return 1; 12 } 13 14 int DestroyStack(STACK *S) 15 { 16 S->Top = NULL; 17 free(S->Bottom); 18 S->Bottom = NULL; 19 return 1; 20 }
然后是Push和Pop的函数,注释应该把代码解释的比较清楚了
1 int Push(STACK *S, DataType e) 2 { 3 //当超出当前栈的容量时进行扩容 4 //这里应该只有等于的情况 5 //而不会大于 6 if ((S->Top - S->Bottom) >= S->stacksize) 7 { 8 //realloc函数将开辟指定大小的储存空间 9 //并将原来的数据全部移到这个新的储存空间 10 S->Bottom = (DataType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(DataType)); 11 if (!S->Bottom) 12 { 13 return 0; 14 } 15 //由于重新开辟了空间 16 //需要重新根据bottom指针的位置指定top 17 S->Top = S->Bottom + S->stacksize; 18 //最后增加当前栈容量 19 S->stacksize += STACKINCREMENT; 20 } 21 //再把入栈的数据存放好 22 *(S->Top++) = e; 23 return 1; 24 } 25 26 int Pop(STACK *S, DataType *e) 27 { 28 if (S->Bottom == S->Top) 29 { 30 printf("Empty Stack!Can not pop! "); 31 return 0; 32 } 33 //top指针先减1再取值 34 *e = *(--S->Top); 35 return 1; 36 }
然后是其他一些奇奇怪怪的“辅助”函数
1 int StackLength(STACK S) 2 { 3 return S.Top - S.Bottom; 4 } 5 6 int GetTop(STACK S, DataType *e) 7 { 8 if (S.Bottom != S.Top) 9 { 10 //由于top指向的是最顶上元素的下一个位置 11 //所以取出最顶上元素的时候 12 //要把top减去1 13 *e = *(S.Top - 1); 14 return 1; 15 } 16 return 0; 17 }
最后是打印整个栈的数据的函数
1 int StackTraverse(STACK S) 2 { 3 int timer = 0; 4 int *cur=S.Bottom; 5 if (S.Bottom ==S.Top) 6 { 7 printf("Empty! "); 8 return 1; 9 } 10 while (cur < S.Top) 11 { 12 printf("%d) ", timer++); 13 printf("%d ", *(cur++)); 14 } 15 return 1; 16 }
最后随便写个main函数测试一下
Stacktest.c
1 #include"Stack.h" 2 3 int main() 4 { 5 STACK s; 6 int input=0; 7 int output = 0; 8 9 InitStack(&s); 10 StackTraverse(s); 11 12 while(scanf("%d",&input) && input!=999) 13 { 14 Push(&s, input); 15 } 16 StackTraverse(s); 17 printf("Length: %d ", StackLength(s));; 18 19 if (Pop(&s, &output)) 20 { 21 printf("Poped : %d ", output); 22 } 23 24 DestroyStack(&s); 25 return 0; 26 }
不同人的代码差别是很大的。。。但数据结构代码又没有一个标准,适和自己的需求就好,我也不敢确定我这么写最好。
如果感兴趣的话可以用栈做一个简单的计算器程序,用于计算比如
(3+(3-2))*10 = ?
这种的复合计算。
具体实现方法,我还在摸索。。