目录
3.2.1 栈的链式存储结构定义
- 采用链表存储的栈称为链栈,这里采用带头结点的单链表实现

- 链栈的优点是不存在栈满上溢的情况
- 入栈出栈操作在单链表的表头(入栈也叫头插)
- 在栈顶插入数据元素,新插入的数据元素成为新的栈底

3.2.2栈的链式存储结构及其基本运算的实现
1.链栈中数据结点的类型LinkStNode声明如下:
typedef struct linknode
{ ElemType data; //数据域
struct linknode *next; //指针域
} LinkStNode;//链栈节点类型
2.在链栈中,栈的基本运算算法如下:
(1)初始化栈initStack(&s)
建立一个空栈s,实际上是创建链栈的头结点,并将其next域置为NULL,算法如下:
void InitStack(LinkStNode *&s)
{ s=(LinkStNode *)malloc(sizeof(LinkStNode));
s->next=NULL;
}
//创建一个空栈
(2)销毁栈DestroyStack(&s)
释放栈s占用的全部存储空间,算法如下:
void DestroyStack(LinkStNode *&s)
{ LinkStNode *p=s,*q=s->next;//p指向头节点,q指向尾节点
while (q!=NULL) //循环到p为空
{ free(p); //释放p节点
p=q; //p,q同步后移
q=p->next;
}
free(p); //此时p指向尾结点,释放其空间
}
(3)判断栈是否为空StackEmpty(s)
栈S为空的条件是s->next==NULL,即单链表中没有数据结点,算法如下:
bool StackEmpty(LinkStNode *s)
{
return(s->next==NULL);
}
(4)进栈Push(&s,e)
将新数据结点插入到头结点之后,算法如下:
void Push(LinkStNode *&s,ElemType e)
{ LinkStNode *p;
p=(LinkStNode *)malloc(sizeof(LinkStNode));//新建节点p
p->data=e; //新建元素e对应的结点p
p->next=s->next; //插入p结点作为开始结点
s->next=p;
}

(5)出栈Pop(&s,&e)
在栈不为空的条件下,将头结点后继数据结点的数据域赋给e,然后将其删除,算法如下:
bool Pop(LinkStNode *&s,ElemType &e)
{ LinkStNode *p;
if (s->next==NULL) //栈空的情况
return false; //返回假
p=s->next; //p指向开始结点
e=p->data; //提取首节点值
s->next=p->next; //删除p结点
free(p); //释放p结点,即被删除节点的存储空间
return true; //返回真
}
(6)取栈顶元素GetTop(s,e)
在栈不为空的条件下,将头结点后继数据结点的数据域赋给e,算法如下:
bool GetTop(LinkStNode *s,ElemType &e)
{ if (s->next==NULL) //栈空的情况
return false;
e=s->next->data; //提取首节点值
return true;
}

3.思考:
链栈和顺序栈两种存储结构有什么不同?
解析:顺序栈——顺序分配 (1)在申明顺序栈类型时,就已经确定顺序栈所占空间,此处空间为一块连续的存储单元; (2)而确定空间之后,经过后续不断有元素进栈,栈中的元素位置会发生变化,同时可能出现栈满的情况
链栈——动态分配 进栈一个元素,就给该元素分配空间,相对于顺序栈而言,更便于元素的修改
4.例题
编写一个算法判断输入的表达式中括号是否配对(假设只含有左、右圆括号)
算法设计思路: 一个表达式中的左右括号是按最近位置配对的,所以利用一个栈来进行求解,这里采用链栈


⑤ 栈空且exp扫描完,返回true

运行结果:

其中,核心代码如下:

5.小结


1993

被折叠的 条评论
为什么被折叠?



