栈
理解栈
栈是一个特殊的线性表,它最特的地方就是它的操作被限制在了栈顶,栈底是固定的,操作也就只有入栈和出栈。
最先进栈的元素不是只能最后出栈。
栈只是对入栈出栈的位置进行了限制,但是没有对进出时间有限制。
栈的存储结构
顺序存储
栈的结构定义
typedef int DataType;
struct SeqStack
{
int MAX; //最大容量
int top; //栈顶指针
DataType *elem; //存放元素的起始指针
};
typedef struct SeqStack *SeqStack;
栈的创建
SeqStack SetNullStack_Seq(int m) //创建空顺序栈
{
SeqStack sstack = (SeqStack)malloc(sizeof(struct SeqStack));
if (sstack != NULL){
sstack->elem = (int*)malloc(sizeof(int)*m);
if (sstack->elem != NULL){
sstack->MAX = m;
sstack->top = -1;
return(sstack);
}
else {
free(sstack);
return NULL;
}
}
else{
printf("out of space");
return NULL;
}
}
栈的判空
判断top值是否为-1来判断栈是否为空,因为一般栈的首元素插入都是在‘0’这个位置,就像插入数组的第一位。
int IsNullStack_seq(SeqStack sstack)
{
return(sstack->top == -1);
}
顺序进栈
void Push_seq(SeqStack sstack, int x) //入栈
{
if (sstack->top >= (sstack->MAX - 1)) //检查栈是否满
printf("overflow! \n");
else{
sstack->top++; //若不满,先修改栈顶变量
sstack->elem[sstack->top] = x;//把元素x放到栈顶变量的位置中
}
}
顺序出栈
void Pop_seq(SeqStack sstack)//出栈
{
if (IsNullStack_seq(sstack)) //判断栈是否为空
printf("Underflow!\n");
else
sstack->top = sstack->top - 1;//栈顶减1
}
拓:求栈顶元素
DataType Top_seq(SeqStack sstack)//求栈顶元素的值
{
if (IsNullStack_seq(sstack))//判断sstack所指的栈是否为空栈
{
printf("it is empty");
return 0;
}
else
return sstack->elem[sstack->top];
}
入栈和出栈都无循环语句,所以时间复杂度均是O(1)。
链式存储
链栈是栈顶放在单链表的头部(失去意义的头结点可以不要),基本不存在栈满的情况。
栈链的操作绝大部分和单链表类似,只是在插入和删除上,特殊一些
栈的结构定义
有头结点的结构
typedef int DataType;
struct Node{
DataType data;
struct Node* next;
};
typedef struct Node *PNode;
typedef struct Node *LinkStack;
无头结点的结构
typedef int Status;
typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */
typedef struct StackNode
{
SELemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack
{
LinkStackPtr top;
int count;
}LinkStack;
栈的创建
有头结点的创建
LinkStack SetNullStack_Link() //创建带有头结点的空链栈
{
LinkStack top = (LinkStack)malloc(sizeof(struct Node));
if (top != NULL) top->next = NULL;
else printf("Alloc failure");
return top; //返回栈顶指针
}
无头结点的创建
“把S置为空栈”个人认为是保险措施,在有头结点的创建中可以尝试使用。
/* 构造一个空栈S */
Status InitStack(LinkStack *S)
{
S->top = (LinkStackPtr)malloc(sizeof(StackNode));
if(!S->top)
return 0;
S->top=NULL;
S->count=0;
return OK;
}
/* 把S置为空栈 */
Status ClearStack(LinkStack *S)
{
LinkStackPtr p,q;
p=S->top;
while(p)
{
q=p;
p=p->next;
free(q);
}
S->count=0;
return OK;
}
栈的判空
有头结点的链栈判空
int IsNullStack_link(LinkStack top) //判断一个链栈是否为空
{
if (top->next == NULL)
return 1;
else
return 0;
}
无头结点的链栈判空
Status StackEmpty(LinkStack S)
{
if (S.count==0)
return 1;
else
return 0;
}
链式进栈
有头结点的进栈
void Push_link(LinkStack top, DataType x) //进栈
{
PNode p;
p = (PNode)malloc(sizeof(struct Node));
if (p == NULL)
printf("Alloc failure");
else
{
p->data = x;
p->next = top->next;
top->next = p;
}
}
无头结点的进栈
/* 插入元素e为新的栈顶元素 */
Status Push(LinkStack *S,SElemType e)
{
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top; /* 把当前的栈顶元素赋值给新结点的直接后继,见图中① */
S->top=s; /* 将新的结点s赋值给栈顶指针,见图中② */
S->count++;
return OK;
}
链式出栈
有头结点的出栈
void Pop_link(LinkStack top)// 删除栈顶元素
{
PNode p;
if (top->next == NULL)
printf("it is empty stack!");
else
{
p = top->next;
top->next = p->next;
free(p);
}
}
无头结点的出栈
(这个直接自带获取栈顶元素的部分)
/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(LinkStack *S,SElemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))
return ERROR;
*e=S->top->data;
p=S->top; /* 将栈顶结点赋值给p,见图中③ */
S->top=S->top->next; /* 使得栈顶指针下移一位,指向后一结点,见图中④ */
free(p); /* 释放结点p */
S->count--;
return OK;
}
拓:求栈顶元素
有头结点
DataType Top_link(LinkStack top)// 求栈顶元素的值
{
if (top->next == NULL)
{
printf("It is empty stack!");
return 0;
}
else
return top->next->data;
}
入栈和出栈都无循环语句,所以时间复杂度均是O(1)。
双栈对比
- 时间复杂度都为O(1)
- 顺序栈:若元素变化范围可控,即长度固定,则用顺序栈好一些(优势:存取时定位方便)
- 链栈:若元素变化范围不可控,即长度不固定,则用链栈好一些(优势:可以很大!)
栈的拓展
正在开发ing…………