cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学

Slides:



Advertisements
Similar presentations
河內塔(Hanoi)問題.
Advertisements

迷 宫 最短路径 施沈翔.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
動畫與遊戲設計 Data structure and artificial intelligent
计算学科的基本问题 本章首先介绍一个对问题进行抽象的典型实例——哥尼斯堡七桥问题。然后,通过“梵天塔”问题和“停机问题”分别介绍学科中的可计算问题和不可计算问题。从“梵天塔”问题再引出算法复杂性中的难解性问题、P类问题和NP类问题,证比求易算法,P=NP是否成立的问题。
数据结构概论 第2章 线性表 董黎刚 浙江工商大学信电学院
数据结构——树和二叉树 1/96.
資料結構與C++程式設計進階 資料結構概論 講師:林業峻 CSIE, NTU 6/ 7, 2010.
数据结构与算法 数据结构与算法实验
Chapter 3: Stack.
第三章 堆疊與佇列的基本應用 3-1 簡介堆疊(Stack) 3-2 迷宮問題研究 3-3 佇列(queue)的介紹
第3章 栈和队列 3.1 栈 3.2 队列 3.3 应用.
第6章 佇列(Queues) 6-1 佇列的基礎 6-2 佇列的表示法 6-3 環狀佇列 6-4 雙佇列.
佇列 (Queue).
佇列與推疊 (Queue and Stack)
資料結構簡介.
資料結構 第5章 佇列.
Chap 3 堆疊與佇列 Stack and Queue.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
Ch.3 栈和队列 黄刘生 中国科学技术大学计算机系 国家高性能计算中心(合肥)
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
4 堆疊與佇列 4.1 前言 四種基本的資料結構 (可儲存資料的容器) 陣列 (Array)、串列(List): 最基本
第三章 C++中的C 面向对象程序设计(C++).
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
第3章 栈和队列(二) 1/.
第十章 C高级程序应用—链表* 10.1链表的基本概念 10.2单向链表 10.3双向链表 10.4应用举例.
堆疊 Stack chapter 4 德明科技大學資訊科技系.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
计算机网络讲义 第5章 批量数据处理—数组 一维数组 排序和查找 二维数组 字符串.
1.1 数据结构讨论的范畴 1.2 基本概念 1.3 算法和算法的量度.
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
第 3 讲 线性表(一).
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第三章 栈和队列 3.1 栈 3.2 栈的应用举例 3.3 栈与递归的实现 3.4 队列.
第3章 栈和队列(一).
第三章 栈和队列.
数据结构 Data Structure 中南大学 主讲人:王国军,郑瑾 中南大学信息院计科系
資料結構與C++程式設計進階 堆疊與佇列(Stack & Queue) 講師:林業峻 CSIE, NTU 6/ 21, 2010.
第一章 绪论.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
内容回顾 线性表的定义和特点 线性表的顺序存储及查找、插入和删除.
C++语言程序设计 第二章 C++简单程序设计.
6.3 遍历二叉树和线索二叉树(知识点二) 遍历二叉树 一、问题的提出
第三章 栈和队列.
#define STACK_INIT_SIZE 10
第四章 串.
資料結構 第4章 堆疊.
严蔚敏、吴伟民编著 清华大学出版社 学习网站:
Chapter6 队列(Queues) The Abstract Data Type
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
Chapter 03 Stack & Queue 第三章 栈和队列
经典算法之 冒 泡 排 序.
程式結構&語法.
top b top a a top 空栈 a 进栈 b 进栈 top top e e e top d d d c c c b b b a a
Linked Lists Prof. Michael Tsai 2013/3/12.
Chap2 Stack & Queue.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
資料結構與C++程式設計進階 遞迴(Recursion) 講師:林業峻 CSIE, NTU 6/ 17, 2010.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
資料結構簡介 綠園.
第 六 讲 栈和队列(一).
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
Chapter 2 Entity-Relationship Model
Presentation transcript:

