栈和队列
1.栈
栈是一种只能在一段进行插入或者删除操作的线性表。
允许进行操作的的一端称为栈顶(Top)。表的另一端称为栈顶,栈顶是固定不变的。
栈的特点:后进先出
比如我们在往瓶子里装沙子时,最后被装进去的沙子会被最先倒出来。
栈可以分为顺序栈和链式栈
创建一个栈
定义栈中的节点,拥有一个数据域与一个指针域。
typedef struct Node
{
int data ;
struct Node * next;
}NODE,*PNODE;
typedef struct Stack
{
PNODE top;
//top 指向栈顶元素
PNODE bottom ;
// bottom 指向栈尾元素 的下一个没有实际意义的元素
}STACK, *PSTACK;
初始化栈
主函数中新建一个 STACK 变量并不能称为建立一个栈,此时里面存放的是垃圾值,需要对其进行初始化。
// PSTACK 等价于struct Stack *
//形参接收 的是 * 类型 所以实参传值时需要 添加&
void init(PSTACK ps)
{
ps->top = (PNODE)malloc(sizeof(NODE));
if(ps->top==NULL)
{
printf("fail");
exit(-1);
}
else
{
ps->bottom = ps->top;
ps->top->next =NULL;
}
}
int main()
{
STACK S ; //stack 等价于 struct stack
return 0;
}
压入元素 (入栈)
void push(PSTACK ps,int val)
{
PNODE pnew =(PNODE)malloc(sizeof(NODE));
pnew->data=val;
pnew->next = ps->top ;
//将 指向栈顶的指针 赋值给 最新的元素 的指针域
ps->top = pnew ;
// 将新建立的 元素 赋值给 栈顶 做为新的栈顶
}
输出栈内所有元素
void traverse(PSTACK ps)
{
PNODE p = ps->top ;
while(p!=ps->bottom)
{
//当栈顶指针还没有指向栈尾指针时 继续往下遍历输出
printf("%3d",p->data);
p=p->next;
}
}
弹出元素(出栈)
在进行出栈操作前,要判断栈是否为空
int empty(PSTACK ps)
{
if(ps->top==ps->bottom)
{
return 0;
// 栈为空 返回0
}
else
{
return 1;
// 栈不为空 返回 1
}
}
// 出栈操作 把出栈的元素保存到val 形参所指向的变量
void pop(PSTACK ps,int *val )
{
int i = empty(ps);
// ps 保存的是形参的地址
// 所以再往其他函数传值时使用 ps 而不是 &ps
//printf("%d\n",i);
if(i==0) //ps 存放的就是 S 的地址 如果
{
printf("\n栈为空 出栈失败\n");
}
else
{
PNODE r = ps->top;
//建立指针 指向栈顶
*val = r->data ;
// 保存 出栈元素的值
ps->top = r->next;
// 将所出栈元素的指针域 赋值给 top 当作新的栈顶
free(r);
r=NULL ;
//释放空间
traverse(ps);
printf("出栈成功,出栈元素是:%d\n",*val);
}
}
清除栈
void clear(PSTACK ps)
{
//不可以直接改变指针的指向 而应该一个一个释放空间到最后一个指针
int i=empty(ps);
if(i==1)
{
PNODE p,q;
p=ps->top;
q=NULL;
while(p!=ps->bottom)
{
q=p->next; //将p 的指针域赋值给 q
free(p);
//p=q->next;
p=q; // 将q 赋值给 新的 p 再进行下一步的判断
}
ps->top=ps->bottom ;
printf("\清空完毕\n");
}
else
{
printf("\n栈为空,无需清空\n");
}
}
完整程序在最后
2.队列
队列是一种操作受限的线性表,仅允许在表的一端进行插入,在表的一端进行删除。
可进行插入的一端称为队尾(Rear)
可进行删除的一端称为对头(Front)
向队列中插入元素叫做进队,新元素进队之后就是新的队尾元素。
从队列中删除元素叫做出队,元素出队之后,它的后继元素就是新的队头元素。
队列的特点:先进先出
比如我们在排队的时候,排在最前面的最先完成排队,完成排队之后离开,即删除元素。排在最后面的最后完成排队,而进行插队只可以排到最后,也就是插入元素。
队列可以分为顺序队列和链式队
下面介绍静态队列
静态队列必须是循环队列。在使用数组时,每次删除、插入元素之后,空闲的空间将不能在使用,而定义为循环队列之后,可以通过类似于首尾相接的方式来最大化利用所有的空间。
创建结构体数组
typedef struct Queue
{
int * pBase; //
int front; // 指向队头元素
int rear; // 指向队尾元素的下一个元素
}QUEUE ;
//队列初始化
// font 和rear 的值都是零
//队列非空
// font代表的是队头元素
// rear代表的是队列的最后一个有效元素的下一个元素
//队列空
// font和rear 的值相等,但不一定是0
初始化队列
void init(QUEUE *pQ)
{
pQ->pBase=(int *)malloc(sizeof(int)*6);
pQ->front=0;
pQ->rear=0;
}
判断队列是否已满
int full(QUEUE *pQ)
{
if((pQ->rear+1)%6==pQ->front)
{
return 1;
}
else
{
return 0;
printf("队列已经满 \n");
}
}
判断队列是否为空
int empty(QUEUE *pQ)
{
if(pQ->front==pQ->rear)
{
return 1;
}
else
{
return 0;
}
}
入队
// 更新位置时 使用 (rear+1)%数组长度 是为了能够将所有的元素位置循环起来
// 如果数组长度是 6 0 位置的下一个位置是 1
// 那就是 0+1 % 6 %为取余操作 结果为1 也就完成了循环的操作
void insert(QUEUE *pQ,int val)
{
if(full(pQ)==1)
{
printf("队列已满\n");
}
else
{
pQ->pBase[pQ->rear] =val;
// 将新的值 赋给 数组中的最后一个元素的下一个元素
pQ->rear=(pQ->rear+1)%6;
// 更新 rear 指向的位置
}
}
出队
// 出队操作
int out(QUEUE *pQ,int *pval)
{
if(empty(pQ)==0)
{
*pval = pQ->pBase[pQ->front] ;
pQ->front =(pQ->front+1)%6 ;
printf("出队成功\n");
traverse(pQ);
return 1;
}
else
{
return 0;
printf("出队失败\n");
}
}
输出所有元素
void traverse(QUEUE *pQ)
{
int i = pQ->front ;
// 定义开始位置
while(i!=pQ->rear)
{
printf("%d\n",pQ->pBase[i]);
i=(i+1)%6 ;
}
}
栈操作的完整程序:
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include<time.h>
typedef struct Node
{
int data ;
struct Node * next;
}NODE,*PNODE;
typedef struct Stack
{
PNODE top;
//top 指向栈顶元素
PNODE bottom ;
// bottom 指向栈元素 的下一个没有实际意义的元素
}STACK, *PSTACK;
// PSTACK 等价于struct Stack *
//形参接收 的是 * 类型 所以实参传值时需要 添加&
void init(PSTACK ps)
{
ps->top = (PNODE)malloc(sizeof(NODE));
if(ps->top==NULL)
{
printf("fail");
exit(-1);
}
else
{
ps->bottom = ps->top;
ps->top->next =NULL;
}
}
void push(PSTACK ps,int val)
{
PNODE pnew =(PNODE)malloc(sizeof(NODE));
pnew->data=val;
pnew->next = ps->top ;
//将 指向栈顶的指针 赋值给 最新的元素 的指针域
ps->top = pnew ;
// 将新建立的 元素 赋值给 栈顶 做为新的 栈顶
}
void traverse(PSTACK ps)
{
PNODE p = ps->top ;
while(p!=ps->bottom)
{
//当栈顶指针还没有指向栈尾指针时 继续往下遍历输出
printf("%3d",p->data);
p=p->next;
}
}
int empty(PSTACK ps)
{
if(ps->top==ps->bottom)
{
return 0;
// 栈为空 返回0
}
else
{
return 1;
// 栈不为空 返回 1
}
}
// 出栈操作 把出栈的元素保存到val 形参所指向的变量
void pop(PSTACK ps,int *val )
{
int i = empty(ps);
// ps 保存的是形参的地址
// 所以再往其他函数传值时使用 ps 而不是 &ps
//printf("%d\n",i);
if(i==0) //ps 存放的就是 S 的地址 如果
{
printf("\n栈为空 出栈失败\n");
}
else
{
PNODE r = ps->top;
//建立指针 指向栈顶
*val = r->data ;
// 保存 出栈元素的值
ps->top = r->next;
// 将所出栈元素的指针域 赋值给 top 当作新的栈顶
free(r);
r=NULL ;
//释放空间
traverse(ps);
printf("出栈成功,出栈元素是:%d\n",*val);
}
}
void clear(PSTACK ps)
{
//不可以直接改变指针的指向 而应该一个一个释放空间到最后一个指针
int i=empty(ps);
if(i==1)
{
PNODE p,q;
p=ps->top;
q=NULL;
while(p!=ps->bottom)
{
q=p->next; //将p 的指针域赋值给 q
free(p);
//p=q->next;
p=q; // 将q 赋值给 新的 p 再进行下一步的判断
}
ps->top=ps->bottom ;
printf("\清空完毕\n");
}
else
{
printf("\n栈为空,无需清空\n");
}
}
int main()
{
STACK S ; //stack 等价于 struct stack
int val ; //保存出栈元素
init(&S); //造出一个空栈
push(&S,1); // 压栈
push(&S,2);
push(&S,3);
push(&S,4);
traverse(&S); //输出
printf("出栈\n");
pop(&S,&val);//出栈
clear(&S); //清空
pop(&S,&val);//出栈
return 0;
}
输出所有元素
#include <stdio.h>
#include <malloc.h>
typedef struct Queue
{
int * pBase ;
int front;
int rear;
//队列初始化
// font 和rear 的值都是零
//队列非空
// font代表的是队头元素
// rear代表的是队列的最后一个有效元素的下一个元素
//队列空
// font和rear 的值相等,但不一定是0
}QUEUE;
void init(QUEUE *pQ)
{
pQ->pBase=(int *)malloc(sizeof(int)*6);
pQ->front=0;
pQ->rear=0;
}
int full(QUEUE *pQ)
{
if((pQ->rear+1)%6==pQ->front)
{
return 1;
}
else
{
return 0;
printf("队列已经满 \n");
}
}
// 更新位置时 使用 (rear+1)%数组长度 是为了能够将所有的元素位置循环起来
// 如果数组长度是 6 0 位置的下一个位置是 1
// 那就是 0+1 % 6 %为取余操作 结果为1 也就完成了循环的操作
void insert(QUEUE *pQ,int val)
{
if(full(pQ)==1)
{
printf("队列已满\n");
}
else
{
pQ->pBase[pQ->rear] =val;
// 将新的值 赋给 数组中的最后一个元素的下一个元素
pQ->rear=(pQ->rear+1)%6;
// 更新 rear 指向的位置
}
}
void traverse(QUEUE *pQ)
{
int i = pQ->front ;
while(i!=pQ->rear)
{
printf("%d\n",pQ->pBase[i]);
i=(i+1)%6 ;
}
}
int empty(QUEUE *pQ)
{
if(pQ->front==pQ->rear)
{
return 1;
}
else
{
return 0;
}
}
// 出队操作
int out(QUEUE *pQ,int *pval)
{
if(empty(pQ)==0)
{
*pval = pQ->pBase[pQ->front] ;
pQ->front =(pQ->front+1)%6 ;
printf("出队成功\n");
traverse(pQ);
return 1;
}
else
{
return 0;
printf("出队失败\n");
}
}
int main()
{
QUEUE Q;
init(&Q);
insert(&Q,1);
insert(&Q,2);
insert(&Q,3);
insert(&Q,4);
insert(&Q,5);
full(&Q);
traverse(&Q);
int val= 4;
int i = out(&Q,&val);
printf("\n%d\n",val);
return 0;
}