栈
栈的基本概念
栈是只允许在一端进行插入或删除操作的线性表,是一种限制存取点的线性表。
栈顶top:线性表允许插入删除的一端
栈底bottom:固定的,不允许插入删除
栈的操作特性:先进后出
栈的数学特性:n个不同元素进栈,出栈元素不同排列的个数为(1/n+1)C(2n)n
栈的基本操作
InitStack(&S) 初始化一个空栈
StackEmpty(S) 判断栈是否为空
Push(&S,x) 进栈,栈未满时,加入x成为新栈顶
Pop(&S,x) 出栈,栈非空时,弹出栈顶元素,用x返回
GetTop(S,&x) 读栈顶元素,栈非空时,用x返回栈顶元素(栈不变)
DestoryStack(&S) 销毁栈,释放栈S的存储空间
栈的顺序存储结构
顺序栈
一组地址连续的存储单元存放自栈底到栈顶的数据元素,并附设一个指针top指示当前栈顶元素的位置
栈的顺序存储类型描述
#define MaxSize 50
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];//存放栈元素
int top; //栈顶指针
}SqStack;
栈顶指针初始化:S.top = -1
栈顶元素:S.data[S.top]
进栈:栈不满时,栈顶指针先加1,再送值到栈顶元素
出栈:栈非空时,先取栈顶元素值,再将栈顶指针减1
栈空条件:S.top == -1 栈满条件:S.top= = MaxSize-1 栈长:S.top+1
顺序栈的基本运算
初始化一个空栈
//InitStack(&S)
void InitStack(SqStack &S)
{
S.top = -1;
}
判断栈是否为空
//StackEmpty(S)
bool StackEmpty(SqStack S)
{
if (S.top == -1)
return true;
return false;
}
进栈
//Push(&S, x) 进栈,栈未满时,加入x成为新栈顶
bool Push(SqStack &S, ElemType x)
{
if (S.top == MaxSize - 1)
return false;
S.data[++S.top] = x;
return true;
}
出栈
//Pop(&S, x) 出栈,栈非空时,弹出栈顶元素,用x返回
bool Pop(SqStack& S, ElemType x)
{
if (S.top == - 1)
return false;
x = S.data[S.top--];
return true;
}
读栈顶元素
//GetTop(S, &x) 读栈顶元素,栈非空时,用x返回栈顶元素(栈不变)
bool GetTop(SqStack S, ElemType& x)
{
if (S.top == -1)
return false;
x = S.data[S.top];
return true;
}
共享栈
利用栈底位置相对不变的特性,让两个顺序栈共享一个一维数组空间,栈底分别设置在共享空间的两端。
top0 = -1时,0号栈为空,top1 = MaxSize时,1号栈为空
当两个栈顶指针相邻即top1-top0 = 1时,栈满
共享栈的优点:更有效利用存储空间,存取数据的时间复杂度为O(1)
栈的链式存储结构
通常采用单链表实现,很少用也少考
链栈没有头结点,Lhead指向栈顶元素,所有操作在表头进行
优点: 1.便于多个栈共享存储空间和提高效率 2.不存在栈满上溢的情况
栈的链式存储类型描述
typedef struct
{
ElemType data;
struct SNode* next;
}*LiStack;
链栈的操作与链表相似
队列
队列的基本概念
队列是只允许在一端进行插入,另一端删除操作的线性表,也是一种限制存取点的线性表。操作特性是先进先出(FIFO)
队头:允许删除的一端,也称队首
队尾:允许插入的一端
队列的基本操作
InitQueue(&Q) 初始化队列,构造一个空队列
QueueEmpty(Q) 判断队列是否为空
EnQueue(&Q,x) 入队,队列未满时,将x加入到队尾
DeQueue(&Q,&x) 出队,队列非空时,删除队头元素,用x返回
GetHead(Q,&x) 读队头元素,队列非空时,将队头元素赋给x
队列的顺序存储结构
顺序队列
队列的顺序实现分配了一块连续的存储单元,并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置。
队列的顺序存储类型描述
#define MaxSize 50
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int front, rear;
}SqQueue;
初始状态:Q.front == Q.rear == 0(队空)
进队:队不满时,先送值到队尾元素,队尾指针+1
出队:队不空时,先取队头元素值,队头指针+1
假溢出:设队头指针front指向队头元素的前一位置,rear指向队尾元素。当front等于-1时队空,rear等于m-1时为队满。由于“删除”在队头而“插入”在队尾,所以当队尾指针rear等于m-1时,若front不等于-1,则队列中仍有空闲单元,所以队列并不是真满。这时若再有入队操作,会造成假“溢出”。
不能用Q.rear == MaxSize作为队满条件
循环队列
把存储队列元素的表在逻辑上视为一个环。
当队首指针Q.front = MaxSize -1后,再前进一个位置就自动到0,利用取余(%)运算实现
队空:Q.front == Q.rear == 0
队首指针进1:Q.front = (Q.front +1)%MaxSize
队尾指针进1:Q.rear = (Q.rear +1)%MaxSize
队列长度:(Maxsize + Q.rear - Q.front)%MaxSize
指针都按顺时针方向进1
队满时有Q.front = Q.rear
为了区分队空和队满,有三种处理方式:
1、牺牲一个单元(最常用)
以队头指针在队尾指针的下一位置作为队满标志
队满条件:(Q.rear +1)%MaxSize == Q.front
2、增设表示元素个数的数据成员size
队空条件:Q.size = 0
队满条件:Q.size = MaxSize
3、增设tag数据成员
tag=0时,若因删除导致Q.front = Q.rear,则为队空
tag=1时,若因插入导致Q.front = Q.rear,则为队满
循环队列的操作
初始化队列
void InitQueue(SqQueue& Q)
{
Q.rear = Q.front = 0;
}
判队空
bool QueueEmpty(SqQueue Q)
{
if (Q.rear == Q.front)
return true;
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;
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;
return true;
}