ToB企服应用市场:ToB评测及商务社交产业平台
标题:
数据结构3-栈和队列
[打印本页]
作者:
麻花痒
时间:
5 天前
标题:
数据结构3-栈和队列
栈和队列的操纵特点
栈和队列是限定插入和删除只能在表的“端点”举行的线性表
栈(操纵尾部)和队列(操纵头部)是线性表的子集(是插入和删除位置受限的线性表)
栈(Stack)
栈是一个遵循
后进先出
(LIFO, Last In First Out) 规则的线性表。栈的插入和删除操纵都只能在表的
一端
举行,即栈顶。常见的栈操纵包括:
Push
:向栈顶插入一个元素。
Pop
:从栈顶删除一个元素。
Top
或
Peek
:检察栈顶元素。
队列(Queue)
队列是一个遵循
先辈先出
(FIFO, First In First Out) 规则的线性表。队列的插入操纵发生在表的一端(队尾),删除操纵发生在表的另一端(队头)。常见的队列操纵包括:
Enqueue
:向队尾插入一个元素。
Dequeue
:从队头删除一个元素。
Front
:检察队头元素。
栈与队列的比较:
栈
:插入和删除只能发生在表的顶部。栈是一种顺序存取的数据结构,操纵是单向的。
队列
:插入操纵发生在队尾,删除操纵发生在队头,符合先辈先出的原则。
在顺序存储结构和链式存储结构上实现栈(顺序栈、链栈)和队列(循环队列、链队列)的各种基本操纵
1. 栈(Stack)基本操纵
顺序栈(Array-based Stack)
顺序栈使用数组作为底层存储结构。基本操纵包括 Push(入栈)、Pop(出栈)、Peek(获取栈顶元素)。
#define MAX_SIZE 100
typedef int ElemType; // 假设栈的元素类型为整型
typedef struct {
ElemType elem[MAX_SIZE];
int top; // 栈顶指针
} SqStack;
// InitStack: 初始化栈,构建一个空的顺序栈
void InitStack(SqStack &S) {
S.top = -1; // 初始化栈为空栈
}
// StackEmpty: 判断栈是否为空,栈空返回1,否则返回0
int StackEmpty(SqStack S) {
return S.top == -1; // 栈空返回1,否则返回0
}
// StackLength: 返回栈的元素个数(栈的长度)
int StackLength(SqStack S) {
return S.top + 1; // 栈长度为栈顶指针+1
}
// Push: 向栈顶插入元素e,如果栈满则报错
void Push(SqStack &S, ElemType e) {
if (S.top == MAX_SIZE - 1) {
printf("栈满\n");
return;
}
S.elem[++S.top] = e; // 入栈
}
// Pop: 从栈顶删除元素,并将删除的元素存储在e中
void Pop(SqStack &S, ElemType &e) {
if (S.top == -1) {
printf("栈空\n");
return;
}
e = S.elem[S.top--]; // 出栈
}
// GetTop: 获取栈顶元素,但不删除该元素
void GetTop(SqStack S, ElemType &e) {
if (S.top == -1) {
printf("栈空\n");
return;
}
e = S.elem[S.top]; // 获取栈顶元素
}
复制代码
链栈(Linked List-based Stack)
链栈使用链表作为底层存储结构。基本操纵与顺序栈雷同。
typedef int ElemType;
typedef struct Node {
ElemType data;
struct Node* next;
} Node;
typedef struct {
Node* top; // 栈顶指针
} LinkStack;
// InitStack: 初始化栈,构建一个空的链栈
void InitStack(LinkStack &S) {
S.top = NULL; // 初始化栈为空栈
}
// StackEmpty: 判断链栈是否为空,空栈返回1,否则返回0
int StackEmpty(LinkStack S) {
return S.top == NULL; // 栈空返回1,否则返回0
}
// Push: 向链栈的栈顶插入元素e
void Push(LinkStack &S, ElemType e) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = e;
newNode->next = S.top;
S.top = newNode; // 入栈
}
// Pop: 从链栈的栈顶删除元素,并将删除的元素存储在e中
void Pop(LinkStack &S, ElemType &e) {
if (S.top == NULL) {
printf("栈空\n");
return;
}
Node* temp = S.top;
e = S.top->data; // 出栈
S.top = S.top->next;
free(temp);
}
// GetTop: 获取链栈的栈顶元素,但不删除该元素
void GetTop(LinkStack S, ElemType &e) {
if (S.top == NULL) {
printf("栈空\n");
return;
}
e = S.top->data; // 获取栈顶元素
}
复制代码
链队列(Linked List-based Queue)
链队列使用链表作为底层存储结构,队列的头指针指向队头,尾指针指向队尾。
typedef int ElemType;
typedef struct Node {
ElemType data;
struct Node* next;
} Node;
typedef struct {
Node* front; // 队头指针
Node* rear; // 队尾指针
} LinkQueue;
// InitStack: 初始化栈,构建一个空的链栈
void InitQueue(LinkQueue &Q) {
Q.front = Q.rear = NULL; // 初始化队列为空队列
}
// StackEmpty: 判断链栈是否为空,空栈返回1,否则返回0
int QueueEmpty(LinkQueue Q) {
return Q.front == NULL; // 判断队列是否为空
}
// Enqueue: 向队列的队尾插入元素e
void Enqueue(LinkQueue &Q, ElemType e) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = e;
newNode->next = NULL;
if (Q.rear == NULL) {
Q.front = Q.rear = newNode; // 队列为空时,队头和队尾都指向新节点
} else {
Q.rear->next = newNode; // 队尾插入新节点
Q.rear = newNode;
}
}
// Dequeue: 从队列的队头删除元素,并将删除的元素存储在e中
void Dequeue(LinkQueue &Q, ElemType &e) {
if (Q.front == NULL) {
printf("队列空\n");
return;
}
Node* temp = Q.front;
e = Q.front->data; // 出队
Q.front = Q.front->next;
if (Q.front == NULL) {
Q.rear = NULL; // 如果队列为空,更新队尾指针
}
free(temp);
}
// GetFront: 获取队列的队头元素,但不删除该元素
void GetFront(LinkQueue Q, ElemType &e) {
if (Q.front == NULL) {
printf("队列空\n");
return;
}
e = Q.front->data; // 获取队头元素
}
复制代码
栈和队列的简单应用
假设有一个洗碗机,它需要根据不同的顺序来处置处罚碗碟。这个问题涉及到两个部门:清洗和列队。当碗碟进洗碗机时,它们是按先辈先出(FIFO)的顺序进入的。然而,清洗时,洗碗机只能后进先出(LIFO)的顺序举行处置处罚。也就是说,洗碗机必须清洗最后进入的碗碟,然后才清洗前面的碗碟。
使用一个队列来模拟碗碟的进入顺序,而使用一个栈来模拟洗碗机清洗的顺序。
碗碟进入洗碗机
:用户将碗碟依次放入洗碗机,这部门用一个队列来模拟。每个碗碟被按顺序到场队列。
清洗顺序
:由于洗碗机必须按照“后进先出”的顺序清洗碗碟,可以将队列中的碗碟转移到栈中,然后按栈的顺序举行清洗。
清洗过程
:从栈中弹出碗碟并清洗,直到栈为空。
from collections import deque
class Dishwasher:
def __init__(self):
self.queue = deque() # 用队列存储碗碟进入顺序
self.stack = [] # 用栈模拟清洗顺序
def add_dish(self, dish):
# 把碗碟按顺序放入队列
self.queue.append(dish)
print(f"添加碗碟:{dish}")
def start_washing(self):
# 把队列中的碗碟转移到栈中(洗碗机需要后进先出的顺序)
while self.queue:
dish = self.queue.popleft()
self.stack.append(dish)
print("开始清洗碗碟:")
# 按栈的顺序清洗碗碟(后进先出)
while self.stack:
dish = self.stack.pop()
print(f"正在清洗:{dish}")
# 使用示例:
dishwasher = Dishwasher()
dishwasher.add_dish("碗1")
dishwasher.add_dish("碟1")
dishwasher.add_dish("杯子1")
# 开始清洗
dishwasher.start_washing()
复制代码
碗碟进入顺序
:碗1, 碟1, 杯子1,它们依次进入队列。
清洗顺序
:由于洗碗机必须后进先出(LIFO),我们把队列中的碗碟依次转移到栈中,然后按栈的顺序清洗:先清洗最后放入的“杯子1”,然后是“碟1”,最后是“碗1”。
递归程序筹划的基本方法(分治法、减治法)
递归程序筹划的基本方法,包括分治法和减治法。其中,分治法递归的结构为“基本项+归纳项”。详细而言:
基本项:递归的停止条件,当问题足够简单时,直接返回结果。
归纳项:通过将问题分解为更小的子问题来解决,并递归地调用同样的方法来处置处罚这些子问题,直到满足基本项条件。
分治法递归:基本项+归纳项。
阶乘计算:
long Fact(long n) {
if (n == 0) return 1; // 基本项
else return n * Fact(n - 1); // 归纳项
}
复制代码
基本项:当 n == 0 时,返回 1。
归纳项:当 n > 0 时,返回 n * Fact(n - 1),通过递归调用计算阶乘。
这种结构很适合解决分治法问题,即通过递归将大问题分解成小问题,再逐步解决。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4