栈定义:是限定仅在表尾进行插入或删除操作的线性表。表尾 称为栈顶,表头称为栈底,不含元素的空表称为空栈(记得以前学汇编语言的时候,经常讲压栈、出栈当是就不明白,现在算是明白了一点了,栈并不是能把所有不同的东西都压入的,我猜测可能有多种栈,按元素类型来划分,比如有函数栈,变量栈等)。
栈表示和实现:栈也是像线性表一样可以用数组或者链表来实现,大多采用链表。栈的操作就是在添加到链表尾和从链表尾取出。
C语言实现的栈结构
#include <stdio.h>
#include <stdlib.h>
typedef int NodeType; //重定义一下int这个数据类型,作为节点数据域类型
struct LinkList{
NodeType i; //数据域,这里可以使用泛型代替,这样就可以对泛型数据操作了
struct LinkList* pLast; //指向上一个节点的指针,因为我们是在表尾操作,所以存储前一个的指针;
};
struct Stack{
int size; //栈中元素个数
struct LinkList* bottomLink; //栈结构中的栈底元素
struct LinkList* topLink; //栈结构中的栈顶元素
};
void initStack(struct Stack *s) //初始化栈结构
{
struct LinkList l;
l.i=0;
l.pLast=NULL;
s->size=0;
s->bottomLink=&l;
s->topLink=s->bottomLink;
}
//压栈
void push(NodeType node,struct Stack *s)
{
struct LinkList *m=(struct LinkList *)malloc(sizeof(struct LinkList)); //首先把节点元素给穿件出来
m->i=node; //把节点元素中的数据域赋值
m->pLast=s->topLink; //把节点元素中指向上一个节点的指针改为栈顶指针
s->size++; //栈元素增加
s->topLink=m; //更新栈顶指针
}
void show(struct Stack *s)
{
struct LinkList* m=s->topLink; //先取到栈顶元素
while(m->pLast!=NULL) //判断是否到达栈底
{
printf("%d |",m->i); //输出单前节点的元素
m=m->pLast; //把m指向上一个节点
}
printf("\n");
}
int count(struct Stack *s)
{
return s->size; //统计元素个数,用size属性 也是可以的
}
//出栈
int pop(struct Stack *s) //对于栈来说,移除肯定是指移除栈底元素,不然就不是栈了
{
struct LinkList* m=s->topLink; //获取栈顶元素
s->topLink=m->pLast; //把上一个节点的指针赋给栈顶元素
s->size--; //更改栈中元素个数
free(m); //释放开始的栈顶元素
}
int main(void)
{
struct Stack s; //新建一个栈引用,还没有分配内存
initStack(&s);
int i=10;
for(i;i<20;i++){
push(i,&s);
}
pop(&s);
pop(&s);
show(&s);
printf("%d",s.size);
return -1;
}
结果:
栈结构的应用:进制之间的转换
#define BINARY 2 //二进制数
#define OCTONARY 8 //八进制
#define DECIMAL 10 //十进制
#define HEXAD 16//十六进制
void conversion(unsigned n,int d)
{
struct Stack s1; //新建一个栈引用,还没有分配内存
initStack(&s1);
int m=0;
switch(d)
{
case BINARY:
m=BINARY;
break;
case OCTONARY:
m=OCTONARY;
break;
case DECIMAL:
m=DECIMAL;
break;
case HEXAD:
m=HEXAD;
break;
default:
printf("你输入的进制不合法!");
exit(-1);
break;
}
while(n)
{
push(n%m,&s1);
n=n/m;
}
while(isEmpty(&s1))
{
int i=pop(&s1);
if(i<9){
printf("%d",i);
}else{
printf("%c",i+55); //应对十六进制的,大于9的数就使用字母表示
}
}
switch(d)
{
case BINARY:
printf(" b\n");
break;
case OCTONARY:
printf(" O\n");
break;
case DECIMAL:
printf(" \n");
break;
case HEXAD:
printf(" H\n");
break;
default:
printf("你输入的进制不合法!");
exit(-1);
break;
}
}
//用法,直接传入需要转换的数和要被转换的进制数就行,这个必须是十进制转换为其他进制
conversion(1348,BINARY);
递归:自身调用自身的函数,或者形成循环调用的函数
汉诺塔问题使用递归求解
#include <stdio.h>
long c=0;
void move(char x,int n,char z)
{
//第n个圆盘从塔座x搬到塔座z
c++;
}
void hannoi(int n,char x,char y,char z)
{
if(n==1){
move(x,1,z);
}else{
hannoi(n-1,x,z,y);
move(x,n,z);
hannoi(n-1,y,x,z);
}
}
int main(void)
{
int n=0;
printf("请输入圆盘数:");
scanf("%d",&n);
hannoi(n,'x','y','z');
printf("%ld",c);
return -1;
}
队列
#include <stdio.h>
typedef int QElemType;
struct Node{
QElemType e;
struct Node* pNext;
};
struct Queue{
int zise;
struct Node* front;
struct Node* rear;
};
void init(struct Queue *q)
{
struct Node n;
n.e=0;
n.pNext=NULL;
q->zise=0; //一定要记住初始化,不然这个size值就不是从0开始计数
q->front=&n;
q->rear=&n;
}
/*
在表尾添加,
*/
void push(QElemType e,struct Queue *q)
{
struct Node* n=(struct Node*)malloc(sizeof(struct Node)); //先生成一个节点
n->e=e; //把这个节点数据域赋值
n->pNext=NULL; //把这个节点指向下一个节点的值赋值为NULL
q->rear->pNext=n; //把尾节点的指向下一个节点的值赋值为新节点
q->rear=n; //把当前节点赋值为尾节点
q->zise++; //节点元素增加
}
void show(struct Queue *q)
{
struct Node* n=q->front;
while(n->pNext!=NULL)
{
n=n->pNext;
printf("%d ",n->e);
}
printf("\n");
}
/*
从表头弹出
*/
int pop(struct Queue *q)
{
struct Node* n=q->front; //获取表头元素
int i=n->e; //先把节点中的数据拿出来
if(n->pNext==NULL)
{
printf("队列中没有元素!");
exit(-1);
}
struct Node* m=n->pNext; //把表头之后的那个元素指针拿到
q->front=m;
free(n);//释放节点内存
q->zise--;
return i;
}
int main(void)
{
struct Queue q;
init(&q);
int i=90;
for(i;i<100;i++)
{
push(i,&q);
}
pop(&q);
pop(&q);
pop(&q);
show(&q);
printf("%d",q.zise);
return -1;
}
结果: