数据结构3-栈和队列

打印 上一主题 下一主题

主题 903|帖子 903|积分 2709

 栈和队列的操纵特点

栈和队列是限定插入和删除只能在表的“端点”举行的线性表
栈(操纵尾部)和队列(操纵头部)是线性表的子集(是插入和删除位置受限的线性表)
栈(Stack)

栈是一个遵循 后进先出 (LIFO, Last In First Out) 规则的线性表。栈的插入和删除操纵都只能在表的一端举行,即栈顶。常见的栈操纵包括:

  • Push:向栈顶插入一个元素。
  • Pop:从栈顶删除一个元素。
  • TopPeek:检察栈顶元素。
队列(Queue)

队列是一个遵循 先辈先出 (FIFO, First In First Out) 规则的线性表。队列的插入操纵发生在表的一端(队尾),删除操纵发生在表的另一端(队头)。常见的队列操纵包括:

  • Enqueue:向队尾插入一个元素。
  • Dequeue:从队头删除一个元素。
  • Front:检察队头元素。
栈与队列的比较:



  • :插入和删除只能发生在表的顶部。栈是一种顺序存取的数据结构,操纵是单向的。
  • 队列:插入操纵发生在队尾,删除操纵发生在队头,符合先辈先出的原则。

在顺序存储结构和链式存储结构上实现栈(顺序栈、链栈)和队列(循环队列、链队列)的各种基本操纵

1. 栈(Stack)基本操纵

顺序栈(Array-based Stack)

顺序栈使用数组作为底层存储结构。基本操纵包括 Push(入栈)、Pop(出栈)、Peek(获取栈顶元素)。
  1. #define MAX_SIZE 100
  2. typedef int ElemType;  // 假设栈的元素类型为整型
  3. typedef struct {
  4.     ElemType elem[MAX_SIZE];
  5.     int top;  // 栈顶指针
  6. } SqStack;
  7. // InitStack: 初始化栈,构建一个空的顺序栈
  8. void InitStack(SqStack &S) {
  9.     S.top = -1;  // 初始化栈为空栈
  10. }
  11. // StackEmpty: 判断栈是否为空,栈空返回1,否则返回0
  12. int StackEmpty(SqStack S) {
  13.     return S.top == -1;  // 栈空返回1,否则返回0
  14. }
  15. // StackLength: 返回栈的元素个数(栈的长度)
  16. int StackLength(SqStack S) {
  17.     return S.top + 1;  // 栈长度为栈顶指针+1
  18. }
  19. // Push: 向栈顶插入元素e,如果栈满则报错
  20. void Push(SqStack &S, ElemType e) {
  21.     if (S.top == MAX_SIZE - 1) {
  22.         printf("栈满\n");
  23.         return;
  24.     }
  25.     S.elem[++S.top] = e;  // 入栈
  26. }
  27. // Pop: 从栈顶删除元素,并将删除的元素存储在e中
  28. void Pop(SqStack &S, ElemType &e) {
  29.     if (S.top == -1) {
  30.         printf("栈空\n");
  31.         return;
  32.     }
  33.     e = S.elem[S.top--];  // 出栈
  34. }
  35. // GetTop: 获取栈顶元素,但不删除该元素
  36. void GetTop(SqStack S, ElemType &e) {
  37.     if (S.top == -1) {
  38.         printf("栈空\n");
  39.         return;
  40.     }
  41.     e = S.elem[S.top];  // 获取栈顶元素
  42. }
复制代码
链栈(Linked List-based Stack)

链栈使用链表作为底层存储结构。基本操纵与顺序栈雷同。
  1. typedef int ElemType;
  2. typedef struct Node {
  3.     ElemType data;
  4.     struct Node* next;
  5. } Node;
  6. typedef struct {
  7.     Node* top;  // 栈顶指针
  8. } LinkStack;
  9. // InitStack: 初始化栈,构建一个空的链栈
  10. void InitStack(LinkStack &S) {
  11.     S.top = NULL;  // 初始化栈为空栈
  12. }
  13. // StackEmpty: 判断链栈是否为空,空栈返回1,否则返回0
  14. int StackEmpty(LinkStack S) {
  15.     return S.top == NULL;  // 栈空返回1,否则返回0
  16. }
  17. // Push: 向链栈的栈顶插入元素e
  18. void Push(LinkStack &S, ElemType e) {
  19.     Node* newNode = (Node*)malloc(sizeof(Node));
  20.     newNode->data = e;
  21.     newNode->next = S.top;
  22.     S.top = newNode;  // 入栈
  23. }
  24. // Pop: 从链栈的栈顶删除元素,并将删除的元素存储在e中
  25. void Pop(LinkStack &S, ElemType &e) {
  26.     if (S.top == NULL) {
  27.         printf("栈空\n");
  28.         return;
  29.     }
  30.     Node* temp = S.top;
  31.     e = S.top->data;  // 出栈
  32.     S.top = S.top->next;
  33.     free(temp);
  34. }
  35. // GetTop: 获取链栈的栈顶元素,但不删除该元素
  36. void GetTop(LinkStack S, ElemType &e) {
  37.     if (S.top == NULL) {
  38.         printf("栈空\n");
  39.         return;
  40.     }
  41.     e = S.top->data;  // 获取栈顶元素
  42. }
复制代码
链队列(Linked List-based Queue)

