- 链栈的相关定义:所谓链栈就是使用链式存储的栈,链栈只允许在头指针处进行节点元素的插入与删除,所以头指针被称为栈顶,链栈节点的定义与一般的链表的结点定义一样,有数据域和指针域“next”用于指向下一个节点元素。使用链表来实现栈这种结构的好处是不用事先申请一个足够的空间来存储栈的数据元素,而是边创建节点元素,边动态的为该元素申请存储空间,也就是“走一步,看一步”,所以其不用担心内存不够的问题。
- 链栈的定义与相关操作的C语言实现
- 链表结构的定义
typedef struct LinkStack
{
struct LinkStack *next; //指针域用于存放头指针和下一个指针的位置
int data;
}LinkStack,**Ls;
代码解释 ,对于代码
typedef struct LinkStack
{
....
}LinkStack,**Ls;
它的意思是为结构体类型"struct LinkStack"其别名为"LinkStack",这样的话会简化该结构体变量的定义,即可以直接用该别名定义该结构体类型的变量即指令
struct LinkStack a;
和以下指令的作用都是一样的都定义了一个结构体类型的变量a
LinkStack a;
而对于下述代码而言
typedef struct LinkStack
{
.....
}**Ls;
其代表的含义是为该结构体类型的二级指针类型定义了一个别名Ls,那么就可以用该别名来定义结构体的二级指针类型的变量,即以下两条指令的效果都是一样的都定义了一个结构体类型的二级指针变量a
Ls a;
Linkstack **a;
2.向链栈中添加元素的操作代码
void push(Ls top, int e) //top为指向栈顶指针的指针;此函数用于向链栈中添加元素
{
LinkStack *p;
p = (LinkStack *)malloc(sizeof(LinkStack)); //应该增加代码判断malloc是否分配内存成功;
if (p)
{
p->data = e;
p->next = *top;
*top = p;
}
else
{
printf("ERROR\n");
exit(-1);
}
}
3.获取栈顶元素的代码
int GetTop(Ls top)
{
if (!(*top))
{
printf("ERROR:栈为空栈没有栈顶元素\n");
return -1;
}
else
return (*top)->data;
}
4.删除栈顶元素并将栈顶元素返回
int pop(Ls top)
{
LinkStack *p;
int temp; //用于暂时存储栈顶元素的值
//先判断是否是空栈
if (!(*top))
{
printf("ERROR:栈是空栈,没有栈顶元素\n");
return -1;
}
else
{
p = *top;
*top = (*top)->next;
temp = p->data;
free(p);
return temp;
}
}
- 完整源代码以及运行效果截图
1.源程序代码
//链栈的定义与实现
//链栈的定义:所谓链栈就是使用链表来进行存储的栈称为链栈
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct LinkStack
{
struct LinkStack *next; //指针域用于存放头指针和下一个指针的位置
int data;
}LinkStack,**Ls;
//用于向链栈中添加元素
void push(Ls s, int e);
//若栈不空则用于返回栈顶元素的值
int GetTop(Ls top);
//若栈不空则删除栈顶元素并将其作为函数返回值返;
int pop(Ls top);
int main()
{
LinkStack *top = NULL;
int test[4] = { 1,2,3,4 }; //测试集,用于创建一个链栈
for (int i = 0; i < 4; i++)
{
push(&top, test[i]); //创建一个链栈使得1,2,3,4为链栈中元素节点的值
}
int elem_top = GetTop(&top); //用于获取链栈的栈顶元素以及测试链栈是否创建成功
printf("栈顶元素的值为:%d\n", elem_top);
//删除栈顶元素;
pop(&top);
printf("执行删除栈顶元素操作之后,栈顶元素变成了:%d\n", GetTop(&top)); //判断上一步的删除栈顶元素操作是否成功
}
void push(Ls top, int e) //top为指向栈顶指针的指针;此函数用于向链栈中添加元素
{
LinkStack *p;
p = (LinkStack *)malloc(sizeof(LinkStack)); //应该增加代码判断malloc是否分配内存成功;
if (p)
{
p->data = e;
p->next = *top;
*top = p;
}
else
{
printf("ERROR\n");
exit(-1);
}
}
int GetTop(Ls top)
{
if (!(*top))
{
printf("ERROR:栈为空栈没有栈顶元素\n");
return -1;
}
else
return (*top)->data;
}
int pop(Ls top)
{
LinkStack *p;
int temp; //用于暂时存储栈顶元素的值
//先判断是否是空栈
if (!(*top))
{
printf("ERROR:栈是空栈,没有栈顶元素\n");
return -1;
}
else
{
p = *top;
*top = (*top)->next;
temp = p->data;
free(p);
return temp;
}
}
运行效果截图
对于以上代码值得注意的是每个操作的形式参数都是一个二级指针,这是因为如果使用一级指针,我们在给函数传参的时候实际上只是将实际参数的值拷贝了一份给形式参数,所以在代码中改变形式参数指针的指向并不会改变实参的指向,这么说可能比较难以理解,以下是一个示例 ,比如我们在main函数中定义了栈顶指针head
LinkStack *head = NULL;
然后在定义push函数的时候这样定义,将形式参数的二级指针变成一级指针,如下图
void push(LinkStack *top, int e)
{
LinkStack* p;
p = (LinkStack*)malloc(sizeof(LinkStack));
if (p)
{
p->data = e;
p->next = top;
top = p;
}
else
{
printf("ERROR\n");
exit(-1);
}
}
此时调用函数push(head,e)向栈中插入元素时,我的本意是想改变栈顶指针head,让其指向元素值为e的结点,但是在实际运行中会发现栈顶指针依然是空指针NULL,其指向未被改变,这是因为在进行传参的时候只是将实参的值拷贝了一份给形式参数,即会在另一个内存空间开辟一个内存用于临时存放top变量,然后将实际参数head的值NULL拷贝一份给top变量,即top=NULL;所以在push函数中改变的是形式参数top的指向,与实际参数head无关,head依然是一个空指针,所以若我们想要改变head的指向,应该找到head变量的地址,才能对其指向改变,那么这时应该传入的参数是head的地址,即指向指针的指针,所以使用二级指针。
- 用链栈实现将一个十进制整数转换为等值的二进制数或者八进制数
- 在上一篇博客中我讲了用顺序栈实现将一个十进制整数转换成等值的二进制数或者八进制数的算法,感兴趣的话可以看看。博客链接:http://t.csdn.cn/kfBn1
完整程序代码以及运行效果截图
程序源代码
//链栈的定义与实现
//链栈的定义:所谓链栈就是使用链表来进行存储的栈称为链栈
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct LinkStack
{
struct LinkStack* next; //指针域用于存放头指针和下一个指针的位置
int data;
}LinkStack, ** Ls;
//用于向栈中插入元素
void push(Ls s, int e);
//若栈不空则用于返回栈顶元素的值
int GetTop(Ls top);
//若栈不空则删除栈顶元素并将其作为函数返回值返;
int pop(Ls top);
int main()
{
//栈的应用,将10进制数转换成等值的二进制数,由于将十进制数转换成其它进制数的时候符合栈的后进先出的特性所以可以使用栈这种数据结构来实现将10进制数转换成2进制数的算法,
//这里其他进制数指的是八进制或者二进制,不指十六进制数
//由于我们在将十进制数转换成其他的r进制数时用到的是除r取余法,在这个过程中我们所得的余数都应该被存储起来,
//然后最后逆序输出便可以得到一个十进制数的其它r进制数的表示形式,所以我们应该先应该定义一个空栈用于存放我们的余数
LinkStack* elem_binary = NULL;
int n;//带转换的十进制数
int r;//想要将n转换成的进制数,如二进制数,八进制数等;
int mo; //用于保存余数
printf("请按顺序输入你想要进行转换的十进制数,以及你想将此十进制数转换成几进制数用阿拉伯数字表示,两个数字之间用半角逗号','分割\n");
scanf("%d,%d", &n, &r);
while (n > 0)
{
mo = n % r;
push(&elem_binary, mo); //将求得的元素进行入栈
n = n / r;
}
//将所有的余数入栈后进行逆序输出其实就是从栈顶元素开始进行出栈操作
while (elem_binary!=NULL)
{
printf("%d", pop(&elem_binary));
}
return 0;
}
void push(Ls top, int e) //top为指向栈顶指针的指针;此函数用于向链栈中添加元素
{
LinkStack* p;
p = (LinkStack*)malloc(sizeof(LinkStack)); //应该增加代码判断malloc是否分配内存成功;
if (p)
{
p->data = e;
p->next = *top;
*top = p;
}
else
{
printf("ERROR\n");
exit(-1);
}
}
int GetTop(Ls top)
{
if (!(*top)) //注意赋值符号和等好的区别;可以细心一点吗;
{
printf("ERROR:栈为空栈没有栈顶元素\n");
return -1;
}
else
return (*top)->data;
}
int pop(Ls top)
{
LinkStack* p;
int temp; //用于暂时存储栈顶元素的值
//先判断是否是空栈
if (!(*top))
{
printf("ERROR:栈是空栈,没有栈顶元素\n");
return -1;
}
else
{
p = *top;
*top = (*top)->next;
temp = p->data;
free(p);
return temp;
}
}
运行效果截图