栈的概念:只能在表尾进行插入(入栈)和删除(出栈)的数据结构(受到限制的线性表)。
表尾比较特殊,我们一般把这个表尾叫做栈顶,另一端栈底。没有数据节点,则叫做空栈。
上一篇文章将的是顺序栈(包括栈的概念),顺序栈结构体设计里面一共三个成员,即一个指针(用来接收malloc从堆内申请的一整块内存)、栈顶指针和存储当前最大的容量的值;而链栈的结构体和单链表相同,只不过这里要注意的点在于用单链表的头插和头删代替表尾进行插入和删除。
链栈的结构体设计:
typedef int ELEM_TYPE;
//链式栈的结构体声明: 直接用单链表的即可
typedef struct LStack
{
ELEM_TYPE data;//数据域
struct LStack* next;//指针域
}LStack, * PLStack;
链栈可执行函数声明:
//初始化
void Init_LStackl(struct LStack* ps);
//入栈(插入)
bool Pushl(PLStack ps, ELEM_TYPE val);
//出栈(获取删除的栈顶元素值,并且删除) //用到一个输出参数
bool Popl(PLStack ps, ELEM_TYPE* rtval);
//获取栈顶元素(获取栈顶元素值,但是不删除) //用到一个输出参数
bool Topl(PLStack ps, ELEM_TYPE* rtval);
//判空
bool IsEmptyl(PLStack ps);
//获取有效长度
int Get_lengthl(PLStack ps);
//清空
void Clearl(PLStack ps);
//销毁
void Destroyl(PLStack ps);
//打印
void Show(PLStack ps);
初始化:
void Init_LStackl(struct LStack* ps)
{
assert(ps != NULL);
ps->next = NULL;
}
入栈:
bool Pushl(PLStack ps, ELEM_TYPE val)
{
assert(ps != NULL);
//assert
//1.购买新节点
struct LStack* pnewnode = (struct LStack*)malloc(1 * sizeof(struct LStack));
assert(pnewnode != NULL);
pnewnode->data = val;
//2.找到合适的插入位置(头插,不用找,因为头结点就是合适位置)
//3.进行插入
pnewnode->next = ps->next;
ps->next = pnewnode;
return true;
}
出栈:(获取删除的栈顶元素值,并且删除) 用到一个输出参数
bool Popl(PLStack ps, ELEM_TYPE* rtval)
{
assert(ps != NULL);
assert(rtval != NULL);
if (IsEmptyl(ps))//确实链栈至少有一个节点
{
return false;
}
*rtval = ps->next->data;//将一会要出栈的值 通过输出参数rtval带出来
//申请一个临时指针指向待删除节点,跨越指向,释放待删除节点
PLStack p = ps->next;
ps->next = p->next;
free(p);
return true;
}
获取栈顶元素(获取栈顶元素值,但是不删除) //用到一个输出参数
bool Topl(PLStack ps, ELEM_TYPE* rtval)
{
assert(ps != NULL);
assert(rtval != NULL);
if (IsEmptyl(ps))//确实链栈至少有一个节点
{
return false;
}
*rtval = ps->next->data;//将一会要出栈的值 通过输出参数rtval带出来
return true;
}
判空:
bool IsEmptyl(PLStack ps)
{
assert(ps != NULL);
return ps->next == NULL;
}
获取有效长度:
int Get_lengthl(PLStack ps)
{
assert(ps != NULL);
int cout=0;
//只要调用我们之前写的 两个for循环里面的那个 不需要前驱的for循环, 遍历一遍即可
for (PLStack p = ps->next; p != NULL; p = p->next)
{
cout++;
}
return cout;
}
清空,销毁,打印
//清空
void Clearl(PLStack ps)
{
assert(ps != NULL);
Destroyl(ps);
}
//销毁
void Destroyl(PLStack ps)
{
assert(ps != NULL);
while (ps->next != NULL)
{
PLStack p = ps->next;
ps->next = p->next;
free(p);
}
}
//打印
void Show(PLStack ps)
{
assert(ps != NULL);
for (PLStack p = ps->next; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
测试:
LStack head;//建立一个头结点
Init_LStackl(&head);//初始化这个头结点
for(int i=0; i<20; i++)//20 19 18.....3 2 1
{
Pushl(&head, i+1);//将1到20入栈
}
Show(&head);//打印1到20
printf("length = %d\n", Get_lengthl(&head));//打印有效长度
//出栈,并且获取出栈的元素
ELEM_TYPE tmp;
bool tag1 = Popl(&head, &tmp);
if(tag1)
{
printf("pop = %d\n", tmp);//打印出栈的值
}
Show(&head);//打印出栈后的链栈
printf("length = %d\n", Get_lengthl(&head));//打印有效长度
//获取栈顶元素值(不删除)
ELEM_TYPE flg;
bool tag2 = Topl(&head, &flg);
if(tag2)
{
printf("top = %d\n", flg);//打印栈顶元素值
}
Show(&head);//打印
printf("length = %d\n", Get_lengthl(&head));//打印有效长度
Destroyl(&head);//销毁
结果:
没有内存泄漏