http://staff.ustc.edu. cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学 Data Structure and Algorithm 《数据结构及其算法》 http://staff.ustc.edu. cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学

第4章 栈和队列 4.1 栈的基本概念 4.2 栈的表示与实现 4.3 栈的应用 4.4 队列的基本概念 4.5 队列表示与实现 4.6 队列的应用 4.7 递归及其应用 2018/12/3 数据结构及其算法 第4章 栈和队列

4.1 栈的基本概念 栈(stack):限定仅在表尾进行插入或删除操作的 线性表 从数据结构角度看,栈也是线性表,或操作受限的 线性表,称为限定性数据结构 Data_Structure = (D,S) 从抽象数据类型角度看,允许的操作不一样,则数 据类型不同 ADT = (D,S,P) 2018/12/3 数据结构及其算法 第4章 栈和队列

詹天佑设计的人字形铁路(北京青龙桥车站附近) 设栈S = (a1,a2,...,an) a1称为栈底(bottom)元素 an称为栈顶(top)元素 栈顶插入元素 – 入栈 栈顶删除元素 – 出栈 后进先出(last in first out, LIFO) 出栈 入栈 栈顶 an … 詹天佑设计的人字形铁路(北京青龙桥车站附近) a2 栈底 a1 2018/12/3 数据结构及其算法 第4章 栈和队列

