1、栈 分为顺序栈和链栈 对应数组和链表
top指向栈顶元素,设置空栈为-1;存在一个元素时top=0
栈的长度为StackSize
定义栈的结构体:
1 #include "stdio.h" 2 3 /* 存储空间初始分配量 */ 4 #define MAXSIZE 20 5 6 /* SElemType类型根据实际情况而定,这里假设为int */ 7 typedef int SElemType; 8 9 /* 顺序栈结构 */ 10 typedef struct 11 { 12 SElemType data[MAXSIZE]; 13 int top; /* 用于栈顶指针 */ 14 }SqStack; 15 16 int main() 17 { 18 }
进栈操作:push
1 int Push(SqStack * S, SElemType e){ 2 if(S->top == MAXSIZE-1){ 3 //栈满 4 return 0; 5 } 6 S->top++; 7 S->data[S->top] = e; 8 return 1; 9 }
初始化一个空栈:
//初始化栈 int InitStack(SqStack S){ S->top = -1; return 1; }
栈的遍历:
1 /* 从栈底到栈顶依次对栈中每个元素显示 */ 2 Status StackTraverse(SqStack S) 3 { 4 int i; 5 i=0; 6 while(i<=S.top) 7 { 8 visit(S.data[i++]); 9 } 10 printf("\n"); 11 return OK; 12 } 13 14 Status visit(SElemType c) 15 { 16 printf("%d ",c); 17 return OK; 18 }
注:S->top代表着栈对应数组的下标0,1,2 。。。
出栈:
1 int Pop(SqStack* S, SElemType* e){ 2 if(-1 == S->top){ 3 return 0; 4 } 5 *e = S->data[S->top]; 6 S->top--; 7 return 1; 8 }
获取栈顶元素:
1 /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */ 2 Status GetTop(SqStack S,SElemType *e) 3 { 4 if (S.top==-1) 5 return ERROR; 6 else 7 *e=S.data[S.top]; 8 return OK; 9 }
判断栈是否为空:
1 |
/* 若栈S为空栈,则返回TRUE,否则返回FALSE */ |
2 |
Status StackEmpty(SqStack S) |
3 |
{ |
4 |
if (S.top==-1) |
5 |
return TRUE; |
6 |
else |
7 |
return FALSE; |
8 |
} |
置空栈:
1 |
/* 把S置为空栈 */ |
2 |
Status ClearStack(SqStack *S) |
3 |
{ |
4 |
S->top=-1; |
5 |
return OK; |
6 |
} |
2、链栈
链栈对应不含头结点的单链表
top代表指向栈顶的指针,这里和链表中的next合并为一个,当top=null时,链栈为空
定义链栈的结构体:
下面是单链表的结构体:
1 typedef struct Node 2 { 3 ElemType data; 4 struct Node *next; 5 }Node; 6 typedef struct Node *LinkList; /* 定义LinkList */
相似的链栈的结构体为:
1 /* 链栈结构 */ 2 typedef struct StackNode 3 { 4 SElemType data; 5 struct StackNode *next; 6 } StackNode,*LinkStackPtr;
这只是链栈一个节点的定义,还需要添加链栈的栈顶指针:
1 typedef int Status; 2 /* SElemType类型根据实际情况而定,这里假设为int */ 3 typedef int SElemType; 4 5 /* 链栈结构 */ 6 typedef struct StackNode 7 { 8 SElemType data; 9 struct StackNode *next; 10 } StackNode,*LinkStackPtr; 11 12 typedef struct 13 { 14 LinkStackPtr top; 15 int count; 16 } LinkStack;
其中count是用来计算栈中元素的个数的,也可以根据需要添加属性等
入栈,其实就是添加新的节点,下面这张图描述的很清楚:
实际上就两步:1、将新的节点next指向原栈顶元素 2、将top指针指向新的节点
1 int Push(LinkStack* L, SElemType e){ 2 //首先分配节点 3 LinkStackPtr p = (LinkStackPtr)malloc(sizeof(StackNode)); 4 p->data = e; 5 p->next = S->top; 6 S->top = p; 7 S->count++; 8 return 1; 9 }
初始化空栈:条件count=0,且top为null
1 /* 构造一个空栈S */ 2 Status InitStack(LinkStack *S) 3 { 4 S->top = (LinkStackPtr)malloc(sizeof(StackNode)); 5 if(!S->top) 6 return ERROR;//分配失败 7 S->top=NULL; 8 S->count=0; 9 return OK; 10 }
遍历的过程和链表完全类似,这里不再赘述:
1 Status StackTraverse(LinkStack S) 2 { 3 LinkStackPtr p; 4 p=S.top; 5 while(p) 6 { 7 visit(p->data); 8 p=p->next; 9 } 10 printf("\n"); 11 return OK; 12 } 13 14 Status visit(SElemType c) 15 { 16 printf("%d ",c); 17 return OK; 18 }
出栈操作:这里主要注意一点就是删除的节点要释放掉 核心代码就是S->top = S->top->next;
1 /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */ 2 Status Pop(LinkStack *S,SElemType *e) 3 { 4 LinkStackPtr p; 5 if(StackEmpty(*S)) 6 return ERROR; 7 *e=S->top->data; 8 p=S->top; /* 将栈顶结点赋值给p,见图中① */ 9 S->top=S->top->next; /* 使得栈顶指针下移一位,指向后一结点,见图中② */ 10 free(p); /* 释放结点p */ 11 S->count--; 12 return OK; 13 }
注:这里的Status是预定义的,这里简化表示int
置空链栈会比顺序栈稍微复杂一些:置空顺序栈只需一步S->top = -1即可,而对于链栈置空实质上就是删除整个链表所有的节点,这里关键的问题就是移动待删除节点指针到下一个位置后,没法直接free掉,这样说起来不好理解,直接看代码:
1 /* 把S置为空栈 */ 2 Status ClearStack(LinkStack *S) 3 { 4 LinkStackPtr p,q; 5 p=S->top; 6 while(p) 7 { 8 q=p; 9 p=p->next; 10 free(q); 11 } 12 S->count=0; 13 return OK; 14 }
其实就是先将待删除节点付给一个临时指针,然后当前指针移动到下一个位置后,就可以将这个临时指针给free掉
那这里来分析一下顺序栈和链栈的区别:两者的时间复杂度都为O(1)
顺序栈定位元素更加方便,需提前分配内存区域;链栈在内存充足的情况下大小是无限的,存取定位没有顺序栈快,每个节点还有指针域,某种程度上增加了空间开销
所以根据不同情况选择