栈
特点:只能在一个端进行数据的插入和删除,先入栈的数据后出栈,后入栈的数据先出栈。
1.顺序栈
内存空间是连续开辟,长度固定
定义一个描述顺序栈的结构体:
typedef int datatype;
typedef struct
{
datatype *data;//当然这里直接写一个数组也是完全没问题的
int len;
int top;
}stack_t;
创建
//1.创建一个空的顺序栈
stack_t *createStack(int len)
{
//创建一个用于保存栈信息的结构体
stack_t *p=(stack_t *)malloc(sizeof(stack_t));
if(NULL == p)
{
printf("createStack error.\n");
return NULL;
}
//初始化结构体
//1.创建栈
p->data=(datatype *)malloc(sizeof(datatype)*len);
if(NULL == p->data)
{
printf("malloc stack error.\n");
return NULL;
}
p->len=len;
p->top=-1;//空栈
return p;
}
,入栈出栈操作
//2.判断栈是否满
int isFullStack(stack_t *p)
{
return p->len==p->top+1;//1:满 0:非满
}
//3.入栈
int inStack(stack_t *p,datatype data)
{
if(isFullStack (p))
{
printf("isFullStack error.\n");
return -1;
}
//栈针先移动
p->top++;
//数据入栈
p->data[p->top]=data;
return 0;
}
//4.判断栈是否为空
int isEmptyStack(stack_t *p)
{
return p->top == -1;
}
//5.出栈
datatype outStack(stack_t *p)
{
if(isEmptyStack(p))
{
printf("isEmptyStack error.\n");
return -1;
}
return p->data[p->top--];
//datatype data=p->data[p->top];
//p->top--;
//return data;
}
//6.计算栈的长度
int lengthStack(stack_t *p)
{
return p->top+1;
}
//7.获取栈顶数据
datatype topStack(stack_t *p)
{
if(isEmptyStack(p))
{
printf("isEmptyStack error.\n");
return -1;
}
return p->data[p->top];
}
//8.清空栈
void clearStack(stack_t *p)
{
p->top=-1;
}
//9.销毁栈
void destroyStack(stack_t **sp)
{
free((*sp)->data);
free(*sp);
*sp=NULL;
}
2.链式栈:无头单向链表
特点:内存空间不连续,通过地址连接到一起,长度不固定。
定义节点结构体:
typedef int datatype;
typedef struct node_t
{
datatype data;
struct node_t *next;
}lstack_t;
链栈
建立链栈要用头插的方式,因为单链表是单向的,如果尾插的话出栈完最后一个节点,没办法回去了。
另外,入栈出栈后栈针是需要移动的,想要改变值,可以传参传地址或者通过返回值。
//1.创建一个空的无头链式栈
void createLinkStack(lstack_t **ptop)
{
*ptop=NULL;
}
//2.入栈
int inLinkStack(lstack_t **ptop,datatype data)
{
//定义新节点保存入栈的数据
lstack_t *pnew=(lstack_t *)malloc(sizeof(lstack_t));
if(NULL == pnew)
{
printf("malloc new node error.\n");
return -1;
}
//初始化新建节点
pnew->data=data;
pnew->next=*ptop;
*ptop=pnew;
return 0;
}
//3.出栈
datatype outLinkStack(lstack_t **ptop)
{
datatype data;
lstack_t *pdel=*ptop;
if(isEmptyLinkStack(*ptop))
{
printf("isEmptyLinkStack error.\n");
return -1;
}
data=pdel->data;
*ptop=pdel->next;
free(pdel);
pdel=NULL;
return data;
}
//4.判断栈是否为空
int isEmptyLinkStack(const lstack_t *top)
{
return top == NULL;
}
//5.计算栈的长度
int lengthLinkStack(lstack_t *top)
{
int len=0;
while(top != NULL)
{
len++;
top=top->next;
}
return len;
}
//6.获取栈顶数据
datatype topLinkStack(lstack_t *top)
{
if(isEmptyLinkStack(top))
{
printf("isEmptyLinkStack error.\n");
return -1;
}
return top->data;
}
//7.清空栈
void clearLinkStack(lstack_t **ptop)
{
while(!isEmptyLinkStack(*ptop))
{
outLinkStack(ptop);
}
}
或者在主函数里利用返回值修改:
lstack_t *ptop = NULL;
ptop = push(lstack_t *p,datatype x);
ptop = pop(lstack_t *p);
队列
特点:只允许在两端进行插入和删除操作的线性表,
在队尾插入,在队头删除。 插入的一端被称为"队尾",删除的一端被称为"队头"
顺序队列
防止假溢出, 用数组实现队列, 调整“指针”代替队列元素的移动,用取余的方式保证一个循环队列。
特点:内存空间连续开辟,长度固定。
typedef int datatype;
typedef struct
{
datatype data[N];
int front;//队头,这里规定指向队头元素的下一个元素
int rear;//队尾,这里规定指向队尾的下一个元素
}seqeue_t;
//队空
p->rear == p->top
//队满,为了和队空条件区分开,这里浪费一个空间,不能再放数据
(p->rear+1)%N == p->front
//入队
p->data[p->rear]=x; p->rear = (p->rear+1)%N;
//出队
p->front = (p->front+1)%N
//队中元素个数,加N是为了防止rear小于front,相减是个负数,可以画图看看
(p->rear-p->front +N)%N
链队:
结构体:
typedef struct link
{
datatype data;
struct link *next;
}link_t;
//结构体空间保存信息,next,front,rear指向的类型相同,都是link_t *
typedef struct
{
int len;
link_t *front;
link_t *rear;
}queue_t;
创建一个空的链式队列
//1.创建一个空的链式队列
queue_t *createLinkQueue(void)
{
//1)开辟一个保存信息的结构体空间
queue_t *p=(queue_t *)malloc(sizeof(queue_t));
if( NULL == p)
{
printf("createLinkQueue error.\n");
return NULL;
}
//2)初始化这个结构体
//1-》创建一个无效头节点,出队的时候很好用
p->front=p->rear=(link_t *)malloc(sizeof(link_t));
if(p->front == NULL)
{
printf("malloc head node error.\n");
return NULL;
}
//2->初始化节点
p->front->next=NULL;
//初始化长度
p->len=0;
return p;
}
//入队
int inQueue(queue_t *p,datatype data)
{
//1.新建节点保存如队的数据
link_t *pnew=(link_t *)malloc(sizeof(link_t));
if(NULL == pnew)
{
printf("malloc new node error.\n");
return -1;
}
//2.初始化节点
pnew->data=data;
pnew->next=NULL;
//3.入队
p->rear->next=pnew;
p->rear=pnew;
p->len++;
return 0;
}
判空
int isEmptyQueue(queue_t *p)
{
return p->front==p->rear;
}
//3.出队
datatype outQueue(queue_t *p)
{
if(isEmptyQueue(p))
{
printf("isEmptyQueue error.\n");
return -1;
}
/*第一次出队的元素是头节点,而不是真正的那个节点,此时它变成了一个无效头节点,队空的时候还剩一个节点。那为什么不让头节点后的那个元素出队呢?这样front会永远指向头节点不动,当p->front->next==NULL的时候,还要让p->rear=p->front,要不然变成野指针了*/
p->rear=q->front的操作。
link_t *pdel=p->front;
p->front=pdel->next;
free(pdel);
pdel=NULL;
p->len--;
return p->front->data;
}
//其他
//5.计算队列的长度
int lenQueue(queue_t *p)
{
return p->len;
}
//6.清空队列
void clearQueue(queue_t *p)
{
while(!isEmptyQueue(p))
{
outQueue(p);
}
}
//7.销毁队列
void destroyQueue(queue_t **pp)
{
clearQueue(*pp);
free((*pp)->front);
free(*pp);
*pp=NULL;
}