1、算法设计与分析,唐光海20052006学年第一学期,本课程主要介绍在实践应用中行之有效的解决非数值数据处理问题的若干种经典的计算机算法,以帮助学生提高运用计算机解决实际问题的能力。教材: 邹海明、余祥宣编计算机算法基础(第2版) 华中科技大学出版社。,目 录,第一章 导引与基本数据结构 第二章 分治法 第三章 贪心方法 第四章 动态规划 第五章 基本检索与周游方法 第六章 回溯法 第七章 分枝限界法 第八章 NP难度和NP完全的问题 第九章 并行算法,第一章 导引与基本数据结构,1.1 算法,一、基本概念,算法:解决某一特定问题的一组有穷的、有效的运算规则。,1. 确定性:每一条规则(每一步)
2、都有确切的含义。 2. 能行性:每一步都是可行的,可在有限的时间内完成。 3. 输 入:一个算法有零个或多个输入。 4. 输 出:一个算法产生一个或多个输出。 5. 有穷性:一个算法总是在执行了有穷步的运算之后终止。,二、算法的五个基本特点,是否有穷是算法与计算过程的主要区别。,由于算法要投入到计算机上运行,所以应考虑算法的效率,考虑的算法应在相当有穷步内终止。,三、拟制算法的步骤,2. 表示算法:用一种语言描述。如类C语言。,1. 设计算法:分析问题、考虑问题的解决方法。,3. 确认算法:证明算法对所有可能的合法输入都能得出正确结果。 通常用测试的方法证明。,4. 分析算法:对算法的时空复杂
3、度作定量的分析,以便了解算法 的性能,了解算法的最佳适用环境,在同一问题的多种算法中选择一种最有效的算法。,5. 测试程序: 调试:在抽象数据集上执行程序,以确定是否会产生错误的结果,若有,则修改程序。 作时空分布图:用各种给定的数据执行调试没有出错的程序,测定花费的时间与空间,以印证以前所作的分析是否正确,指出实现最优化的有效逻辑位置。,四、评价一个算法的标准,2. 易读性:通俗易懂、易交流。,1. 正确性:能正确解决所给定的问题。 无语法与逻辑错误; 给定一组正确的数据有正确的结果; 任意给定一组有刁难性的正确数据都有正确的结果; 对所有的正确数据都能得出正确的结果。,3. 健壮性(容错性
4、):输入非法数据能识别并能作出反应或进行处理。,4. 易编码、易实现。,5. 省时、效率高。,6. 节省空间。,1.2 分析算法,分析算法的好坏,促使我们去设计一些更好的算法,以收到少花钱多办事、办好事的效果,提高经济效益。,假设算法实现时使用的是一台“通用”计算机:每次执行程序中的一条指令;有足够的RAM;在固定时间内可以把一个数从任一单元取出或存入。,一、分析算法的前提,1. 确定使用哪些运算以及执行这些运算所用的时间。, 基本运算:加、减、乘、除、比较、赋值、过程调用。每种运算只花费一个固定量的时间(设为一个单位时间)。, 由基本运算的任意长序列组成的运算:时间的长短取决于序列的长短。,
5、2. 确定能反映出算法在各种情况下工作的数据集,以了解算法的性能:假设数据为n个。,二、算法分析方法,1. 事后测试: 收集算法的执行时间和实际占用空间的统 计资料,作出时空分布图。,受到的影响:,2. 事前分析:求出算法的一个时间限界函数,即时间复杂度 (数量级)。,机器环境,语言及相应的编译系统,问题的规模,时钟的准确性,概念:一条语句的时间复杂度执行它的频率一个算法的时间复杂度算法中所有语句的执行频率之和,定义:设T(n)为算法的执行时间函数,f(n)为n的某个简单函数,若存在常数C 0,自然数 n0,使得对于任意自然数n n0,均有 |T(n)|C|f(n)|则称算法的执行时间数量级为
6、(f(n),或称T(n)与 f(n)是同数量级的,或称算法具有(f(n)的计算时间。记为 T(n) =(f(n),要确定一个算法的时间数量级是很复杂的,经常不能写成一个简单函数,可用下述定理简化。,常用的指数时间算法:(2n ) (n!) (nn ),定理: T(n)=aknk + ak-1nk-1 + a1n + a0 = (nk ), 其中 ak 0,算法的分类:a. 多项式时间算法:可用多项式来对其计算时间限界的算法b. 指数时间算法: 计算时间用指数函数限界的算法,常用的多项式时间: (1 ) (logn ) (n ) (n logn ) (n2 ) (n3),1.3 描述算法,描述算
7、法的语言要考虑到两个标准:易读、易实现,用类C语言描述。,1.4 基本数据结构,1. 线性表:由n(0)个同类型的数据元素组成的有限序列,记为:(a1,a2,an)有顺序存储结构和链式存储结构两种常用方式。栈和队列是两种特殊的线性表。,一、栈和队列,2. 栈:限定在表的一端(栈顶)进行插入(进栈)操作和删除(出栈)操作的特殊线性表。又称后进先出表。设置一个栈顶指针指向栈的顶部。,1)顺序栈:,typedef struct nodeElemtype stackm;int top;STACK; STACK s;,进栈:if (s.top=m-1) printf(“栈满”);else s.top+;
8、s.stacks.top=x;return (s);,出栈:if (s.top=-1) printf(“ 栈空”);else y=s.stacks.top;s.top-;return (s);,2)链栈:,typedef struct nodeElemtype data;struct node *link;STACK; STACK *top;,进栈:p=(STACK*)malloc(sizeof(STACK);p-data=x;p-link=top;top=p;return (top);,出栈:if (top=NULL) printf(“ 栈空”);else y=top-data;p=top;
9、 top=top-link;free(p);return (top);,3. 队列:限定在表的一端(队尾)进行插入(入队列)操作,另一端(队首)进行删除(出队列)操作的特殊线性表。又称先进先出表。设置两个指针:一个队首指针指向队列的第一个元素的前一个位置, 一个队尾指针指向队列的最后一个元素的位置。,typedef struct squeueElemtype am;int front,rear;SQUEUE; SQUEUE Q;,1)顺序队列:,入队列:if (Q.rear+1)%m=Q.front) printf(“队满”);else Q.rear =(Q.rear+1)%m;Q.aQ.re
10、ar=x;return (Q);,出队列:if (Q.rear=Q.front) printf(“ 队空”);else y=Q.a(Q.front+1)%m;Q.front=(Q.front+1)%m;return (s);,2)链队列:,typedef struct nodeElemtype data;struct node *link;NODE; typedef struct queueNODE *front, *rear;QUEUE; QUEUE Q;,入队列: p=(NODE*)malloc(sizeof(NODE); p-data=x;p-link=NULL;Q.rear-link=
11、p; Q.rear=p;return (Q);,出队列: if (Q.rear=Q.front) printf(“ 队空”);else p=Q.front-link;y=p-data;Q.front-link=p-link;free(p);return (Q);,二、树型结构,树:一个或多个结点组成的有限集合,由一个唯一的称为根的结点和若干个互不相交的称为根的子树的子集合组成。,结点的度、分支结点、叶结点、树的度、父结点、子结点、结点的层次、树的深度、森林,树的存储结构:见P17图1.12图1.13,1. 二叉树: 1)定义:由n(0)个结点组成的有限集合。当n=0时,称为空二叉树;n0时,由
12、一个唯一的称为根的结点和两个互不相交的分别称为根的左、右子树的子集合组成。,2)性质:一棵二叉树第i层最多有2i-1个结点。深度为k的二叉树最多有2k-1个结点。,满二叉树:深度为k有2k-1个结点的二叉树。 完全二叉树:除最下面一层外都是满的,最下一层的结点都集中在左边的二叉树。,有n个结点的完全二叉树中,对于编号为i的结点: )当i1时,i的父亲为|_i/2_|,否则无父亲; )当2i n时,i的左孩子为2i,否则无左孩子; )当2i+1 n时,i的右孩子为2i+1,否则无右孩子。,4)两种常用的二叉树:堆:最大堆:每个结点的值都不小于其左右孩子的值的完全二叉树。最小堆:每个结点的值都不大
13、于其左右孩子的值的完全二叉树。二分检索树:每个结点的值都大于其左子树中所有结点的值、小于其右子树中所有结点的值的二叉树。,2. 树转换成二叉树 方法:加边:加兄弟之间的边。减边:减去每个结点与第一个孩子结点之外的孩子结点的边。旋转:顺时针旋转450。,三、集合的树表示和不相交集合的合并,用树表示集合,集合中的每个结点均有一个指向其父结点的信息,用序号代表结点,parenti代表结点i的双亲信息,根结点的父结点为0。,1. 简单的合并与查找1)合并:合并根为i、j的两棵树int U(int i, int j)parenti=j;return(j);,时间复杂度为O(1),2)查找:找i 所在的树
14、的根int F(int i) int j;j=i;while (parentj0)j=parentj;return(j);,比较次数为结点i所在的层次。,例:若有n个集合:Si=i,1in(即n棵均只有一个结点的树构成的森林) 依次执行 U(1,2),F(1),U(2,3),F(1), F(1) , U(n-1,n) 时间为 1 + 2 + 1 + 3 + + n-1 + 1=n-1+(2+3+ +n-1)=n-1+n(n-1)/2-1=O(n2),得到的树为:,2. 使用加权规则的合并算法 1)加权规则:若树i的结点数少于树j的结点数,则j成为i的双亲,否则i成为j的双亲(即结点数多的树的根
15、为合并后的树的根)根结点的parent值为结点个数的相反数。,2)算法: int UNION(int i, int j) int x;x=parenti+parentj;if (parentiparentj) parenti=j;parentj=x;return(j);else parentj=i;parenti=x;return(i); ,3. 使用压缩规则的查找算法 1)压缩规则:若j是由i到它的根的路径上的一个结点,则置parentj=rooti,2)算法: int FIND(int i) int j, k, t;j=i;while (parentj0) j=parentj;k=i;wh
16、ile (k!=j) t=parentk;parentk=j;k=t;return(j); ,四、图,1. 概念: 图G =(V,E)其中:V顶点集 EV中的顶点之间的边集,有向图、无向图、子图 网、有向网、无向网 邻接 顶点的度、入度、出度 路径(简单路径)、环(简单环) 连通图、连通子图、连通分量 强连通图、强连通子图、强连通分量 森林,2. 图的存储结构: 1)邻接矩阵:适用于稠密图2)邻接表: 适用于稀疏图,1.5 递归与消去递归,递归的优点:算法简单、正确性容易证明。缺点:实现的代价高(时间、空间)。,一、 递归的例子,斐波那契数列:0,1,1,2,3,5,8,13,定义为: F0=
17、0,F1=1, Fi=Fi-1+Fi-2 i1,算法:int F(int n) if (n=0) return(0);else if (n=1) return(1);else return(F(n-1)+F(n-2);,算法:int GCD(int a, int b) if (b=0) return(a);else return(GCD(b,a%b);,检索:在A0An-1中找x查找成功返回x在A中的位置,否则返回-1。,算法:int search(int i, int j, Elemtype x, Elemtype A ) if (ij) return(-1);else if(Ai=x) r
18、eturn(i);else return(search(i+1,j,x,A);,调用语句为 search(0,n-1,x,A),二 、 消去递归的方法,1. 迭代法,一般地,受限于一个条件B的递归程序P可以表示为基语句Si(不包含P)和P自身的组合 Si,P递归算法的方案为 Pif B Si,P可改写成 Px=x0; while B S;其中S不包含P,方法:利用题给条件求出第1,i个值作为初值赋给局部变量A1,Ai;给循环变量赋初值,确定循环终值;重复下列步骤,直到循环变量的值超过终值为止:)把利用A1,Ai的值按题给条件求出的值赋给中间变量T;)将T, Ai,A2的值依次赋给Ai,A1;)
19、给循环变量增值;此时的局部变量Ai的值即为所求。,例: int F(int n) int i,f1,f2,f;if (n=0) f=0;else if (n=1) f=1;else f1=0;f2=1;i=2;while (i=n) f=f1+f2;f1=f2;f2=f;i+;return (f); ,2. 消去尾递归,递归调用语句是过程或函数的最后一条执行语句的情况。对于尾递归,从调用返回后不需再执行别的语句,返回地址在过程或函数的末尾,外层的实际参数值和返回地址不需要再用,所以不必保存,但编译程序不识别尾递归,仍会把各层的实际参数值和返回地址保存下来而浪费了存储单元。为节省存储单元,可利用
20、循环语句消去尾递归。,方法:将最外层实际参数值赋给局部变量;重复下列步骤,直到原递归条件不满足为止:)执行语句体;)将向里一层的实际参数值赋给局部变量;其中:语句体为原递归算法中除递归调用语句外的所有可执行语句。,3. 利用堆栈消去递归,这种情况下,当向里一层递归运算执行完后返回原调用层时,还要继续执行后面的执行语句,所以,递归调用进入内层时,外层和各形式参数相对应的实参和返回地址要记录下来,以备返回时用。用栈实现。,方法: 设置栈,将最外层的所有实参值、局部变量及返回地址进栈;重复下列步骤,直到栈为空为止:)执行语句体1;)当要进行递归调用时,将栈顶指针加1,将本次递归调用的所有实参值、局部
21、变量及返回地址进栈,然后返) 执行,否则执行);)执行语句体2;)若栈非空,则将栈顶元素出栈,将其值赋给参变量与局部变量,将栈顶指针减1,转至本次返回地址执行(即转)执行)。 其中:语句体1与语句体2分别为递归调用语句前、后的所有可执行语句。,第二章 分治法,2.1 一般方法,将整个问题分成若干个同类型的小问题分而治之。,抽象化描述算法: 函数类型 DANDC(int p, int q) if (small(p,q) return(G(p,q);else m=DIVIDE(p,q);return(COMBINE(DANDC(p,q),DANDC(m+1,q); ,2.2 二分检索,输入n,An
22、,x,在A0An-1中找x的位置,若找到则返回x在数组A中的位置(即下标),若找不到,则返回-1,一、二分检索算法,在数组AlAh中找x:先找中点Am若Am=x,则m 为所求;若Amx,则在AlAm-1中找x;,int BINSRCH (Elemtype A ,int n, Elemtype x) int j,m,l,h;j=-1;l=0;h=n-1;whle (lAm) l=m+1;else j=m; l=h+1; return (j); ,例:P39例2.1,用二叉树描述算法:每个内结点表示一次元素比较,用圆形结点表示;每个外结点表示一种不成功检索的情况,用方结点表示。,二分检索时间:最好
23、 平均 最坏 成功的检索 (1) (log n) (log n)不成功的检索 (log n) (log n) (log n),二、每次循环只用一次比较的二分检索,int BINSRCH1(Elemtype A ,int n, Elemtype x) int j,m,l,h;j=-1;l=0;h=n; /*h总比可能的最大下标大1*/whle (lh-1) m=(l+h)/2;if( xAm) h=m;else l=m;if (x=Al) j=l;else j=0; return (j); ,此算法保证每次循环只需一次比较,将A0An分成A0Am和 AmAn两部分,但当x=Am时还需继续循环(对
24、应的检索树形如B+树),所以,不论什么情况都要等循环结束后才能判定x是否在A中,所以,所有情况的时间复杂度均为(log n)。,三、以比较为基础检索的时间下界,只允许进行元素间的比较而不允许对它们实施运算,在这种条件下设计的算法称为以比较为基础的算法。 以比较为基础的检索算法对应一棵比较树(二叉树),树的高度为比较的最大次数,当比较树每个结点为中点时,树的高度最低(为|_log n_|+1),即比较次数(时间)最小。,结论:二分检索是解决检索问题的最优的最坏情况算法。,2.3 找最大最小元素,在A0An-1中找最大最小元素。,一、直接算法,void straitmaxmin(Elemtype
25、A, int n, Elemtype *max, Elemtype *min) int i;*max=A0; *min=A0;for(i=1;i*max) *max=Ai;else if (Ai*min) *min=Ai;,比较次数为: 最好 最坏 平均n-1 2(n-1) 3(n-1)/2,二、分治策略算法,void MAXMIN(Elemtype A, int i, int j, Elemtype *max, Elemtype *min) int m; Elemtype *gmax,*gmin,*hmax,*hmin;if (i=j-1) if (Ai*hmax) *max=*gmax;e
26、lse *max=*hmax;if (*gmin*hmin) *min=*gmin;else *min=*hmin; ,两个算法平均时间基本相同,而MAXMIN算法要大量进出栈的开销,因而会更慢。,三、结论,2. 若A的元素间的比较远比整数间的比较代价高,则分治方法会产生效率较高(最优)的算法,反之,会得到一个效率较低的算法。,所以,分治策略只能看成是一个较好的然而并不是总能成功的算法设计指导。,2.4 归并分类,void Mergesort(Elemtype A, int l, int h) int m;if (lh)m=(l+h)/2;Mergesort(A,l,m);Mergesort(
27、A,m+1,h);Merge(A,l,m,h);,一、归并分类算法,void Merge (Elemtype A, int l, int m, int h) Elemtype Bmax;int i, j, k, t;i=l; j=m+1; k=l;while (i=m) ,例:P48例2.3,算法的不足之处:当n=2时还要递归调用,浪费时间;附加空间为(n),二、改进的归并分类算法,void Mergesort1 (Elemtype A, int l, int h, int *p) int m, *q,*r;if (h-l+116) Insertionsort1(A,l,h,p);else m
28、=(l+h)/2;Mergesort1(A,l,m,q);Mergesort1(A,m+1,h,r);Merge1(A,q,r,p);,void Merge1(Elemtype A, int *q, int *r, int *p) int i, j, k;i=*q; j=*r; k=0;while (i!=0) ,其中:link数组为全程数组,初值全为0。,void Insertionsort1 (Elemtype A, int l, int h, int *p) int i, j, k;for(i=l; i=h; i+)j=0;k=linkj;while (k!=0) ,三、结论 以比较为基
29、础的分类算法的最坏情况时间下界为(nlog n) 归并分类算法是最坏情况的最优算法。,例:P51例2.4,改进算法的时间复杂度仍为(nlog n),快速分类又称为分区交换分类。其基本思想是:首先将待分类数据序列中的所有数据作为当前待分类区域,从中任选取一个数据(比如第一个),并以该数据为基准,从位于待分类数据序列左右两端开始,逐渐向中间靠拢,交替与基准数据进行比较、交换,每次比较,若遇右侧数据小于基准数据,则将其与基准数据交换,使其移到基准数据的左侧,若遇左侧数据大于基准值,则将其与基准数据交换,使其移至基准数据的右侧,,2.5 快速分类,一、快速分类算法,1. 快速分类的基本思想,最后让基准
30、数据到达它的最终位置,此时,基准数据将待分类数据分成了左右两个区域,位于基准数据左侧的数据都小于或等于基准数据,位于基准数据右侧的所有数据都大于或等于基准数据,这就是一趟快速分类(或称为一次划分);然后分别对左右两个新的待分类区域,重复上述一趟快速分类的过程,其结果分别让左右两个区域中的基准数据都到达它们的最终位置,同时将待分类数据序列分成更小的待分类区域,再次重复对每个区域进行一趟快速分类,直到每个区域只有一个数据为止,此时所有的数据都到达了它的最终位置,即所有待分类数据按升序排列,至此分类结束。,2. 算法: 1)划分算法,int partition (int p,int q ) int
31、i,j; Elemtype v; i=p; j=q; v=ai; while(ij) while (ij ) ,2)快速分类算法void quicksort (int p,int q ) int m;if(pq) m= partition (p,q);quicksort(p,m-1); /*对左侧分区域进行快速排序*/quicksort(m+1,q); /*对右侧分区域进行快速排序*/ ,3. 例:,V k1 k2 k3 k4 k5 k6,V k1 k2 k3,43,28 15 21 28,28,V k1 k2,V k5 k6,15 15 21,二、快速分类算法时间复杂度分析,2.6 选择问题
32、,在A0An-1中找第k小元素。,利用划分算法求出划分元素所在位置m, 若k=m+1,则Am为第k小元素; 若km+1,则第k小元素在Am+1An-1中,继续在Am+1An-1中找第k-m-1小元素。,一、选择问题算法,1. 算法int select (Elemtype A; int n, int k ) int p,q,m;p=0;q=n-1;while (p=q) m= partition (p,q);if (k=m+1) return (m+1);else if (km+1) q=m-1;else p=m+1; ,2. 时间复杂度,二、最坏情况时间为(n)选择算法二次取中值法,2. 使用
33、二次取中值规则的选择算法的说明性描述 见P60算法2.16P62算法2.17,第三章 贪心方法,3.1 一般方法,问题(集合),一、概念,约束条件,可行解,最优解(次优解),满足,目标函数,取极值,贪心方法:是一种改进了的分级处理方法。首先根据题意选取一种量度标准,然后按这种量度标准对这n个输入排序,并按序一次输入一个量,如果这个输入和当前已构成在这种量度意义下的部分最优解加在一起不能产生一个可行解,则不把此输入加到这部分解中,这种能够得到某种量度意义下的最优解的分级处理方法称为贪心方法。,核心:选择能产生问题最优解的最优量度标准。,二、算法,贪心方法的抽象化控制: 函数类型 GREEDY(E
34、lemtype A,int n) solution=;for(i=1;i=n;i+)x=SELECT(A);if FEASIBLE(solution,x)solution=UNION(solution,x);return(solution); ,3.2 背包问题,一、问题,已知有n种物品和一个可容纳M重量的背包,每种物品i的重量为wi。假设将物品i的一部分xi放入背包就会产生pixi的效益(0xi1,pi0),如何装载背包使得装入背包内的物品产生最大的效益?,可行解: (x1,x2,xn),二、例,例3.1 考虑下列情况的背包问题:n=3,M=20,(p1,p2,p3)=(25,24,15),
35、 (w1,w2,w3)=(18,15,10),其中的四个可行解为:(x1,x2,x3) wixi pixi (1/2,1/3,1/4) 16. 5 24.25 (1,2/15,0) 20 28.2 (0,2/3,1) 20 31 (0,1,1/2) 20 31.5,三、量度标准的选择,每一次装入的物品应使它占用的每一单位容量获得当前最大的单位效益。,量度标准:每次装入要使得累计效益值与所占容量的比值有最多的增加(第一次)或最少的减少(第二次及以后的装入)。,方法:按pi/wi的非增次序装入。,四、结论:按pi/wi的非增次序装入可使得pixi取得最大值 (即可得最优解)。,五、算法void G
36、REEDY-KNAPSACK(int P,int W,int M,int X,int n) float PWmax;int cu,i; for(i=1;i=n;i+) PWi=Pi/Wi;quicksort1(PW,1,n); /*将PW数组按非增次序排列*/,for(i=1;icu) break;Xi=1;cu=cu-Wi;if (i=n) Xi=cu/ Wi; ,3.3 带有限期的作业排序,一、问题,约束条件: 被选中的作业均能在其截止期限内完成,可行解:n个作业中满足约束条件的作业组成的子集J,二、例,例3.2 n=4,(p1,p2,p3,p4)=(100,10,15,20), (d1,
37、d2,d3,d4)=(2,1,2,1),可行解为: J 处理顺序 效益值 (1) 1 100 (2) 2 10 (3) 3 15 (4) 4 20 (1,2) 2,1 110 (1,3) 3,1或3,1 115 (1,4) 4,1 120 (2,3) 2,3 25 (3,4) 4,3 35,三、量度标准的选择, J=1;for(i=2;i=n;i+) if (J i的所有作业都能在它们的截止期限前完成)J=J + i;,四、抽象化描述算法 P71算法3.3,五、定理,定理3.2 算法3. 3所描述 的贪心方法对于作业排序问题总是得到一个最优解。,定理3.3 设J是k个作业的集合,=i1i2ik
38、是J中作业的一种排列,它使得di1di2dik。J是一个可行解,当且仅当J中的作业可以按照的次序而不违反任何一个期限的情况来处理。,定理3.3给我们启示:在判断J是否是一个可行解时,只要将J中的作业按截止期限非降次序排列后J中的作业是否都能在其截止期限内完成即可。,六、算法 带有限期和效益的单位时间的作业排序贪心算法,void JS(int d , int J , int n, int *k) int i,j,r;for(i=0;idi) ,七、一种更快的作业排序算法,如果J是作业的可行子集,则使用下列规则来确定这些作业中的每一个作业的处理时间:若还没给作业i分配处理时间,则分配给它时间片-1
39、,,其中 是使得 1 di 的最大整数,且-1, 是空的。,此规则就是尽可能地推迟对作业i的处理。在将作业一个一个地装配到J中时,不必为接纳新作业而去移动J中那些已分配了时间片的作业。若正被考虑的新作业不存在像上面那样定义的,这个作业就不能计入J。,1. 例 设n=5,(p1,p2,p3,p4,p5)=(20,15,10,5,1), (d1,d2,d3,d4,d5)=(2,2,1,3, 3) ,使用上述可行性规则得J 已分配的时间片 正被考虑的作业 动作 无 1 分配1,21 1,2 2 分配0,11,2 0,1,1,2 3 不适合,舍弃1,2 0,1,1,2 4 分配2,31,2,4 0,1
40、,1,2,2,3 5 不适合,舍弃 最优解为J=1,2,4,由于只有n个作业且每个作业花费一个单位时间,故只需考虑这样一些时间片i-1,i,1i b,其中b=minn,maxdi。为简单起见,用i来表示时间片i-1,i。这n个作业的期限值只能是1,2,b中的某些(或全部)元素。把这b 个期限值分成一些集合,对于任一期限值i,设ni是使得nji的最大整数且是空的时间片,即所要处理的作业的期限值如果是i,则当前可分配的最接近的时间片是ni。(为避免极端情况,引进一个虚构的期限值0和时间片-1,0)。当ni=nj时,期限值i和j在同一个集合中,显然,若ij,则i,i+1, ,j都在同一个集合中。,作
41、出一些以期限值为元素的集合,且使同一个集合中的元素都有一个当前共同可用的最接近的空时间片。对于每个期限值i ,0i b,用Fi表示当前最接近的空时间片,即Fi=ni。使用树表示集合,最初,这b+1个期限值最接近的时间片是Fi=i, 0i b,有b+1个集合与这b+1个期限值相对应。如果要调度具有期限d 的作业,则寻找包含期限值minn,d的那个根j,若Fj0,则Fj是最接近的空时间片,在使用了这一时间片后,其根j的集合要与包含期限值Fj-1的集合合并。,2. 算法 void FJS(int d , int n, int b, int J , int *k)int i,j,l;for(i=0;i
42、=n;i+) Fi=i;*k=0;for(i=1;i=n;i+) j=FIND(min(n,di);if (Fj != 0) (*k)+; J*k=i; l=FIND(Fj-1);j=UNION(l,j); Fj=Fl; ,3. 例 P75例3.4,4. 时间分析 最多执行 2n次 FIND,n 次UNION最少执行 n次 FIND,0 次UNION 最坏时间为 O(n)+O(2n(2n,n+1)=O(n)最好时间为 O(n)+O(n(n,1)=O(n),3.4 最优归并模式,一、问题,约束条件: 无,可行解:任一次序归并得到的归并树,归并n个文件x1,x2,xn用外结点表示原始文件,用内结点
43、表示归并后的文件,则得到一棵归并树。,二、例 (q1,q2,q3,q4,q5)=(20,30,10,5,30),对应归并树为:,(q1,q2,q3,q4,q5)=(20,30,10,5,30),三、量度标准的选择,Birtree TREE(Birtree *L,int n)int i; Birtree *T;for(i=1;ilchild=LEAST(L);/*在森林L中找根的权值最小的树*/T-rchild=LEAST(L);T-data= T-lchild-data+ T-rchild-data;INSERT(L,T); /*将树T插入到森森L中*/return(LEAST(L);,四、算
44、法,定理3.4 若L是最初n1棵包含单个结点的树构成的森林,这些树依次有权值为q1,q2,qn,则算法TREE对于具有这些长度的n个文件生成一棵最优的二叉归并树。,五、定理,若用k路归并,则每次选取k棵具有最小长度的子树进行归并。要求最初的文件数n满足条件: n%(k-1)=1 否则要补充若干个长度为0的文件使其满足条件。,3.5 最小生成树,一、问题,求由n个结点组成的图G(V,E)的最小生成树T(V,E )设图G中有m条边,每条边(i,j)上的权值为Cij,约束条件:V(T)=V(G)且T是连通的且有最少的边数,可行解:由n个结点n-1条边组成的树(生成树),二、量度标准的选择,以目标函数
45、作为量度标准。 每次选择使得迄今为止所计入的边的成本之和有最小的增量的那条边。,三、prim算法,使得迄今为止所选择的边的集合A构成一棵树,将要计入到A中的下一条边(u,v)是一条不在A中且使得A(u,v)也是一棵树的最小成本的边。,算法3.7 prim算法: void prim (int COST , int n, int T , int mincost) int i,j,k,min,t,NEAR ;mincost=0;for(i=2;i=n;i+) NEARi=1;NEAR1=0;for(i=1;in;i+) min=MAX;t=0;for(j=1;j=n;j+)if (NEARj!=0)/*设t是NEARj!=0且COSTjNEARj最小的下标*/,