目录
一、栈和队列的思维导图
二、栈和队列的基本概念
1、栈的基本概念
2、队列的基本概念
三、栈的结构体定义和基本操作
1、顺序栈
2、链栈
四、队列的结构体定义和基本操作
1、顺序队
2、链队
一、栈和队列的思维导图
栈(Stack)是一个先进后出(First In Last out,FILO)的线性表,它要求只在一端进行删除和插入操作。
队列(queue)是一种先进先出(First In First Out,FIFO)的线性表,它只允许在一端进行插入操作,在另一端进行删除操作。
二、栈和队列的基本概念
1、栈的基本概念
(1)栈的定义
栈(Stack)是一种只能在一端进行插入或删除操作的线性表。其中允许进行插入和删除操作的一端称为栈顶(Top)。栈顶由一个称为栈顶指针的位置指示器(顺序栈→记录元素所在数组的位置、链栈→记录栈顶元素所在节点地址的指针)来指示,它是动态变化的。表的另一端称为栈底,栈底固定不变。栈的插入和删除操作称为入栈和出栈。
(2)栈的特点:先进后出(First In Last out,FILO)——类比手枪弹夹。
(3)栈的存储结构
栈依照存储结构分为:顺序栈和链式栈,分别用顺序表和带头节点的单链式表来存储。
(4)栈的数学性质
当n个元素以某种顺序进栈,并且在任意时刻出栈(FILO),则所获得的元素排列数目N满足:
2、队列的基本概念
(1)队列的定义
队列(queue)简称队,是一种先进先出(First In First Out,FIFO)的线性表,它只允许在一端进行插入操作,而在另一端进行删除操作。可以插入的一端称为队尾(Rear),另一端称为队头(Front)。向列表中插入新的元素称为进队,新元素进队之后就成为新的队尾元素;从队列中删除元素称为出队,元素出队后,其后记元素就成为新的队头元素。
(2)队列的特点:先进先出(First In First Out,FIFO)——类比火车开过隧道。
(3)队列的存储结构:队列依照存储结构分为:顺序队和链式队,分别用环形数组和单链表来存储。
三、栈的结构体定义和基本操作
1、顺序栈
说明:顺序栈 st 的两个特殊状态和两个操作
st.top == -1; //栈空状态
st.top == maxSize-1 //栈满状态
++(st.top);
st.data[st.top] = x; //进栈操作
x = st.data[st.top];
--(st.top); //出栈操作
(1)顺序栈的结构体定义:
typedef struct{
int data[maxSize]; //存放栈中元素,maxSize是已定义的常量
int top; //栈顶指针(顺序栈这里实际是一个指示器,存放栈顶元素所在数组位置的标号)
}SqStack; //顺序栈的类型定义
(2)顺序栈的初始化:初始化一个栈,只需要将栈顶指针设置为 -1
void initStack(SqStack &st){ //初始化栈
st.top = -1; //只需将栈顶指针设置为 -1
}
(3)判断栈空:栈 st 为空的时候返回1,否则返回 0
int isEmpty(SqStack st){
if(st.top == -1)
return 1;
else
return 0;
}
(4)进栈操作:栈满不能进栈,若栈未满,先移动栈顶指针,然后将进栈元素赋值进去
int push(SqStack &st,int x){
if(st.top == maxSize-1) //判断栈是否满
return 0;
++(st.top); //先移动指针,再进栈
st.data[st.top] = x;
return 1;
}
(5)出栈操作:栈空不能出栈,若栈未空,先取出元素,再移动栈顶指针
int pop(SqStack &st,int &x){
if(st.top == -1) //判断栈是否为空
return 0;
x = st.data[st.top]; //将栈顶元素取出
--(st.top);
return 1;
}
2、链栈
说明:链栈的两个特殊状态和两个操作
lst->next == NULL; //栈空状态
//假设不受内存限制,则不存在栈满的状态
p->next = lst->next; //进栈元素由p指针所指
lst->next = p; //进栈操作——类比单链表的头插法
p = lst->next; //出栈元素由p指针所指
x = p->data;
lst->next = p->next;
free(p); //出栈操作——类比单链表的删除操作
(1)链栈的结构体定义:
typedef struct LNode{
int data; //数据域
struct LNode *next; //指针域
}LNode; //链栈类型定义
(2)链栈的初始化:需要声明一个头节点(相当于栈顶指针),将其指针域赋值为 null
void initStack(LNode *&lst){ //lst要改变,用引用型
lst = (LNode*)malloc(sizeof(LNode)); //制造一个头节点
lst->next = NULL;
}
(3)判断栈空:头节点指针域为空则表示此时栈为空
int isEmpty(LNode *lst){ //指向头节点的指针lst
if(lst->next == NULL) //判断头节点的指针与是否为空
return 1;
else
return 0;
}
(4)进栈操作:先为进栈元素申请节点空间,再将新节点做进栈插入操作
void push(LNode *lst, int x){
LNode *p;
p = (LNode*)malloc(sizeof(LNode)); //为进栈元素申请节点空间
p->next = NULL; //将新节点的指针域赋值为null
p->data = x;
p->next = lst->next; //相当于链表的头插法
lst->next = p;
}
(5)出栈操作:若栈空则不能出栈,若栈非空,再做出栈操作
int pop(LNode *lst,int &x){
LNode *p;
if(lst->next == NULL) //判断栈是否为空
return 0;
p = lst->next; //假设p指向要出栈的元素
x = p->data; //出栈操作,类比单链表的删除操作
lst->next = p->next;
free(p);
return 1;
}
四、队列的结构体定义和基本操作
1、顺序队
说明:在顺序队中,通常让队尾指针rear 指向刚进队的元素位置,让队首指针ront 指向刚出队的元素位置。因此,元素进队的时候,rear要向后移动;元素出队的时候,front也要向后移动。这样经过一系列的出队和进队操作以后,两个指针最终会达到数组末端maxSize-1处。虽然队中已经没有元素,但仍然无法让元素进队,这就是所谓的“假溢出”。要解决这个问题,可以把数组弄成一环,让rear和front沿着环走,这样就永远不会出现两者来到数组尽头无法继续往下走的情况,这样就产生了循环队列。循环队列是改进的顺序队列。下面是循环队列的两个特殊状态和两个操作
qu.rear == qu.front; //队空状态
(qu.rear+1)%maxSize == qu.front; //队满状态
qu.rear = (qu.rear+1)%maxSize;
qu.data[qu.rear] = x; //进队操作(移动队尾指针)
qu.front = (qu.front+1)%maxSize;
x = qu.data[qu.front]; //出队操作(移动队头指针)
(1)顺序队列的结构体定义:
typedef struct{
int data[maxSize];
int front; //队首指针
int rear; //队尾指针
}SqQueue; //顺序队类型定义
(2)顺序队的初始化:队首和队尾指针重合,并且指向 0
void initQueue(SqQueue &qu){
qu.front = qu.rear = 0; //队首和队尾指针重合,并且指向 0
}
(3)判断队空:不论队首、队尾指针指向数组中哪个位置只要两者重合,即为队空
int isQueueEmpty(SqQueue qu){
if(qu.front == qu.rear) //不论队首、队尾指针指向数组中哪个位置
return 1; //只要两者重合,即为队空
else
return 0;
}
(4)进队操作:队满不能进队,若队未满,先移动队尾指针,然后将进队元素赋值进去
int enterQueue(SqQueue &qu,int x){
if((qu.rear+1)%maxSize == qu.front) //判断队列是否满
return 0;
qu.rear = (qu.rear+1)%maxSize; //若未满,则先移动指针
qu.data[qu.rear] = x; //再存入元素
return 1;
}
(5)出队操作:队空不能出队,若队列未空,先取出元素,再移动队首指针
int deQueue(SqQueue &qu,int &x){
if(qu.front == qu.rear) //判断当前队列是否为空
return 0;
qu.front = (qu.front+1)%maxSize; //若队不空,先移动指针
x = qu.data[qu.front]; //再取出元素
return 1;
}
2、链队
说明:链队就是从采用链式存储队列,用单链表实现。下面是链队的两个特殊状态和两个操作
lqu->rear == NULL;
lqu->front == NULL; //两个条件均能判断链队为空
//假设不受内存限制,则不存在链队列满的状态
lqu->rear->next = p;
lqu->rear = p; //进队操作(假设p指向进队元素) 队空为特殊状态需要单独考虑
p = lqu->front;
lqu->front = p->next;
x = p->data;
free(p); //出队操作(假设x存储出队元素) 队中只有一个元素的话需要单独考虑
(1)链队结构体定义:
typedef struct {
int data; //数据域
struct *front; //队头指针
struct e *rear; //队尾指针
}; //链队类型定义
(2)链队的初始化:开辟一个链队节点,将链队的前后指针都赋值为null
void initListQueue(LiQueue *&lqu){
lqu = (LiQueue*)malloc(sizeof(LiQueue));
lqu->front = lqu->rear = NULL;
}
(3)判断队空:front 和 rear 任何一个为空都可以判断链队为空
int isQueueEmpty(LiQueue *lqu){
if(lqu->rear==NULL || Lqu->front==NULL)
return 1;
else
return 0;
}
(4)入队操作:为入队的元素开辟内存空间,再将其做入队操作,若队为空则前后指针都指向它,若不为空则rear指向它
int isQueueEmpty(LiQueue *lqu, int x){
QNode *p;
p = (QNode*)malloc(sizeof(QNode));
p->data = x;
p->next = NULL;
if(lqu->rear == NULL) //若队列为空,则新节点是队首节点也是队尾节点
Lqu->front = lq->rear = p;
else{
lqu->rear->next = p; //将新节点链接到队尾,rear指向它
lqu->rear = p;
}
}
(5)出队操作:如果队列中只有一个节点,则出队后将队列初始化即可,若不止一个,则进行出队操作
int deQueue(LiQueue *lqu,int &x){
QNode *p;
if(lqu->rear == NULL) //判断此时的队列是否为空
return 0;
else
p=lqu->front;
if(lqu->front == lqu->rear) //若队列只有一个节点,需要进行特殊操作
lqu->front = lqu->rear = NULL;
else
lqu->front=lqu->front-next;
x = p->data;
free(p);
return 1;
}