注意事项:
顺序栈:
- 实现需要使用数组,数组的元素在内存中的存储位置是连续的;
- 且需要知道数组的长度才可以使用;无法避免溢出问题;
- 当系统给数组分配了内存空间,其他的任务是不能使用这个内存空间的;
- 本算法中的顺序栈避免了溢出问题以及空间不能增长的缺陷,使用了动态分配的方法使用连续内存。存储密度 = 1;
- 顺序栈的top指针指向的是栈顶的空元素处,top - 1才是指向栈顶元素;不易实现插入和删除操作。
特点:
顺序栈是以一个不断变大的结构体为基址,因为要改变这个结构,所以传递给函数的都是这个的地址。和链表不同的是,他仅仅是传递的结构的指针,所以在函数中直接用->就可以。
#include<stdio.h>//有一个大bug,在用realloc分配内存的过程中,在大小项没有*数据类型的大小
#define STACK_INIT_SIZE 10//导致了每次只增加2字节的量,而不是2字节*4字节
#define STACK_INCREAMENT 2
typedef struct SqStack//顺序表是一个以基址为核心的数组,每次操作针对传递基址。根据需求空间,伪动态一次分配少量空间。建立一个结构体,里面包括了基址指针和栈顶指针以及基址已分配空间大小(不是已占用空间)
{
int *base; //还有另一种简单顺序栈,不需要建立结构,只需要一个基本数组,和一个代表栈顶下标的整形变量。BiTree SqStack[100];int top=-1; SqStack[++top] = t;这是非递归遍历时,临时建立的栈结构,后者是代替了push函数。t =SqStack[top--]为pop函数。Top == -1代表栈空。
int *top;
int stacksize; //需要这个结构成员来判断是否分配的空间够用
}SqStack; //不需要指向结构变量的指针,只需要传递结构地址。
void Traverse(SqStack s);
void Push(SqStack *s, int e);//相当于顺序表的插入
void Pop(SqStack *s, int *e);//相当于顺序表的删除
void InitStack(SqStack *s);
void Gettop(SqStack s);
int main()
{
SqStack s;
int i;
int e;
InitStack(&s);
for (i = 1; i <= 12;i++)
Push(&s, i);
printf("the element of stack is :\n");
Traverse(s);
getchar();
return 0;
}
void InitStack(SqStack *s)
{
s->base = (int *)malloc(STACK_INIT_SIZE * sizeof(int));
if (!s->base)
exit(1);
s->top = s->base;
s->stacksize = STACK_INIT_SIZE;
}
void Push(SqStack *s, int e)
{
if (s->top - s->base >= s->stacksize)
{
s->base = (int *)realloc(s->base, (s->stacksize + STACK_INCREAMENT) *
sizeof(int));
if (!s->base)
exit(1);
s->top = s->base + s->stacksize;
s->stacksize += STACK_INCREAMENT;
}
*(s->top)++ = e;
}
void Pop(SqStack *s, int *e)
{
if (s->top == s->base)
exit(1);
(*e) = *--(s->top);
}
void Traverse(SqStack s)
{
int *q = s.base;
while (s.top > q)
printf("%d ", *q++);
}
int SqStacklength(SqStack s)
{
return s.top - s.base;
}
void Gettop(SqStack s, int *e)
{
if (s.top <= s.base)
exit(1);
else
*e = *(s.top - 1);
}
链栈:
- 实现使用链表,链表的元素存储在不同的地址;
- 动态申请地址,即可以以非常小的内存空间开始;
- 当某项不使用内存时,可以将内存返还给系统;
- 存储密度<1;链栈的top指针相当于链表中的head指针,即指向实在的元素;
- 相比于顺序栈易实现插入和删除操作且不易出现栈满的情况。
特点:
(易于实现)入栈使用头插法,其余都与链表差不多。头插头删
链栈最好设置有头结点的链表结构,因为都是在头部插入结点,所以如没有头结点,需要使用指向头指针的指针来用来传递函数参数。
#include<stdio.h>
#include<stdlib.h>
typedef struct SNode
{
int data;
struct SNode * next;
}SNode, *LinkStack;
LinkStack Creat();
void Push(LinkStack s, int e);
void Pop(LinkStack s, int *e);
void Traverse(LinkStack s);
int main()
{
LinkStack s = Creat();
Push(s, 5);
printf("After push:\n");
Traverse(s);
printf("After Pop:\n");
int e;
Pop(s, &e);
Traverse(s);
printf("the number e is:%d", e);
return 0;
}
LinkStack Creat()
{
LinkStack s = (LinkStack)malloc(sizeof(SNode));
s->next = NULL;
s->data = -1;
int i;
for (i = 1; i <= 10;i++)
Push(s, i);
return s;
}
void Push(LinkStack s, int e)//相当于插入
{
LinkStack p = (LinkStack)malloc(sizeof(SNode));
p->data = e;
p->next = s->next;
s->next = p;
}
void Pop(LinkStack s, int *e)//相当于删除
{
if (s)
{
LinkStack p = s->next;
s->next = p->next;
(*e) = p->data;
free(p);
}
else
exit(1);
}
void Traverse(LinkStack s)
{
LinkStack q = s->next;
while (q)
{
printf("%d ", q->data);
q = q->next;
}
printf("\n");
}