一、链式表:list
元素(结点)的数据项:
数据域:可以是任何类型的若干个数据项
指针域:指向下一个结点
由若干个结点通过指针域连接到一起就形成了链表
不带头结点的单链表:
第一个结点的数据域存储有效数据
缺点:添加、删除结点时,可能修改指向第一个结点的指针,参数需要使用二级指针,才能更改指针的指向,比较麻烦
带头结点的单链表:
第一个结点的数据域不存储有效元素,仅仅只是使用它的指针域永远指向链表的第一个数据有效的结点
优点:添加、删除时会比不带头结点的链表更方便
注意:其他操作要从头结点的下一个结点开始
二、功能受限的表结构
对表结构的部分功能加以限制,形成特殊的表结构
栈:
只有一个进出口的表结构,先进后出,FILO
顺序栈:
数据域:
存储元素的内存首地址
栈的容量
栈顶的位置
运算:
创建、销毁、入栈、出栈、栈顶、栈满、栈空
栈的应用:
1、函数的调用、栈内存特点
2、生产者和消费者模型(仓库->栈)
3、表达式解析 3-3*20/2
栈的常考笔试面试题:
某个序列是一个栈的入栈顺序,判断哪个是正确的出栈顺序
1 2 3 4 5 入栈
1 2 3 4 5 yes
3 2 1 4 5 yes
3 1 2 5 4 no
有可能边入边出
问题:两个容量相同的顺序栈,如何能够让空间利用率最高?
让两个栈相对着入栈
实现一个函数,序列a为入栈顺序,判断序列b是否是序列a的出栈顺序?
bool is_pop_stack(int a[],int b[],int len)
{
ArrayStack* stack=create_array_stack(len);
for(int i=0,j=0;i<len;i++)
{
push_array_stack(stack,a[i]);
int val=0;
while(top_array_stack(stack,&val)&&val==b[j])
{
pop_array_stack(stack);
j++;
}
}
//判断栈是否为空
bool flag=empty_array_stack(stack);
destory_array_stack(stack);
return flag;
}
链式栈:
栈结构数据项:
栈顶结点
结点数量
运算:
创建、销毁、入栈、出栈、栈空、栈顶
问题:顺序栈和链式栈的区别
顺序结构:数据元素存储在连续的内存中,用数据元素的相对位置来表示关系
优点:支持随机访问、访问查找效率极高、适合用于查找数据频繁的结构
缺点:插入、删除时效率低不方便、内存空间利用率低、要求高
链式结构:数据元素存储在彼此相互独立的内存中,每个独立的元素也叫做结点,每个结点中增加一项数据项用于存储其他相关结点的地址,以此表示结点之间的关系
优点:插入、删除更方便、空间利用率极高、对内存要求不高
缺点:不支持随机访问、只能从头到尾逐个访问
队列:
有两个端口,一个端口只能入队,另一个只能出队
先进先出 FIFO
顺序队列:
数据项:
存储元素的内存首地址
容量
队头下标 //出队
队尾下标 //入队
运算:
创建、销毁、入队、出队、队空、队满、查队头、查队尾、数量
顺序队列的注意点:
由一维数组+队头下标front+队尾下标tail
入队tail++,出队front++,为了让队列能够反复使用,我们把队列想象成一个环,因此当front和tail加1后都需要用队列的容量求余再重新赋值
front=(front+1)%cal;
tail=(tail+1)%cal;
如何判断队空、队满:
额外多申请一个元素的内存
队空:front==tail
队满:(tail+1)%cal==front
代价是空了一个位置不能使用,计算数量时较为麻烦 计算数量:(tail-front+cal)%cal
另一种方式是在队列结构中增加一项数据项用于记录队列中元素个数,判断空或满直接判断该数据项即可,更方便
链式队列:
由链表结点组成的队列结构
数据项:
队头指针
队尾指针
结点数量
运算:
创建、销毁、入队、出队、队空、队头、队尾
队列的应用:
1、消息队列
2、树的层序遍历使用队列
3、图的广度优先遍历使用对列
4、封装线程池、数据池
使用两个顺序栈模拟一个队列
栈2必须空栈,栈1才能入
从栈1到栈2必须一个不留地入
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define TYPE int
/*使用两个栈模拟队列功能*/
typedef struct Queue
{
ArrayStack* s1;
ArrayStack* s2;
}Queue;
//创建队列
Queue* create_quque(size_t cal)
{
Queue* queue=malloc(sizeof(Queue));
queue->s1=create_array_stack(cal);
quque->s2=create_array_stack(cal);
return queue;
}
//队满
bool full_queue(Queue* queue)
{
return full_array_stack(queue->s1) && full_array_stack(queue->s2);
}
//队空
bool empty_queue(Queue* queue)
{
return empty_array_stack(queue->s1) && empty_array_stack(queue->s2);
}
//入队
bool push_queue(Queue* queue,TYPE val)
{
if(full_array_stack(queue->s1))
{
if(!empty_array_stack(queue->s2)) return false;
while(!empty_array_stack(queue->s1))
{
TYPE top=0;
top_array_stack(queue->s1,&top);
pop_array_stack(queue->s1);
push_array_stack(queue->s2,top);
}
}
return push_array_stack(queue->s1,val);
}
//出队
bool pop_queue(Queue* queue)
{
if(empty_array_stack(queue->s2))
{
if(empty_array_stack(queue->s1)) return false;
while(!empty_array_stack(queue->s1))
{
TYPE top=0;
top_array_stack(queue->s1,&top);
pop_array_stack(queue->s1);
push_array_stack(queue->s2,top);
}
}
return pop_array_stack(queue->s2);
}