1.3栈
1.概念:
只能在一端进行插入和删除操作的线性表,进行插入和删除的一端叫做栈顶,另一端叫做栈底
- 特点:
先入后出,后入先出
FILO,LIFO
1.3.1顺序栈:seqstack
sequence 顺序 stack 栈
1)逻辑结构:线性结构
2)存储结构:顺序存储
3)操作:
c
#ifndef _SEQSTACK_H_
#define _SEQSTACK_H_
typedef struct seqstack
{
int* data;//指向栈的存储位置
int maxlen;//保存栈的最大长度
int top;//称为栈针,用的时候,心里面可以将按照顺序表里的last来使用
//top 始终代表当前栈内最后一个有效元素的下标
}seqstack_t;
//1.创建一个空的栈
seqstack_t *CreateEpSeqStack(int len);//len代表的是创建栈的时候的最大长度
//2.判断是否为满,满返回1 未满返回0
int IsFullSeqStack(seqstack_t *p);
//3.入栈
int PushStack(seqstack_t *p, int data);//data代表入栈的数据
//4.判断栈是否为空
int IsEpSeqStack(seqstack_t *p);
//5.出栈
int PopSeqStack(seqstack_t *p);
//6. 清空栈
void ClearSeqStack(seqstack_t *p);
//7. 获取栈顶数据(注意不是出栈操作,如果出栈,相当于删除了栈顶数据,只是将栈顶的数据获取到,不需要移动栈针)
int GetTopSeqStack(seqstack_t *p);
//8. 求栈的长度
int LengthSeqStack(seqstack_t *p);
#endif
具体实现
c
#include"seqstack.h"
//1.创建空栈
seqstack_t *CreateEpSeqStack(int len)
{
seqstack_t *p=(seqstack_t *)malloc(sizeof(seqstack_t));
if(p==NULL)
{
perror("struct malloc err");
return NULL;
}
p->maxlen=len;
p->top=-1;
p->data=(int *)malloc(sizeof(int)*p->maxlen);
if(p->data==NULL)
{
perror("stack malloc err");
return NULL;
}
return p;
}
//2.判满
int IsFullSeqStack(seqstack_t *p)
{
return p->top+1 == p->maxlen;
}
//3.入栈
int PushStack(seqstack_t *p,int num)
{
if(IsFullSeqStack(p))
{
perror("stack full");
return -1;
}//1.容错判满
p->top++;//2.移动栈针
p->data[p->top]=num;//3.入栈
return 0;
}
//4.判空
int IsEqSeqStack(seqstack_t *p)
{
return p->top == -1;
}
//5.出栈
int PopStack(seqstack_t *p)
{
if(IsEqSeqStack(p))
{
perror("stack empty");
return -1;
}//容错判空
int data=p->data[p->top];
p->top--;
return data;
}
//6.清空栈
void ClearSeqStack(seqstack_t *p)
{
p->top=-1;
}
//7.获取栈顶元素
int GetTopSeqStack(seqstack_t *p)
{
if(IsEqSeqStack(p))
{
perror("stack empty");
return -1;
}
return p->data[p->top];
}
//8.求栈长度
int LengthSeqStack(seqstack_t *p)
{
return p->top+1;
}
1.3.2链式栈
1)逻辑结构:线性结构
2)存储结构:链式存储
c
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_
#include <stdio.h>
#include <stdlib.h>
//入栈和出栈只在第一个节点位置操作
typedef int datatype;
typedef struct linkstack
{
datatype data;//数据域
struct linkstack *next;//指针域
}linkstack_t;
//1.创建一个空的栈
void CreateEpLinkStack(linkstack_t **ptop);
//2.入栈 data是入栈的数据
参数上之所以采用二级指针,因为我们要随着入栈添加新的节点作为头,top需要永远指向当前链表的头,
那么修改main函数中的top,我们采用地址传递
int PushLinkStack(linkstack_t **ptop, datatype data);
//3.判断栈是否为空
int IsEpLinkStack(linkstack_t *top);
//4.出栈
datatype PopLinkStack(linkstack_t **ptop);
//5.清空栈
void ClearLinkStack(linkstack_t **ptop);//用二级指针,是因为清空后需要将main函数中的top变为NULL
//6.求栈的长度
int LengthLinkStack(linkstack_t *top);//用一级指针,是因为我只是求长度,不需要修改main函数中top指针的指向
//7.获取栈顶数据,不是出栈,不需要移动main函数中的top,所以用一级指针
datatype GetTopLinkStack(linkstack_t *top);
#endif
c
#include"linkstack.h"
//1.创建一个空的栈
void CreateEpLinkStack(linkstack_t **ptop)
{
*ptop=NULL;
}
//2.入栈 data是入栈的数据
// 参数上之所以采用二级指针,因为我们要随着入栈添加新的节点作为头,top需要永远指向当前链表的头,
// 那么修改main函数中的top,我们采用地址传递
int PushLinkStack(linkstack_t **ptop, datatype data)
{
//1.开辟节点
linkstack_t *pnew=(linkstack_t *)malloc(sizeof(linkstack_t));
if(pnew==NULL)
{
perror("pnew err");
return -1;
}
pnew->data=data;
pnew->next=*ptop;//2.新节点插入当前无头链表,作为头节点
*ptop=pnew;//3.移动栈针,栈针永远指向头节点
return 0;
}
//3.判断栈是否为空
int IsEpLinkStack(linkstack_t *top)
{
return top==NULL;
}
//4.出栈
datatype PopLinkStack(linkstack_t ** ptop)
{
if(IsEpLinkStack(*ptop))
{
perror("linkstack empty");
return -1;
}
//定义一个指针pdel用于删除节点
linkstack_t *pdel=*ptop;
datatype data=pdel->data;//临时变量存储被删除数据
*ptop=pdel->next;//跨过被删除节点
free(pdel);//释放被删除节点
pdel=NULL;
return data;
}
//5.清空栈
void ClearLinkStack(linkstack_t **ptop)//用二级指针,是因为清空后需要将main函数中的top变为NULL
{
linkstack_t *pdel;
while(*ptop!=NULL)
{
pdel=*ptop;
*ptop=pdel->next;
free(pdel);
pdel=NULL;
}
}
//6.求栈的长度
int LengthLinkStack(linkstack_t *top)
{
int sum=0;
while(top!=NULL)
{
sum++;
top=top->next;
}
return sum;
}//用一级指针,是因为我只是求长度,不需要修改main函数中top指针的指向
//7.获取栈顶数据,不是出栈,不需要移动main函数中的top,所以用一级指针
datatype GetTopLinkStack(linkstack_t *top)
{
if(IsEpLinkStack(top))
{
perror("stack empty");
return -1;
}
return top->data;
}
总结:
顺序栈和链式栈的区别?
1)存储结构不同,顺序栈相当于数组,连续;链式栈 链表 内存不连续
2)顺序栈长度受限制,链式栈不会
如何避免头文件重复包含
防止结构体等重新定义
c
#ifndef _xxx_H_
#define _xxx_H_
....
#endif
顺序表和链表的相同点和不同点有哪些?
相同点: 都是线性表 逻辑结构:线性结构 一对一
不同点:
(1)顺序表存储结构是顺序存储,内存当中存储不连续的链表是链式存储,通过指针将节点联系到一起,内存上存储不连续
(2)顺序表(数组)长度固定,链表不固定
(3)顺序表查找方便,但是插入和删除麻烦,链表插入和删除方便,但是查找麻烦
线性表的特征?
线性表: 顺序表 链表 栈(顺序栈和链式栈) 队列(顺序队列也叫循环队列和链式队列)
线性表的特征:一对一,每个节点最多有一个前驱和一个后继(首尾节点除外)
1.4队列(queue)
顺序队列(循环队列)和链式队列
- 概念:
只允许在两端进行插入和删除操作的线性表,在队尾插入,在队头删除 - 特点:
先入先出,后入后出
FIFO LILO
1.4.1顺序队列
1)逻辑结构:线性结构
2)存储结构:顺序存储
3)操作
1.4.1循环队列
c
#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef int datatype;
typedef struct
{
datatype data[N];//循环队列的数组
int rear;//存数据端 rear 后面
int front;//取数据端 front 前面
}sequeue_t;
//1.创建一个空的队列
sequeue_t *CreateEmptySequeue();
//2.入列 data代表入列的数据
int InSequeue(sequeue_t *p,datatype data);
//3.判断队列是否为满
int IsFullSequeue(sequeue_t *p);
//4.判断队列是否为空
int IsEmptySequeue(sequeue_t *p);
//5.出列
datatype OutSequeue(sequeue_t *p);
//6.求队列的长度
int LengthSequeue(sequeue_t *p);
//7.清空队列函数
void ClearSequeue(sequeue_t *p);
具体实现
c
#include"sequeue.h"
//1.创建一个空的队列
sequeue_t *CreateEmptySequeue()
{
sequeue_t *p=(sequeue_t *)malloc(sizeof(sequeue_t));
if(p==NULL)
{
perror("p error");
return NULL;
}
p->front=0;
p->rear=0;
return p;
}
//2.判断队列是否为满
int IsFullSequeue(sequeue_t *p)
{
return (p->rear+1)%N == p->front;
}
//3.入列 data代表入列的数据 浪费了一个存储空间
int InSequeue(sequeue_t *p,datatype data)
{
//1.判满
if(IsFullSequeue(p))
{
perror("queue full");
return -1;
}
//2.入队
p->data[p->rear]=data;
//3.尾指针后移
p->rear=(p->rear+1)%N;
return 0;
}
//4.判断队列是否为空
int IsEmptySequeue(sequeue_t *p)
{
return p->front == p->rear;
}
//5.出列
datatype OutSequeue(sequeue_t *p)
{//1.判空
if(IsEmptySequeue(p))
{
perror("queue empty");
return -1;
}
//2.取出数据
datatype data=p->data[p->front];
//3.移动对头
p->front=(p->front+1) % N;
return data;
}
//6.求队列的长度
int LengthSequeue(sequeue_t *p)
{
return (p->rear + N - p->front) % N;
}
//7.清空队列函数
void ClearSequeue(sequeue_t *p)
{
p->rear = p->front;
}
循环队列中,假设数组的元素个数为N,那么循环队列中存储最多的数据个数为N-1个
因为需要一个空间判断是否队列为满
1.4.2链式队列
c
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;//数据域
struct node *next;//指针域
}linkqueue_node_t,*linkqueue_list_t;
//linkqueue_list_t p === linkqueue_node_t *
typedef struct//将队列头指针和尾指针封装到一个结构体里
{
linkqueue_list_t front;//相当于队列的头指针
linkqueue_list_t rear;//相当于队列的尾指针
//有了链表的头指针和尾指针,那么我们就可以操作这个链表
}linkqueue_t;
//1.创建一个空的队列
linkqueue_t *CreateEmptyLinkQueue();
//2.入列 data代表入列的数据
int InLinkQueue(linkqueue_t *p,datatype data);
//3.出列
datatype OutLinkQueue(linkqueue_t *p);
//4.判断队列是否为空
int IsEmptyLinkQueue(linkqueue_t *p);
//5.求队列长度的函数
int LengthLinkQueue(linkqueue_t *p);
//6.清空队列
void ClearLinkQueue(linkqueue_t *p);
具体实现
c
#include "linkqueue.h"
//1.创建一个空的队列
linkqueue_t *CreateEmptyLinkQueue()
{
//1.开辟存放头尾指针的结构体大小空间
linkqueue_t *p=(linkqueue_t *)malloc(sizeof(linkqueue_t));
if(p==NULL)
{
perror("p err");
return NULL;
}//初始化头尾指针,指向头节点
p->rear=p->front=(linkqueue_list_t)malloc(sizeof(linkqueue_node_t));
if(p->rear==NULL)
{
perror("p->front malloc err");
return NULL;
}
//头节点初始化
p->front->next=NULL;
return p;
}
//2.入列 data代表入列的数据
int InLinkQueue(linkqueue_t *p,datatype data)
{
linkqueue_list_t pnew=(linkqueue_list_t)malloc(sizeof(linkqueue_node_t));
if(pnew==NULL)
{
perror("pnew err");
return -1;
}
//初始化节点
pnew->data=data;
pnew->next=NULL;
//插入队列 移动尾指针
p->rear->next=pnew;
p->rear=pnew;
return 0;
}
//3.判断队列是否为空
int IsEmptyLinkQueue(linkqueue_t *p)
{
return p->rear==p->front;
}
//4.出列
datatype OutLinkQueue(linkqueue_t *p)
{
if(IsEmptyLinkQueue(p))
{
perror("queue empty");
return -1;
}
linkqueue_list_t pdel;
pdel=p->front;
p->front=p->front->next;
free(pdel);
pdel=NULL;
return p->front->data;
}
//5.求队列长度的函数
int LengthLinkQueue(linkqueue_t *p)
{
int len=0;
linkqueue_list_t flag=p->front;
while(flag->next!=NULL)
{
flag=flag->next;
len++;
}
return len;
}
//6.清空队列
void ClearLinkQueue(linkqueue_t *p)
{
while(p->front->next!=NULL)
{
OutLinkQueue(p);
}
}