4.2 栈的表示与实现 顺序栈:栈的顺序存储结构 类似顺序表 由于总在栈顶插入、删除,保留栈顶位置方便操作 typedef struct { ElemType *elem; // 基地址 int stacksize; // 当前分配内存大小,单位是sizeof(ElemType) int top; // 栈顶位置,定义为表长-1 } SqStack; 2018/12/3 数据结构及其算法 第4章 栈和队列

顺序栈基本操作的实现(算法4.1~4.3) void InitStack_sq(SqStack &S, int msize) { S.elem = new ElemType[msize]; S.stacksize = msize; S.top = -1; }   void DestroyStack_sq(SqStack &S) { delete []S.elem; S.stacksize = 0; bool GetTop_sq(SqStack S, ElemType &e) { if (S.top == -1) return false; e = S.elem[S.top]; return true; 2018/12/3 数据结构及其算法 第4章 栈和队列

顺序栈基本操作的实现(算法4.4、4.5) const int SQSTACK_INC_SIZE = 10;   void Increment(SqStack &S, int inc_size = SQSTACK_INC_SIZE) { ElemType *a = new ElemType[S.stacksize + inc_size]; for (int i=0; i<=S.top; ++i) a[i] = S.elem[i]; delete []S.elem; S.elem = a; S.stacksize += inc_size; } void Push_sq(SqStack &S, ElemType e) { if (S.top == S.stacksize-1) Increment(S); S.elem[++S.top] = e; bool Pop_sq(SqStack &S, ElemType &e) { if (S.top == -1) return false; e = S.elem[S.top--]; return true; 2018/12/3 数据结构及其算法 第4章 栈和队列

链栈:栈的链式存储结构 链栈基本操作的实现(算法4.6、4.7) 类似链表 由于插入、删除只在栈顶进行,因此将栈顶设为首元结点, 方便操作 typedef LinkList LinkStack; void InitStack_L(LinkStack &S) { S = NULL; }   void DestroyStack_L(LinkStack &S) { while (S) { LinkStack p = S; S = S->next; delete p; 2018/12/3 数据结构及其算法 第4章 栈和队列

链栈基本操作的实现(算法4.8~4.10) 思考:顺序栈和链栈何者更优?为什么? bool GetTop_L(LinkStack S, ElemType &e) { if (!S) return false; e = S->data; return true; }   void Push_L(LinkStack &S, ElemType e) { LinkStack p = new LNode; p->data = e; p->next = S; S = p; bool Pop_L(LinkStack &S, ElemType &e) { LinkStack p = S; S = S->next; e = p->data; delete p; return true; 思考:顺序栈和链栈何者更优?为什么? 2018/12/3 数据结构及其算法 第4章 栈和队列

4.3 栈的应用 例:数制转换问题 基本等式:N = (N div d) × d + N mod d 计算过程 算出的余数逆序排列即为输出结果 可以利用栈实现 算法4.11 1348 8 168 21 2 余数 4 5 (1348)10=(2504)8 void conversion(int N, int d) { if (N<=0 || d<=0) ErrorMsg("Input error"); Stack S; InitStack(S); while (N) { Push(S, N%d); N = N/d; } int e; while (Pop(S, e)) { cout << e; } cout << endl; } 2018/12/3 数据结构及其算法 第4章 栈和队列

如{[(3+5)×2]-7}÷3是合法的表达式, [(3+5]×2)-7或者((3+5)×2]-7)÷3非法 例:括号匹配问题 如{[(3+5)×2]-7}÷3是合法的表达式, [(3+5]×2)-7或者((3+5)×2]-7)÷3非法 规则:从左向右,第一个出现的右括号需要和最后 一个出现的左括号配对,“后进先出” 算法4.12 初始化栈 从左向右读入表达式中的括号 如果是左括号,进栈 如果是右括号,检查栈顶的左括号是否与它配对,若配对则左括 号出栈,否则错误 读入结束后,栈应该是空的,否则错误 思考:如何对算法进行改进,进一步检查括号的优 先级?即:[]中不能有{},()中不能有[]和{} 2018/12/3 数据结构及其算法 第4章 栈和队列

例:N皇后问题 国际象棋中的“后”可以吃掉与她同一行、同一列、同一对 角线的棋子,如何在N×N的棋盘摆上N个皇后,使得她们 彼此吃不到对方? 典型算法:试探-回溯法 思路: 逐行试探,先在第一行摆一个,再在第二行摆一个,…… N行全部摆好,说明找到一种解法 如果某一行无法摆放,说明之前行的摆法不可行,退回到上一行重新摆放 数据结构: 采用栈记录皇后摆放位置,以便回溯 附设列、左下对角线、右下对角线三个数组防止皇后冲突 2018/12/3 数据结构及其算法 第4章 栈和队列

思考:如何对程序进行修改以找出所有的解? void queen(int N) { // 使用栈求解N皇后问题 bool *A = new bool[N], *B = new bool[2*N-1], *C = new bool[2*N-1]; for (int i=0; i<N; ++i) A[i] = false; for (int i=0; i<2*N-1; ++i) B[i] = false; for (int i=0; i<2*N-1; ++i) C[i] = false; Stack S; InitStack(S); int sj = 0; while (true) { int i = StackLength(S); bool feasible = false; if (i < N) for (int j = sj; j < N; ++j) { if (place(i, j, N, A, B, C)) { mark(i, j, N, true, A, B, C); Push(S, j); if (i == N-1) { print(S); return; } feasible = true; break; } if (!feasible) { int j; if (!Pop(S, j)) break; mark(i-1, j, N, false, A, B, C); sj = j+1; else sj = 0; bool place(int i, int j, int N, bool *A, bool *B, bool *C) { return !(A[j] || B[i+j] || C[i-j+N-1]); } void mark(int i, int j, int N, bool flag, bool *A, bool *B, bool *C) { A[j] = B[i+j] = C[i-j+N-1] = flag; } 思考:如何对程序进行修改以找出所有的解? 2018/12/3 数据结构及其算法 第4章 栈和队列

一般的算术表达式又称中缀表达式(infix notation),如:4×2+(7-8÷2),运算符在操作 数中间,需要用括号来表示运算顺序 例:表达式求值问题(算符优先法) 一般的算术表达式又称中缀表达式(infix notation),如:4×2+(7-8÷2),运算符在操作 数中间,需要用括号来表示运算顺序 例:计算一位整数、无括号的四则运算7-8÷2+… 7 操作数 - 运算符 8 操作数 此时不能计算7-8,后续可能×或÷,7、8、-分别保存 ÷ 运算符 2 操作数 + 运算符 前一运算符÷可做,因为÷优先于+ 计算8(前一操作数)÷2,运算结果为4 再前一运算符-可做,因为左侧-优先于+ 计算7-4,运算结果为3,保存 …… 2018/12/3 数据结构及其算法 第4章 栈和队列

运算符:要和前一个比较优先级 操作数:要和前一个进行计算 表达式求值算法 设两个栈 运算符栈保存暂时不能计算的运算符 操作数栈保存暂时不能计算的操作数或中间结果 虚设一对'#'构成整个表达式的界限符(delimiter) 读入操作数直接入栈 读入运算符或()或#,进行算符优先级判断和相应操作 设两个栈 2018/12/3 数据结构及其算法 第4章 栈和队列

例:计算#4×2+(7-8÷2)#的过程 OPTR OPND OPTR OPND # OPTR OPND # 8 + 7 ( - ) + ) 3 OPTR OPND # 8 + # 7-4 8+3 3 11 2018/12/3 数据结构及其算法 第4章 栈和队列

算法4.16 char Precede(char c1, char c2) { if (c1 == '+' && c2 == '+') return '>'; ... } 算法4.16 int EvaluateExpression(char *expr) { Stack SOP, SVAL; InitStack(SOP); Push(SOP, '#'); InitStack(SVAL); expr[strlen(expr)] = '#'; char tmp[2]; int i=0; char c=expr[i++]; while (c != '#' || GetTop(SOP) != '#') { if (!IsOperator(c)) { tmp[0] = c; tmp[1] = '\0'; Push(SVAL, atoi(tmp)); c = expr[i++]; } else switch(Precede(GetTop(SOP), c)) { case '<': Push(SOP, c); c = expr[i++]; break; case '=': Pop(SOP, c); c = expr[i++]; break; case '>': char theta; Pop(SOP, theta); int a, b; Pop(SVAL, b); Pop(SVAL, a); Push(SVAL, Operate(a, theta, b)); break; expr[strlen(expr)] = '\0'; return GetTop(SVAL); bool IsOperator(char c) { return c == '+' || c == '-' ... } int Operate(int a, char c, int b) { if (c == '+') return a+b; ... } 2018/12/3 数据结构及其算法 第4章 栈和队列

三种表达式 表达式的相互转换 如4×2+(7-8÷2)转为逆波兰式:42×782÷-+ 逆波兰式中,运算符的出现顺序决定了运算顺序 将中缀表达式转为逆波兰式,可以用操作符栈来实现 对逆波兰式求值,可以用操作数栈来实现 表达式 前缀(prefix)表达式 中缀(infix)表达式 后缀(postfix)表达式 别名 波兰式(Polish notation) 逆波兰式(Reverse Polish notation) 特点 运算符在操作数之前 运算符在操作数中间 需要括号表示优先级 运算符在操作数之后 例 *a+bc a*(b+c) abc+* 2018/12/3 数据结构及其算法 第4章 栈和队列

逆波兰式求值(算法4.14) 例:42×782÷-+ 出现操作数则入栈 出现运算符则出栈两个数,运算结果入栈 OPND OPND OPND 3 4 8 8 8 11 2018/12/3 数据结构及其算法 第4章 栈和队列

中缀表达式转为逆波兰式(算法4.15) 例:4×2+(7-8÷2) 出现操作数则直接输出 出现运算符则与栈顶元素比较优先级 ) # ÷ - OPTR OPTR OPTR 4 2 × 7 8 2 ÷ - + 2018/12/3 数据结构及其算法 第4章 栈和队列

4.4 队列的基本概念 队列(queue):只允许在表的一端插入元素、而在 另一端删除元素的线性表 插入端:队尾(rear, back) 同样是操作受限的线性表 插入端:队尾(rear, back) 删除端:队头(front) 先进先出(first in first out, FIFO) 2018/12/3 数据结构及其算法 第4章 栈和队列

队列应用举例 若队列中数据元素有优先级,则称优先队列 银行排队系统(客户取号、柜台叫号) 计算机操作系统中的多任务调度 2018/12/3 数据结构及其算法 第4章 栈和队列

双端队列(double-ended queue, deque):只 允许插入和删除操作在表的两端进行的线性表 栈、队列都是双端队列的特例 C++中提供stack、queue、deque三个类 2018/12/3 数据结构及其算法 第4章 栈和队列

4.5 队列表示与实现 链队列:队列的链式存储结构 头指针 – 指向附设头结点,便于删除元素 尾指针 – 便于插入元素 typedef LinkList QueuePtr; typedef struct { QueuePtr front; // 头指针 QueuePtr rear; // 尾指针 } LinkQueue; 2018/12/3 数据结构及其算法 第4章 栈和队列

链队列基本操作的实现 构造空队列 判断队列是否为空 void InitQueue_L(LinkQueue &Q) { Q.front = Q.rear = new LNode; Q.front->next = NULL; } bool QueueEmpty_L(LinkQueue Q) { return Q.front == Q.rear; // 或者 return Q.front->next == NULL; } 2018/12/3 数据结构及其算法 第4章 栈和队列

入队 出队 void EnQueue_L(LinkQueue &Q, ElemType e) { QueuePtr p = new LNode; p->data = e; p->next = NULL; Q.rear->next = p; Q.rear = p; } bool DeQueue_L(LinkQueue &Q, ElemType &e) { if (QueueEmpty_L(Q)) return false; QueuePtr p = Q.front->next; e = p->data; Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; // 注意:队列变空特殊处理 delete p; return true; } 2018/12/3 数据结构及其算法 第4章 栈和队列

简单照搬顺序表的实现方法,造成存储空间浪费, 移动元素太耗时 解决方法:假想存储空间为“环形” 顺序队列:队列的顺序存储结构 头指针指向队首元素 约定:尾指针指向队尾元素的下一个位置 简单照搬顺序表的实现方法,造成存储空间浪费, 移动元素太耗时 解决方法:假想存储空间为“环形” 2018/12/3 数据结构及其算法 第4章 栈和队列

循环队列:用假想的“环形”存储空间表示的队列 实际存储空间为线性,“环形”是通过计算来模拟 用长度为R的数组来表示环,则某一位置x的下一位置为 (x+1)%R 两个位置x和y之间的距离为 (y-x+R)%R 2018/12/3 数据结构及其算法 第4章 栈和队列

循环队列的问题:如何区分队空和队满 解决方案:少用一个元素空间,约定 “头指针在尾指针的下一位置”表示队满 “头尾相等”表示队空 2018/12/3 数据结构及其算法 第4章 栈和队列

循环队列的实现 循环队列基本操作的实现 构造空队列 typedef struct { ElemType *elem; // 基地址 int queuesize; // 当前分配内存大小 int front; int rear; } SqQueue; void InitQueue_sq(SqQueue &Q, int msize) { Q.elem = new ElemType[msize]; Q.queuesize = msize; Q.front = Q.rear = 0; } 2018/12/3 数据结构及其算法 第4章 栈和队列

求队列长度 入队 出队 int QueueLength_sq(SqQueue Q) { return (Q.rear - Q.front + Q.queuesize) % Q.queuesize; } void EnQueue_sq(SqQueue &Q, ElemType e) { if ((Q.rear+1) % Q.queuesize == Q.front) Increment(Q); Q.elem[Q.rear] = e; Q.rear = (Q.rear+1) % Q.queuesize; } bool DeQueue_sq(SqQueue &Q, ElemType &e) { if (Q.front == Q.rear) return false; e = Q.elem[Q.front]; Q.front = (Q.front+1) % Q.queuesize; return true; } 2018/12/3 数据结构及其算法 第4章 栈和队列

思考:还有哪些方法可以实现循环队列,避免队空 和队满的混淆问题? 方法1:设变量length表示队列长度 方法2:设标志位full表示是否队满 方法3:设标志位empty表示是否队空 typedef struct { ElemType *elem; // 基地址 int queuesize; // 当前分配内存大小 int front, rear, length; // 保留front或者rear之一即可 } SqQueue; 2018/12/3 数据结构及其算法 第4章 栈和队列

4.6 队列的应用 例:打印二项式系数 常规算法(使用两个辅助数组) void yanghui(int n) { int *k_line = new int[n+1], *kp_line = new int[n+1]; k_line[0] = 1; k_line[1] = 1; int k = 1; while (k <= n) { for (int i=0; i<n-k; ++i) cout << ' '; for (int i=0; i<k+1; ++i) cout << k_line[i] << ' '; cout << endl; if (k == n) break; kp_line[0] = 1; for (int i=1; i<k+1; ++i) kp_line[i] = k_line[i-1] + k_line[i]; kp_line[k+1] = 1; int *p = kp_line; kp_line = k_line; k_line = p; ++k; } 2018/12/3 数据结构及其算法 第4章 栈和队列

算法4.27(使用队列) void yanghui_queue(int n) { SqQueue Q; InitQueue_sq(Q, n+3); EnQueue_sq(Q, 0); EnQueue_sq(Q, 1); EnQueue_sq(Q, 1); int k = 1; int s, e; while (k < n) { for (int i=0; i<n-k; ++i) cout << ' '; EnQueue_sq(Q, 0); do { DeQueue_sq(Q, s); GetHead_sq(Q, e); if (e) cout << e << ' '; else cout << endl; EnQueue_sq(Q, s+e); } while (e); ++k; } DeQueue_sq(Q, e); while (QueueLength_sq(Q) > 0) { DeQueue_sq(Q, e); cout << e << ' '; cout << endl; 2018/12/3 数据结构及其算法 第4章 栈和队列

仿真(simulation)是一类重要的计算机应用,可 以辅助系统设计与优化 数据结构:客户、银行窗口、排队 例:银行排队系统仿真 仿真(simulation)是一类重要的计算机应用,可 以辅助系统设计与优化 数据结构:客户、银行窗口、排队 typedef struct { char *name; // 客户姓名 int arrival; // 客户到达银行的时间 int service_time; // 客户办理业务所需时间 } customer;   enum status {idle, busy}; status cur_status; // 窗口当前状态:忙or闲 int cur_start_time; // 当前状态开始的时间 int cur_customer; // 当前服务客户的编号(如果在忙) } window; void bank_simulation(int, customer*, int, window*); 2018/12/3 数据结构及其算法 第4章 栈和队列

void bank_simulation(int nc, customer *cust, int nw, window *win) { const int TIME = 60; int t = 0; int c, w; int idw = nw; SqQueue Q; InitQueue_sq(Q, 100); cout << "Bank open now" << endl; ... // 所有窗口状态初始化为idle while (t < TIME || QueueLength_sq(Q) > 0 || idw < nw) { if (t < TIME) for (c=0; c<nc; ++c) if (cust[c].arrival == t) EnQueue_sq(Q, c); for (w=0; w<nw; ++w) if (win[w].cur_status == busy) { c = win[w].cur_customer; if (t - win[w].cur_start_time == cust[c].service_time) { win[w].cur_status = idle; win[w].cur_start_time = t; ++idw; }} for (w=nw-1; w>=0; --w) { if (win[w].cur_status == idle && QueueLength_sq(Q) > 0) { DeQueue_sq(Q, c); --idw; ... // 将窗口状态改为busy ++t;} cout << "Bank close now" << endl; } 2018/12/3 数据结构及其算法 第4章 栈和队列

4.7 递归及其应用 程序设计中如何实现函数调用? 后调用,先返回——使用栈 int main() { int s = first(1,2,3); printf("%d\n", s); } int first(int a, int b, int c) { return a + second(b, c); } int second(int x, int y) { return x - third(y); } int third(int z) { return z * z; } 后调用,先返回——使用栈 2018/12/3 数据结构及其算法 第4章 栈和队列

函数调用的执行过程 系统工作栈:用于函数调用的栈结构 栈中的数据元素为函数的工作状态 以递归函数为例,解释系统工作栈原理 调用之前,将调用函数的工作状态(当前位置、当前变量 等)入栈保存 调用:形实结合、转移到被调用函数并执行 调用之后:出栈恢复调用函数的工作状态,继续执行调用 函数 系统工作栈:用于函数调用的栈结构 栈中的数据元素为函数的工作状态 当前位置、当前变量等 以递归函数为例,解释系统工作栈原理 2018/12/3 数据结构及其算法 第4章 栈和队列

递归函数:(直接或间接)调用自身的函数 很多数学函数是用递归的方式定义的 递归函数的正确性判断:有限步终止 如斐波那契数列 int fib(unsigned int n) { if (n <= 1) return n; else return fib(n-1) + fib(n-2); } int fib(unsigned int n) { return fib(n-1) + fib(n-2); } 要理解递归,先要理解递归 int fib(unsigned int n) { if (n <= 1) return n; else return fib(n+2) - fib(n+1); } 2018/12/3 数据结构及其算法 第4章 栈和队列

Hanoi塔问题 最简问题:将1个盘子由X移到Z 问题规约:将n个盘子由X移到Z 算法4.31 将n-1个盘子由X移到Y 将盘子n由X移到Z 将n-1个盘子由Y移到Z 算法4.31 void hanoi(int n, char x, char y, char z) { if (n==1) move(x,1,z); else { hanoi(n-1, x, z, y); move(x,n,z); hanoi(n-1, y, x, z); } void move(char u, int i, char v) { printf("move %d from %c to %c", i, u, v); } 2018/12/3 数据结构及其算法 第4章 栈和队列

系统工作栈示例 int main() { hanoi(3, 'A', 'B', 'C'); 0 } 1 void hanoi(int n, char x, char y, char z) { 2 if (n==1) 3 move(x,1,z); 4 else { 5 hanoi(n-1, x, z, y); 6 move(x,n,z); 7 hanoi(n-1, y, x, z); 8 } } 返回位置 当前变量 n=3,x=A,y=B,z=C 返回位置 当前变量 6 n=2,x=A,y=C,z=B n=3,x=A,y=B,z=C 返回位置 当前变量 6 n=1,x=A,y=B,z=C n=2,x=A,y=C,z=B n=3,x=A,y=B,z=C 系统工作栈示例 2018/12/3 数据结构及其算法 第4章 栈和队列

系统工作栈示例 int main() { hanoi(3, 'A', 'B', 'C'); 0 } 1 void hanoi(int n, char x, char y, char z) { 2 if (n==1) 3 move(x,1,z); 4 else { 5 hanoi(n-1, x, z, y); 6 move(x,n,z); 7 hanoi(n-1, y, x, z); 8 } } 返回位置 当前变量 n=3,x=A,y=B,z=C 返回位置 当前变量 6 n=2,x=A,y=C,z=B n=3,x=A,y=B,z=C 返回位置 当前变量 8 n=1,x=C,y=A,z=B 6 n=2,x=A,y=C,z=B n=3,x=A,y=B,z=C 系统工作栈示例 2018/12/3 数据结构及其算法 第4章 栈和队列

系统工作栈示例 int main() { hanoi(3, 'A', 'B', 'C'); 0 } 1 void hanoi(int n, char x, char y, char z) { 2 if (n==1) 3 move(x,1,z); 4 else { 5 hanoi(n-1, x, z, y); 6 move(x,n,z); 7 hanoi(n-1, y, x, z); 8 } } 返回位置 当前变量 n=3,x=A,y=B,z=C 返回位置 当前变量 8 n=2,x=B,y=A,z=C n=3,x=A,y=B,z=C 返回位置 当前变量 6 n=1,x=B,y=C,z=A 8 n=2,x=B,y=A,z=C n=3,x=A,y=B,z=C 系统工作栈示例 2018/12/3 数据结构及其算法 第4章 栈和队列

系统工作栈示例 int main() { hanoi(3, 'A', 'B', 'C'); 0 } 1 void hanoi(int n, char x, char y, char z) { 2 if (n==1) 3 move(x,1,z); 4 else { 5 hanoi(n-1, x, z, y); 6 move(x,n,z); 7 hanoi(n-1, y, x, z); 8 } } 返回位置 当前变量 n=3,x=A,y=B,z=C 返回位置 当前变量 8 n=2,x=B,y=A,z=C n=3,x=A,y=B,z=C 返回位置 当前变量 8 n=1,x=A,y=B,z=C n=2,x=B,y=A,z=C n=3,x=A,y=B,z=C 系统工作栈示例 2018/12/3 数据结构及其算法 第4章 栈和队列

时间复杂度分析 void hanoi(int n, char x, char y, char z) { if (n==1) move(x,1,z); else { hanoi(n-1, x, z, y); move(x,n,z); hanoi(n-1, y, x, z); } ℎ(𝑛)= 𝑛=1时 2 𝑛≠1时 2+2ℎ(𝑛−1) h(n)=O(2n) 2018/12/3 数据结构及其算法 第4章 栈和队列

当递归函数计算复杂度过高时,应考虑不用递归的 替代方案 递归函数的特点 优点 结构清晰、可读性好 正确性容易证明 缺点 时间复杂度高(递归自身特性、函数调用) 空间复杂度高(系统工作栈) 递归函数可以用栈改写 当递归函数计算复杂度过高时,应考虑不用递归的 替代方案 如:计算斐波那契数列的非递归算法 2018/12/3 数据结构及其算法 第4章 栈和队列

算法4.32 N皇后问题的递归算法 思考:对比13页算法,修改程序找出第一组解就返回 void queen_recur(int i, int N, bool *A, bool *B, bool *C, int *result) { for (int j = 0; j < N; ++j) { if (place(i, j, A, B, C)) { mark(i, j, true, A, B, C); result[i] = j; if (i==N-1) { for (int k=0; k<N; ++k) cout << result[k] << ' '; cout << endl; } else queen_recur(i+1, N, A, B, C, result); mark(i, j, false, A, B, C);} void queen2(int N) { // 递归算法求解N皇后问题 bool *A = new bool[N], *B = new bool[2*N-1], *C = new bool[2*N-1]; for (int i=0; i<N; ++i) A[i] = false; for (int i=0; i<2*N-1; ++i) B[i] = false; for (int i=0; i<2*N-1; ++i) C[i] = false; int *res = new int[N]; queen_recur(0, N, A, B, C, res); 思考:对比13页算法,修改程序找出第一组解就返回 2018/12/3 数据结构及其算法 第4章 栈和队列

本章复习提纲 栈和队列的基本概念 栈和队列的表示与实现 栈的应用 系统工作栈、递归函数、递归与栈的等价性 顺序栈、链栈、循环队列、链队列 基本操作的实现 栈的应用 表达式求值(三种表达式) 系统工作栈、递归函数、递归与栈的等价性 2018/12/3 数据结构及其算法 第4章 栈和队列

作业 4.2, 4.6, 4.8, 4.11, 4.14, 4.16 4.2 出栈序列 4.6 判断逆序序列算法 4.8 转逆波兰式算法 4.11 循环队列算法 4.14 递归转非递归算法 4.16 递归单链表逆序算法 2018/12/3 数据结构及其算法 第4章 栈和队列