一、栈
(一)特征
后进先出LIFO
栈的大小不可变
n个不同元素入栈,出栈元素不同排列个数为(1/(n-1))C(n,2n)
基本操作:
InitStack(&S):初始化一个空栈S。
StackEmpty(S):判断一个栈是否为空,若栈s为空则返回true,否则返回false。
Push(&S,x):入栈,若栈S未满,则将x加入使之成为新栈顶。
Pop(&S,&X):出栈,若栈S非空,则弹出栈顶元素,并用x返回。
GetTop(S,&x):读栈顶元素,但不出栈,若栈s非空,则用x返回栈顶元素。
DestroyStack(&S):销毁栈,并释放栈S占用的存储空间
(二)顺序栈
采用顺序存储的栈,一组地址连续的存储单元存放元素
以下操作时间复杂度均为O(1)
//1.定义
#define MaxSize 50 //定义栈中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //存放栈中元素
int top; //栈顶指针,指向当前栈顶元素
}SqStack;
//声明一个栈:SqStack S;
//栈顶元素:S.data[S.top]
//2.初始化
void InitStack(SqStack &S){
s.top=-1;
}
//3.判空
bool StackEmpty(SqStack S){
if (S.top == -1) //栈空
return true; else //不空
return false;
}
//4.进栈
bool Push(SqStack &S, ElemType x) {
if (S.top == MaxSize - 1) //栈满,报错
return false;
S.data[++S.top] = x; //指针先加1,再入栈
return true;
}
//5.出栈
bool Pop(SqStack &S, ElemType &x) {
if (S.top == -1) //栈空,报错
return false;
x = S.data[S.top--]; //先出栈,指针再减1
return true;
}
//6.获取栈顶元素
bool GetTop(SqStack S, ElemType &x) {
if (S.top == -1) //栈空,报错
return false;
x = S.data[S.top]; //x记录栈顶元素
return true;
}
//若top初始为0,即指向栈顶元素的下一位置,则入栈为S.data[S.top++] = x;出栈为x = S.data[--S.top];栈满条件为S.top == MaxSize;栈空条件为S.top == 0
共享栈:两个顺序栈共享一个一维数组空间,两个栈底分别设置在共享空间两端,两个栈顶向共享空间的中间延伸
存取数据时间复杂度O(1)
#define MaxSize 50 //定义栈中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //存放栈中元素
int top0; //0号栈栈顶指针
int top1; //1号栈栈顶指针
}ShStack;
//初始化
void InitStack(ShStack &S){
s.top0=-1;//0判空
s.top1=MAxSize;//1判空
}
//栈满条件:top0+1=top1
//0号入栈:S.data[++S.top0] = x
//1号入栈:S.data[--S.top1] = x
//0号出栈:S.data[S.top0--] = x
//0号入栈:S.data[S.top1++] = x//////////??
(三)链栈
链式存储,便于多个栈共享存储空间,效率高,不存在栈满上溢情况。用单链表,规定只能在表头进行操作。
1.带头结点
2.没有头结点,Lhead指向栈顶元素(推荐)
ypedef struct Linknode{
ElemType data; //数据域
struct Linknode *next; //指针域
}*LiStack;
//初始化 进栈 出栈 获取栈顶元素 判空 判满。。??写一遍
(四)栈的应用
1.括号匹配
2.表达式(这里的机算代码没写*****//////////)
机算:
中缀表达式必须有括号 a+b
后缀表达式(逆波兰式)无括号 ab+
前缀表达式(波兰式)+ab
(1) 中缀表达式转后缀
左优先原则
栈的深度=栈中元素个数
(2)后缀转中缀
(3)计算后缀表达式
①从左往右扫描下一个元素,直到处理完所有元素
②若扫描到操作数则压入栈,并回到①,否则执行③
③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶
先出栈的是右操作数
(4)中缀转前缀
①确定中缀表达式中各个运算符的运算顺序
②选择下一个运算符,按照「运算符 左操作数 右操作数」的方式组合成一个新的操作数
③如果还有运算符没被处理,就继续②
右优先原则
3.递归
(5)计算前缀表达式
①从右往左扫描下一个元素,直到处理完所有元素
②若扫描到操作数则压入栈,并回到①;否则执行③
③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①
先出栈的是左操作数
int Fib(n){//斐波那契数列实现
if(n==0)
return 0;//边界条件
else if(n==1)
return 1;//边界条件
else
return Fib(n-1)+Fib(n-2);//递归表达式
}
二、队列
(一)特征
在队头删除,在队尾插入。操作受限的线性表,先进先出FIFO
基本操作:
InitQueue(&Q):初始化队列,构造一个空队列Q。
QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false。
EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。
DeQueue(&Q,&x):出队,若队列Q非空,删除队首元素,并用x返回。
GetHead(Q,&x):读队首元素,若队列Q非空,则将队首元素赋值给x。
(二)顺序队列
顺序存储,用数组
循环队列:
//定义
#define MaxSize 50 //定义队列中元素的最大个数
typedef struct {
ElemType data[MaxSize]; //存放队列元素
int front, rear; //队头指针指向队首元素,队尾指针指向队尾元素的下一个位置
} SqQueue;
//初始化*循环队列*
void InitQueue(SqQueue &Q) {
Q.rear = Q.front = 0; //初始化队首、队尾指针
}
//声明
void tstQueue() {
SqQueue Q;
InitQueue(Q);
}
//判空
bool isEmpty(SqQueue Q) {
if(Q.rear==Q.front)
return true; //队空条件
else return false;
}
//入队
bool EnQueue(SqQueue &Q, ElemType x) {
if ((Q.rear + 1) % MaxSize == Q.front)
return false;//队满报错*
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize; //队尾指针加1取模
return true;
}
//出队
bool DeQueue(SqQueue &Q, ElemType &x) {
if (Q.rear == Q.front)
return false; //队空报错
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize; //队头指针加1取模
return true;
}
//获取队头元素
bool DeQueue(SqQueue Q, ElemType &x) {
if (Q.rear == Q.front)
return false; //队空报错
x = Q.data[Q.front];
return true;
}
//求队列长度
(Q.rear+MaxSize-Q.front)%MaxSize
//以上判断队空满的方式牺牲了1个存储空间。
//法2:设结构体size=0数据成员表示元素个数,删除成功size-1,插入成功size+1.则队空为Q.size==0;队满为Q.size==MaxSize,两种情况都有Q.front==Q.rear
//法3:设tag成员,删除成功置tag=0,若导致Q.front==Q.rear则为队空;插入成功置tag=1,若导致Q.front==Q.rear则为队满
若rear指向队尾元素
//初始化
Q.rear = MaxSize-1;
Q.front = 0;
//入队
Q.rear = (Q.rear + 1) % MaxSize;
Q.data[Q.rear] = x;
//判空:(Q.rear+1)%MaxSize==Q.front
//判满:牺牲头指针前面的元素位置,不可以存放,(Q.rear+2)%MaxSize==Q.front
//用size和tag也行
(三)链队列
链式存储,单链表
1.带头结点
队首指针指向队头结点,队尾指针指向队尾结点,即单链表最后一个结点
//定义
typedef struct{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct {
LinkNode *front, *rear; //队列的对头和队尾指针
} LinkQueue;
//初始化
void InitQueue(LinkQueue &Q) {
Q.front = Q.rear = (LinkNode *)malloc(sizeof( LinkNode));//建立头结点,front和rear都指向头结点
Q.front->next = NULL; //初始为空
}
void testLinkQueue() {
LinkQueue Q;
InitQueue(Q);
}
//判空
bool IsEmpty(LinkQueue Q) {
if(Q.front==Q.rear)
return true;
else
return false;
}//或者如果Q.front->next==NULL,则队空
//入队
void EnQueue(LinkQueue &Q, ElemType x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL; //创建新结点,插入到链尾
Q.rear->next = s;//新结点查到rear之后
Q.rear = s; //修改表尾指针
}
//出队
bool DeQueue(LinkQueue &Q, ElemType &x) {
if (Q.front == Q.rear)
return false;//空队
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front; //若原队列中只有一个结点,删除后 变空
free(p);
return true;
}
//队满:一般不会满
2.不带头结点
//初始化
void InitQueue(LinkQueue &Q) {
Q.front = NULL;
Q.rear= NULL;
}
//判空
bool IsEmpty(LinkQueue Q) {
if(Q.front==NULL)
return true;
else
return false;
}//或者如果Q.rear==NULL,则队空
//入队,第一个元素入队要有特殊处理
void EnQueue(LinkQueue &Q, ElemType x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL; //创建新结点,插入到链尾
if(Q.front == NULL) {
Q.front = s;
Q.rear = s;
} else {
Q.rear->next = s;//新结点查到rear之后
Q.rear = s; //修改表尾指针
}
}
//出队
bool DeQueue(LinkQueue &Q, ElemType &x) {
if (Q.front == NULL)
return false;//空队
LinkNode *p = Q.front;//p指向此次出队的结点
x = p->data;
Q.front = p->next;
if (Q.rear == p)//若是最后一个结点出队
Q.front == NULL;
Q.rear == NULL;
free(p);
return true;
}
//队满:一般不会满
(三)双端队列
两端插入,两端删除,左前右后
输出受限:一端插入和删除,另一端插入
输入受限:一端插入和删除,另一端删除
(四)队列的应用
1.二叉树层次遍历
2.图的广度优先遍历
3.计算机系统
- 解决主机与外部设备之间速度不匹配的问题(缓冲区)
- 解决由多用户引起的资源竞争问题
三、数组和特殊矩阵
(一)一维数组
以一维数组A[0.…n-1]为例,其存储结构关系式为LOC(ai)=LOC(a0)+i×L(0≤i<n),其中,L是每个数组元素所占的存储单元
(二)二维数组
1.行优先
先行后列,先存储行号较小的元素,行号相等先存储列号较小的元素。设二维数组的行下标与列下标的范围分别为[0,h₁]与[0,h₂],则存储结构关系式为LOC(ai,j)=LOC(a0,0)+[i×(h2+1)+j]×L
2.列优先
LOC(ai,j)=LOC(a0,0)+[jx(h1+1)+i]xL
(三)矩阵的存储
1.普通矩阵
- 用二维数组,矩阵元素行列从a1,1开始,数组一般从0开始
- 压缩存储:为值相同的矩阵元素只分配一个空间,对零元不分配存储空间
2.对称矩阵
aij=aji
只存上三角,或者只存下三角(含主对角线)
(1)行优先,存主对角线+下三角区
n阶对称矩阵存放在一维数组B[n(n+1)/2]中,ai,j存放到B[k]中(数组下标从0开始)
若数组下标从1开始,则上图不用-1
(2)列优先,存主对角线+下三角区
n阶对称矩阵存放在一维数组B[n(n+1)/2]中,ai,j存放到B[k]中(数组下标从0开始)
(3)行优先,存主对角线+上三角区
(2)列优先,存主对角线+上三角区
3.三角矩阵
(1)上三角矩阵,行优先,数组下标从0开始
i>j时,aij = 常量c
(2)下三角矩阵,行优先,数组下标从0开始
i<j时,aij = 常量c
3.三对角矩阵
任意一个元素 aij,当|i-j|>1时,若有aij=0(1<i,j<n),则称为三对角矩阵.在三对角矩阵中,所有非零元素都集中在以主对角线为中心的3条对角线的区域,其他区域的元素都为零。
压缩存储:将3条对角线上的元素按行优先方式存放在一维数组B中,a11放在B[0]中,共存放3n-2个元素,则为B[0]~B[3n-3]
4.稀疏矩阵(零元多,分布无规则)
压缩存储:保存三元组表、行数、列数、非零元素个数
(1)顺序存储:用数组,三元组线性表(非零元素的行i、列j、值aij),稀疏矩阵压缩存储后失去了随机存取特性。
(2)十字链表存储(见6.2节)