链队列使用链表作为底层存储结构,队列的头指针指向队头,尾指针指向队尾。
  1. typedef int ElemType;
  2. typedef struct Node {
  3.     ElemType data;
  4.     struct Node* next;
  5. } Node;
  6. typedef struct {
  7.     Node* front;  // 队头指针
  8.     Node* rear;   // 队尾指针
  9. } LinkQueue;
  10. // InitStack: 初始化栈,构建一个空的链栈
  11. void InitQueue(LinkQueue &Q) {
  12.     Q.front = Q.rear = NULL;  // 初始化队列为空队列
  13. }
  14. // StackEmpty: 判断链栈是否为空,空栈返回1,否则返回0
  15. int QueueEmpty(LinkQueue Q) {
  16.     return Q.front == NULL;  // 判断队列是否为空
  17. }
  18. // Enqueue: 向队列的队尾插入元素e
  19. void Enqueue(LinkQueue &Q, ElemType e) {
  20.     Node* newNode = (Node*)malloc(sizeof(Node));
  21.     newNode->data = e;
  22.     newNode->next = NULL;
  23.     if (Q.rear == NULL) {
  24.         Q.front = Q.rear = newNode;  // 队列为空时,队头和队尾都指向新节点
  25.     } else {
  26.         Q.rear->next = newNode;  // 队尾插入新节点
  27.         Q.rear = newNode;
  28.     }
  29. }
  30. // Dequeue: 从队列的队头删除元素,并将删除的元素存储在e中
  31. void Dequeue(LinkQueue &Q, ElemType &e) {
  32.     if (Q.front == NULL) {
  33.         printf("队列空\n");
  34.         return;
  35.     }
  36.     Node* temp = Q.front;
  37.     e = Q.front->data;  // 出队
  38.     Q.front = Q.front->next;
  39.     if (Q.front == NULL) {
  40.         Q.rear = NULL;  // 如果队列为空,更新队尾指针
  41.     }
  42.     free(temp);
  43. }
  44. // GetFront: 获取队列的队头元素,但不删除该元素
  45. void GetFront(LinkQueue Q, ElemType &e) {
  46.     if (Q.front == NULL) {
  47.         printf("队列空\n");
  48.         return;
  49.     }
  50.     e = Q.front->data;  // 获取队头元素
  51. }
复制代码
栈和队列的简单应用

假设有一个洗碗机,它需要根据不同的顺序来处置处罚碗碟。这个问题涉及到两个部门:清洗和列队。当碗碟进洗碗机时,它们是按先辈先出(FIFO)的顺序进入的。然而,清洗时,洗碗机只能后进先出(LIFO)的顺序举行处置处罚。也就是说,洗碗机必须清洗最后进入的碗碟,然后才清洗前面的碗碟。
使用一个队列来模拟碗碟的进入顺序,而使用一个栈来模拟洗碗机清洗的顺序。


  • 碗碟进入洗碗机:用户将碗碟依次放入洗碗机,这部门用一个队列来模拟。每个碗碟被按顺序到场队列。
  • 清洗顺序:由于洗碗机必须按照“后进先出”的顺序清洗碗碟,可以将队列中的碗碟转移到栈中,然后按栈的顺序举行清洗。
  • 清洗过程:从栈中弹出碗碟并清洗,直到栈为空。
  1. from collections import deque
  2. class Dishwasher:
  3.     def __init__(self):
  4.         self.queue = deque()  # 用队列存储碗碟进入顺序
  5.         self.stack = []  # 用栈模拟清洗顺序
  6.     def add_dish(self, dish):
  7.         # 把碗碟按顺序放入队列
  8.         self.queue.append(dish)
  9.         print(f"添加碗碟:{dish}")
  10.     def start_washing(self):
  11.         # 把队列中的碗碟转移到栈中(洗碗机需要后进先出的顺序)
  12.         while self.queue:
  13.             dish = self.queue.popleft()
  14.             self.stack.append(dish)
  15.         
  16.         print("开始清洗碗碟:")
  17.         # 按栈的顺序清洗碗碟(后进先出)
  18.         while self.stack:
  19.             dish = self.stack.pop()
  20.             print(f"正在清洗:{dish}")
  21. # 使用示例:
  22. dishwasher = Dishwasher()
  23. dishwasher.add_dish("碗1")
  24. dishwasher.add_dish("碟1")
  25. dishwasher.add_dish("杯子1")
  26. # 开始清洗
  27. dishwasher.start_washing()
复制代码
碗碟进入顺序:碗1, 碟1, 杯子1,它们依次进入队列。
清洗顺序:由于洗碗机必须后进先出(LIFO),我们把队列中的碗碟依次转移到栈中,然后按栈的顺序清洗:先清洗最后放入的“杯子1”,然后是“碟1”,最后是“碗1”。
递归程序筹划的基本方法(分治法、减治法)

递归程序筹划的基本方法,包括分治法和减治法。其中,分治法递归的结构为“基本项+归纳项”。详细而言:
基本项:递归的停止条件,当问题足够简单时,直接返回结果。
归纳项:通过将问题分解为更小的子问题来解决,并递归地调用同样的方法来处置处罚这些子问题,直到满足基本项条件。
分治法递归:基本项+归纳项。
阶乘计算:
  1. long Fact(long n) {
  2.     if (n == 0) return 1;  // 基本项
  3.     else return n * Fact(n - 1);  // 归纳项
  4. }
复制代码
基本项:当 n == 0 时,返回 1。
归纳项:当 n > 0 时,返回 n * Fact(n - 1),通过递归调用计算阶乘。
这种结构很适合解决分治法问题,即通过递归将大问题分解成小问题,再逐步解决。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

麻花痒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表