一个简单的链栈:
#include<stdio.h>
typedef struct stacknode // 栈的存储结构
{ int data; // 定义数据类型
struct stacknode *next; // 定义一个结构体的链指针
} StackNode;
typedef struct
{
StackNode *top;
}LinkStack;//栈顶指针
//初始化栈
void InitLinkStack(LinkStack &s)
{
s.top=NULL;
}
//判栈空
int IsEmpty(LinkStack &s)
{
if(NULL==s.top)
return 1;
else
return 0;
}
//入栈
void Push(LinkStack &s,int x)
{
StackNode *p=new StackNode;//申请新结点
p->data=x;
p->next=s.top;
s.top=p;
}
//出栈
int Pop(LinkStack &s,int &x)
{
if(IsEmpty(s))
return 0;
else
{
StackNode *p=s.top;
x=p->data;
s.top=p->next;
delete p;
return 1;
}
}
void ShowStack(LinkStack &s)
{
if(IsEmpty(s))
printf("栈为空!\n");
else
{
StackNode *p=s.top;
printf("栈元素为:");
while(p!=NULL)
{
printf("%6d",p->data);
p=p->next;
}
printf("\n");
}
}
int main()
{
LinkStack s;
int x;
InitLinkStack(s);
Push(s,1);
Push(s,2);
Push(s,3);
ShowStack(s);
Pop(s,x);
ShowStack(s);
}
对于链栈,和顺序栈最大的不同就是没有大小限制。然后在输出的时候,你要一个一个的遍历,同时delete这个节点,释放空间。
再看看进制转换的程序例子,当你在写他的时候,你会遇到一个很诡异的问题,当时我这个问题想了半天才整明白逻辑。
#include <iostream>
#include <stdio.h>
typedef struct LinkNode{
int data;
struct LinkNode *next;
}linknode;
typedef struct LinkStack{
LinkNode *top;
}linkstack;
//初始化
void CreateLink(linkstack &head){
head.top=NULL;
}
//判栈空
bool IsEmpty(linkstack &head)
{
if(NULL==head.top)
return true;
else
return false;
}
//------------------------------------------------------我是功能块分割线------------------------------------------------------//
//出栈
void OutStack(linkstack &head){
if(IsEmpty(head)){
printf("栈里面啥都没有!!\n");
} else {
linknode *p;
int z=1;
while(z){
p=head.top;
printf("%d ",p->data);
if(p->next){
head.top=p->next;
delete p;
} else {
z=0;
}
}
}
}
//进栈
void InStack(linkstack &head,int num){
linknode *p=new linknode;
p->data=num;
p->next=head.top;
head.top=p;
}
//------------------------------------------------------我是功能块分割线------------------------------------------------------//
//转二功能
void TWO(linkstack &head,int tenNum){
int num1;
while(1){
num1=tenNum%2;
InStack(head,num1);
tenNum/=2;
if(tenNum == 1){
InStack(head,tenNum);
break;
}
}
printf("该数的二进制形式:");
OutStack(head);
}
//转二
void ChangeTwo(){
linkstack head;
CreateLink(head);
int tenNum;
printf("请输入十进制数:");
scanf("%d",&tenNum);
TWO(head,tenNum);
}
//------------------------------------------------------我是功能块分割线------------------------------------------------------//
//转八功能
void EIGHT(linkstack &head,int eightNum){
int num1;
while(1){
num1=eightNum%8;
InStack(head,num1);
eightNum/=8;
if(eightNum <= 7 && eightNum>0){
InStack(head,eightNum);
break;
}
if(eightNum == 0){
break;
}
}
printf("该数的八进制形式:");
OutStack(head);
}
//转八
void ChangeEight(){
linkstack head;
CreateLink(head);
int eightNum;
printf("请输入十进制数:");
scanf("%d",&eightNum);
EIGHT(head,eightNum);
}
//------------------------------------------------------我是功能块分割线------------------------------------------------------//
//转十六功能
void SIXTEEN(linkstack &head,int sixteenNum){
int num1;
while(1){
num1=sixteenNum%16;
InStack(head,num1);
sixteenNum/=16;
if(sixteenNum <= 15 && sixteenNum>0){
InStack(head,sixteenNum);
break;
}
if(sixteenNum == 0){
break;
}
}
int i=0;
linknode *p;
printf("该数的十六进制形式:");
while(1){
p=head.top;
//数值在9以下,包括九,不用转换成字符
if(p->data >=0 && p->data <=9){
printf("%d",p->data);
}
else if(p->data >= 10 && p->data <= 15){ //数值在十以上,十五以下,包括十和十五,要转换成对应的字符
switch(p->data){
case 10:
printf(" a ");
break;
case 11:
printf(" b ");
break;
case 12:
printf(" c ");
break;
case 13:
printf(" d ");
break;
case 14:
printf(" e ");
break;
case 15:
printf(" f ");
break;
}
}
head.top=p->next;
delete p;
}
}
//转十六
void ChangeSixteen(){
linkstack head;
CreateLink(head);
int sixteenNum;
printf("请输入十进制数:");
printf("(温馨提示:该十六进制功能大小)\n");
scanf("%d",&sixteenNum);
SIXTEEN(head,sixteenNum);
}
//------------------------------------------------------我是功能块分割线------------------------------------------------------//
//菜单
void menu(){
printf("\n~~~~~~1、十转二 2、十转八~~~~~~\n");
printf("\n~~~~~~3、十转十六 4、退出~~~~~~\n");
}
int main(){
int z=1;
int choose;
printf("请根据菜单选择功能,输入对应的序号!!!\n");
while(z){
menu();
printf("输入序号:");
scanf("%d",&choose);
switch(choose){
case 1:
ChangeTwo();break;
case 2:
ChangeEight();break;
case 3:
ChangeSixteen();break;
case 4:
exit(0);
default:
printf("输入错误!!");
}
}
}
对于进制转换,大家都没问题,就是不断的模进制,然后直到最后一个数小于进制数。但是,你的进制转换完了,他要从下往上读,即你最先模出来的数是各位上的数。
转头一想,这不就是栈吗!?!最先模出来的数压入栈底,然后释放的时候就成了最后一个数。
但是大小呢,你的顺序栈是有大小限制的,你要是整个超级大,这会浪费存储空间资源,你要是整小了,又不够用。
所以链栈这种无限大小的、你用多少就创建多少空间的栈就非常实用了。
但是,又有一个新的问题,上面提过,如果按照以前的链式结构来写,在你输出的时候会有问题。
首先你要获取你的链式末尾的值,充当第一个输出的头值,即先进后出、后进先出。
这个有办法解决。我们专门建立一个结构体指针,专门指向链式最末尾的值,这样我就不用再遍历到链栈的末尾,来获取第一个输出的值。
但是新的问题又来了。比如你的链栈是4321,你得到了1,并输出了1,2呢、3呢、4呢,你如何获取。
如果按照以前的链式结构来写,你的每个节点的指针域是指向下一个节点的,即你可以从4获取3的地址,葱获取2的地址,以此类推。可你没法反向获取地址。
不知道看过我别的文章的读者是否还记得,我有篇文章讲过,链的头结点-尾插法,对应我们是不是还有头结点-头插法?
这个是可以有的。我们建立一个结点a1,并用一个空闲的该结构体类型的指针 p 绑定他的地址,当来了个新的结点a2,我们用p的指针域获取a2的地址,然后再将p绑定a1,这样就完成了。而p就可以是那个一直指向顶层结点的结构体指针。
是不是很简单,但是有些绕呢??
慢慢理解来着吧!!!