算法导论第三十四章答案
算法和数据结构C语言版课后习题集答案解析(机械工业出版社)第34章习题集参考答案解析
第3章栈和队列一、基础知识题3.1有五个数依次进栈:1,2,3,4,5。
在各种出栈的序列中,以3,4先出的序列有哪几个。
(3在4之前出栈)。
【解答】34215 ,34251, 345213.2铁路进行列车调度时,常把站台设计成栈式结构,若进站的六辆列车顺序为:1,2,3,4,5,6,那么是否能够得到435612, 325641, 154623和135426的出站序列,如果不能,说明为什么不能;如果能,说明如何得到(即写出"进栈"或"出栈"的序列)。
【解答】输入序列为123456,不能得出435612和154623。
不能得到435612的理由是,输出序列最后两元素是12,前面4个元素(4356)得到后,栈中元素剩12,且2在栈顶,不可能让栈底元素1在栈顶元素2之前出栈。
不能得到154623的理由类似,当栈中元素只剩23,且3在栈顶,2不可能先于3出栈。
得到325641的过程如下:1 2 3顺序入栈,32出栈,得到部分输出序列32;然后45入栈,5出栈,部分输出序列变为325;接着6入栈并退栈,部分输出序列变为3256;最后41退栈,得最终结果325641。
得到135426的过程如下:1入栈并出栈,得到部分输出序列1;然后2和3入栈,3出栈,部分输出序列变为13;接着4和5入栈,5,4和2依次出栈,部分输出序列变为13542;最后6入栈并退栈,得最终结果135426。
3.3若用一个大小为6的数组来实现循环队列,且当前rear和front的值分别为0和3,当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为多少?【解答】2和 43.4设栈S和队列Q的初始状态为空,元素e1,e2,e3,e4,e5和e6依次通过栈S,一个元素出栈后即进队列Q,若6个元素出队的序列是e3,e5,e4,e6,e2,e1,则栈S的容量至少应该是多少?【解答】 43.5循环队列的优点是什么,如何判断“空”和“满”。
算法分析课后习题解答
2-34、Gray码是一个长度为2n的序列。
序列中无相同元素。
每个元素都是长度为n位的串。
相邻元素恰好只有一位不同。
用分治策略设计一个算法对任意的n构造相应的Gray码。
答:设序列中元素由0、1组成。
当 n=1 时 Gray码的序列有2个元素〔21=2〕,分别为:0,| 1当 n=2 时 Gray码的序列有4个元素〔22=4〕,分别为:00,10,| 11,01当 n=3 时 Gray码的序列有8个元素〔23=8〕,分别为:000,100,110,010,| 011,111,101,001当 n=4 时 Gray码的序列有16个元素〔24=16〕,分别为:0000,1000、1100、0100,0110,1110,1010,0010,| 0011,1011,1111,0111,0101,1101,1001,0001从上面的列举可得如下规律:n=k时,Gray码的序列有2k个元素,分别为:n=k-1时的Gray码元素正向后加0,得前2k-1个元素,反向后加1的后2k-1个元素。
如 n=2时 Gray码序列的4个元素分别为:00,10, 11,01当 n=3 时 Gray码序列的前4个元素〔23=8〕,分别为:000,100,110,010是n=2时Gray码四个元素正向后加0,即:000,100, 110,010Gray码序列的后4个元素〔23=8〕,分别为:011,111,101,001 是n=2时Gray码四个元素反向后加1,n=2时Gray码四个元素:00,10, 11,01即:011,111,101,001可以看出,Gray码可以用分治策略,递归实现,2n的Gray码可以用2n-1的Gray码构成。
算法描述:void Gray( type a[],int n){ char a[];if (n==1) { a[0]=’0’;a[1]=’1’;}if (n>1){ Gray(a[],n-1);int k=2n-1-1; //Gray码的个数,因为数组下标从0开始int i=k;for (int x=k;x>=0;x--){char y=a[x];a[x]=y+’0’;a[i+1]=y+’1’; i++;}}}3-7 给定由n个英文单词组成的一段文章,……答:设由n 个单词组成的一段文章可以表示为 A[1:n],它的“漂亮打印〞方案记为B[1:n],构成该最优解的最小空格数〔最优值〕记为m[1][n](1)分析最优解的结构:A[1:n]的最优解B[1:n],必然在第k个单词处断开,那么A[1:k]是“漂亮打印〞,并且A[k+1:n]也是“漂亮打印〞。
藏书阁-《算法导论》常见算法总结
常见算法总结分治法分治策略的思想:顾名思义,分治是将一个原始问题分解成多个子问题,而子问题的形式和原问题一样,只是规模更小而已,通过子问题的求解,原问题也就自然出来了。
总结一下,大致可以分为这样的三步:分解:将原问题划分成形式相同的子问题,规模可以不等,对半或2/3对1/3的划分。
解决:对于子问题的解决,很明显,采用的是递归求解的方式,如果子问题足够小了,就停止递归,直接求解。
合并:将子问题的解合并成原问题的解。
这里引出了一个如何求解子问题的问题,显然是采用递归调用栈的方式。
因此,递归式与分治法是紧密相连的,使用递归式可以很自然地刻画分治法的运行时间。
所以,如果你要问我分治与递归的关系,我会这样回答:分治依托于递归,分治是一种思想,而递归是一种手段,递归式可以刻画分治算法的时间复杂度。
所以就引入本章的重点:如何解递归式?分治法适用的情况分治法所能解决的问题一般具有以下几个特征:1. 该问题的规模缩小到一定的程度就可以容易地解决2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3. 利用该问题分解出的子问题的解可以合并为该问题的解;4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
——————————————————————————————最大堆最小堆1、堆堆给人的感觉是一个二叉树,但是其本质是一种数组对象,因为对堆进行操作的时候将堆视为一颗完全二叉树,树种每个节点与数组中的存放该节点值的那个元素对应。
《算法导论(第二版)》(中文版)课后答案
17.3-1
11
《算法导论(第二版) 》参考答案
17.3-4
17.4-3 假设第 i 个操作是 TABLE_DELETE, 考虑装载因子 : i =(第 i 次循环之后的表 中的 entry 数)/(第 i 次循环后的表的大小)= numi / sizei
19.1-1. If x is not a root node, then Degree[x]=Degree[sibling[x]]+1 If x is a root node, then Degree[x]<Degree[sibling[x]] 19.1-2
13.1-5 prove:
3
《算法导论(第二版) 》参考答案 13.1-6 2k-1 22k-1 13.2-3 13.3-5
13.4-3
4
《算法导论(第二版) 》参考答案
14.1-4
14.2-2
14.2-3 不可以,性能改变 时间复杂度由 O( lgn ) -> O( nlgn )
14.3-2 Note: 注意 Overlap 的定义稍有不同,需要重新定义。 算法:只要将 P314 页第三行的 改成>就行。 14.3-3 INTERVAL-SEARCH-SUBTREE(x, i) 1 while x ≠ nil[T] and i does not overlap int[x] 2 do if left[x] ≠ nil[T] and max[left[x]] ≥ low[i] 3 then x ← left[x] 4 else x ← right[x] 5 return x INTERVAL-SEARCH-MIN(T, i) 2 y←INTERVAL-SEARCH-SUBTREE(root[T], i) 先找第一个重叠区间 3 z←y 4 while y≠ nil[T] and left[y] ≠ nil[T] 在它的左子树上查找
《算法导论》习题答案
n/2
n! nn , n! o(nn )
3.2.4 是否多项式有界 lg n !与 lg lg n !
设lgn=m,则 m! 2 m ( )m e2m ( )m em(ln m1) mln m1 nln ln n
∴lgn!不是多项式有界的。
T (n) O(lg n)
4.1.2 证明 T (n) 2T (n) n 的解为 O(n lg n)
设 T (n) c n lg n
T (n) 2c n lg n n c lg n n n c(n 1) lg(n / 2) n cn lg n c lg n cn n cn(lg n 1) n c(lg n 2n)
虽然用二分查找法可以将查找正确位置的时间复杂度降下来,但 是移位操作的复杂度并没有减少, 所以最坏情况下该算法的时间复杂 度依然是 (n2 )
2.3-7 给出一个算法, 使得其能在 (n lg n) 的时间内找出在一个 n 元
素的整数数组内,是否存在两个元素之和为 x
首先利用快速排序将数组排序,时间 (n lg n) ,然后再进行查找:
sin(n / 2) 2 1,所以 af (n / b) cf (n) 不满足。 2(sin n 2)
4.1.6 计算 T (n) 2T (
令 m lg n, T (2 ) 2T (2
m m/ 2
n ) 1 的解
) 1
令 T(n)=S(m),则 S (m) 2S (m / 2) 1 其解为 S (m) (m),T (n) S (m) (lg n)
4.2 The recursion-tree method 4.2.1 4.2.2 4.2.3 4.2.5 略
算法导论习题
//在合并排序中对小数组采用插入排序#include<stdio.h>void main(){void MERGE_SORT(int a[],int p,int r,int k);int a[12]; //={3,0,1,10,9,5,4,12,7,8,2,6};int k=3,n=12;int i,j,s;int tmp=0;printf("请输入12个正整数:\n");for(i=0;i<12;i++)scanf("%d",&a[i]);for(i=0;i<=3;i++){for(j=i*3+1;j<i*3+3;j++){s=j;while(s>=i*3+1){if(a[s]<a[s-1])tmp=a[s],a[s]=a[s-1],a[s-1]=tmp;s--;}}}printf("第一步对4个长度3的子列表进行插入排序的结果为:\n");for(i=0;i<12;i++)printf("%d,",a[i]);printf("\n");MERGE_SORT(a,0,3,3);printf("第二步对4个子列表进行合并排序的结果为:\n");for(i=0;i<12;i++)printf("%d,",a[i]);printf("\n");} void MERGE_SORT(int a[],int p,int r,int k) {void MERGE(int a[],int p,int q,int r,int k);int q;if(p<r){q=(p+r)/2;MERGE_SORT(a,p,q,k);MERGE_SORT(a,q+1,r,k);MERGE(a,p,q,r,k);}}void MERGE(int a[],int p,int q,int r,int k) {int n1=(q-p+1)*k,n2=(r-q)*k;int i,j,s;int *L=new int[n1];int *R=new int[n2];for(i=0;i<n1;i++)L[i]=a[p*k+i];for(j=0;j<n2;j++)R[j]=a[(q+1)*k+j];i=0;j=0;for(s=p*3;s<=(r+1)*3-1;s++){if(i>n1-1)a[s]=R[j++];else if(j>n2-1)a[s]=L[i++];else if(L[i]<R[j])a[s]=L[i++];elsea[s]=R[j++];}}//用分治法在数组中查找逆序对#include<stdio.h>void main(){int count_inversion(int a[],int p,int r);int a[5]={5,4,3,2,1};printf("数组的逆序对是%d个\n",count_inversion(a,0,4));}int merge_inversion(int a[],int p,int q,int r){int n1=q-p+1;int n2=r-q;int *L=new int[n1];int *R=new int[n2];int i,j,k,v;for(i=0;i<n1;++i)L[i]=a[p+i];for(j=0;j<n2;++j)R[j]=a[q+1+j];i=0;j=0;v=0;for(k=p;k<=r;++k){if(i>n1-1)a[k]=R[j++];else if(j>n2-1)a[k]=L[i++];else if(L[i]>R[j]){a[k]=R[j++];v+=n1-i;}elsea[k]=L[i++];}delete L;delete R;return v;}int count_inversion(int a[],int p,int r){int v=0,q;if(p<r){q=(p+r)/2;v+=count_inversion(a,p,q);v+=count_inversion(a,q+1,r);v+=merge_inversion(a,p,q,r);}return v;}//用插入方法建堆#include"stdio.h"void HEAP_INCREASE_KEY(int a[],int i,int key) {int tmp;if(key>a[i-1])a[i-1]=key;while(i>1&&a[i/2-1]<a[i-1]){tmp=a[i/2-1],a[i/2-1]=a[i-1],a[i-1]=tmp;i=i/2;}}void MAX_HEAP_INSERT(int a[],int key,int heap_size){heap_size+=1;a[heap_size-1]=0;HEAP_INCREASE_KEY(a,heap_size,key); }void BUILD_MAX_HEAP(int a[],int lengh){int heap_size=1;int i;for(i=2;i<=lengh;i++){MAX_HEAP_INSERT(a,a[i-1],heap_size);heap_size++; //堆的长度要随着循环的次数增长}}void main(){int j;int a[10]={15,84,62,16,29,35,6,18,9,17};BUILD_MAX_HEAP(a,10);for(j=0;j<10;j++)printf("%d\n",a[j]);}#include"stdio.h"void MAX_D_HEAPIFY(int a[],int i,int d,int heap_size){int n=d,j,largest;int tmp;int *child=new int[n];for(j=0;j<n;j++)child[j]=(i-1)*d+2+j;if(child[0]<=heap_size&&a[child[0]-1]>a[i-1])largest=child[0];elselargest=i;for(j=1;j<n;j++){if(child[j]<=heap_size&&a[child[j]-1]>a[largest-1])largest=child[j];}if(largest!=i){tmp=a[largest-1],a[largest-1]=a[i-1],a[i-1]=tmp;MAX_D_HEAPIFY(a,largest,d,heap_size);}}void BUILD_MAX_D_HEAP(int a[],int d,int heap_size){int i,j;j=heap_size%d;if(j==0||j==1)i=heap_size/d;elsei=heap_size/d+1;//由叶子节点求父节点有两种情况for(i;i>=1;i--)MAX_D_HEAPIFY(a,i,d,heap_size);}int EXTRACT_MAX(int a[],int d,int heap_size) {int tmp;tmp=a[heap_size-1];a[heap_size-1]=a[0];a[0]= tmp;heap_size--;MAX_D_HEAPIFY(a,1,d,heap_size);return a[heap_size];}void main(){inta[20]={52,47,16,58,23,26,14,18,59,68,47,19,35,29, 61,82,74,75,98,81};// int b[18]={25,11,15,9,8,17,21,40,18,11,10,20,14,15,19, 21,7,10};int d=5,j,largest;BUILD_MAX_D_HEAP(a,5,20);// BUILD_MAX_D_HEAP(b,5,18);for(j=0;j<20;j++)printf("%d\n",a[j]);largest=EXTRACT_MAX(a,5,20);for(j=0;j<20;j++)printf("%d\n",a[j]);printf("%d\n",largest);/* for(j=0;j<18;j++)printf("%d\n",b[j]);*/#include"stdio.h"void MAX_D_HEAPIFY(int a[],int i,int d,int heap_size){int n=d,j,largest;int tmp;int *child=new int[n];for(j=0;j<n;j++)child[j]=(i-1)*d+2+j;if(child[0]<=heap_size&&a[child[0]-1]>a[i-1])largest=child[0];elselargest=i;for(j=1;j<n;j++){if(child[j]<=heap_size&&a[child[j]-1]>a[largest-1])largest=child[j];}if(largest!=i){tmp=a[largest-1],a[largest-1]=a[i-1],a[i-1]=tmp;MAX_D_HEAPIFY(a,largest,d,heap_size);}}void BUILD_MAX_D_HEAP(int a[],int d,int heap_size){int i,j;j=heap_size/d;if(j==0||j==1)i=heap_size/d;elsei=heap_size/d+1;//由叶子节点求父节点有两种情况for(i;i>=1;i--)MAX_D_HEAPIFY(a,i,d,heap_size);}void HEAP_INCREASE_KEY(int a[],int i,int d,int key){int tmp,j;if(a[i-1]<=key)a[i-1]=key;while(i>1){if(i%d==0||i%d==1)j=i/d;elsej=i/d+1;if(a[j-1]<a[i-1]){tmp=a[j-1],a[j-1]=a[i-1],a[i-1]=tmp;i=j;}else break;}}void INSERT(int a[],int key,int d,int heap_size) {heap_size+=1;a[heap_size-1]=0;HEAP_INCREASE_KEY(a,heap_size,d,key); }void main(){inta[20]={52,47,16,58,23,26,14,18,59,68,47,19,35,29, 61,82,74,75,98,81};int j,s=0;BUILD_MAX_D_HEAP(a,5,19);for(j=0;j<20;j++){printf("%d,",a[j]);s+=1;if(s%6==0)printf("\n");}INSERT(a,a[19],5,19);s=0;printf("\n");for(j=0;j<20;j++){printf("%d,",a[j]);s+=1;if(s%6==0)printf("\n");}}#include"stdio.h"void MAX_D_HEAPIFY(int a[],int i,int d,int heap_size){int n=d,j,largest;int tmp;int *child=new int[n];for(j=0;j<n;j++)child[j]=(i-1)*d+2+j;if(child[0]<=heap_size&&a[child[0]-1]>a[i-1])largest=child[0];elselargest=i;for(j=1;j<n;j++){if(child[j]<=heap_size&&a[child[j]-1]>a[largest-1])largest=child[j];}if(largest!=i){tmp=a[largest-1],a[largest-1]=a[i-1],a[i-1]=tmp;MAX_D_HEAPIFY(a,largest,d,heap_size);}}void BUILD_MAX_D_HEAP(int a[],int d,int heap_size){int i,j;j=heap_size/d;if(j==0||j==1)i=heap_size/d;elsei=heap_size/d+1;//由叶子节点求父节点有两种情况for(i;i>=1;i--)MAX_D_HEAPIFY(a,i,d,heap_size);}void HEAP_DECREASE_KEY(int a[],int i,int d,int key,int heap_size){if(a[i-1]>=key)a[i-1]=key;MAX_D_HEAPIFY(a,i,d,heap_size);}void main(){inta[20]={52,47,16,58,23,26,14,18,59,68,47,19,35,29, 61,82,74,75,98,81};int key=1,s=0,j;BUILD_MAX_D_HEAP(a,5,20);for(j=0;j<20;j++){printf("%d,",a[j]);s+=1;if(s%6==0)printf("\n");}printf("\n");s=0;HEAP_DECREASE_KEY(a,3,5,key,20);for(j=0;j<20;j++){printf("%d,",a[j]);s+=1;if(s%6==0)printf("\n");}}#include"stdio.h"void main(){//int a[10]={6,4,12,7,9,11,5,13,18,8};int a[10]={20,18,17,14,16,10,8,9,8,15};int i=9,j;int heap_size;void BULD_MAX_HEAP(int a[]);int HEAP_DELETE(int a[],int i);BULD_MAX_HEAP(a);heap_size=HEAP_DELETE(a,i);printf("删除第i个元素后的堆是:\n");for(j=0;j<heap_size;j++)printf("%d\n",a[j]);}void BULD_MAX_HEAP(int a[]){void MAX_HEAPIFY(int a[],int j,int heap_size);int heap_size=10;int j;for(j=4;j>=0;j--)MAX_HEAPIFY(a,j,heap_size);printf("堆a是:\n");for(j=0;j<heap_size;j++)printf("%4d",a[j]);printf("\n");}void MAX_HEAPIFY(int a[],int j,int heap_size) {int left=2*(j+1);int right=2*(j+1)+1;//结点与数组下标之间要转换int largest=0,temp;if(left<=heap_size&&a[left-1]>a[j])largest=left-1;elselargest=j;if(right<=heap_size&&a[right-1]>a[largest])largest=right-1;if(largest!=j){temp=a[largest];a[largest]=a[j];a[j]=temp;MAX_HEAPIFY(a,largest,heap_size);}}int HEAP_DELETE(int a[],int i){int temp,key=a[9],key1=a[i-1];int heap_size=10;temp=a[9];a[9]=a[i-1];a[i-1]=temp;heap_size--;if(key>key1){while(i>1&&a[i/2-1]<a[i-1])//如果a[9]大于i结点的值,则通过不断与父结点的比较//来确它的位置{temp=a[i/2-1],a[i/2-1]=a[i-1],a[i-1]=temp;i=i/2;}}elseMAX_HEAPIFY(a,i-1,heap_size);//如果a[9]比i结点的值要小,则从i结点开始堆维护return heap_size;}//建立最小堆#include"stdio.h"void MIN_HEAPIFY(int a[],int i,int heap_size){int small,tmp;int left=2*i,right=2*i+1;if(left<=heap_size&&a[left-1]>a[i-1])small=left;elsesmall=i;if(right<=heap_size&&a[right-1]>a[small-1]) small=right;if(small!=i){tmp=a[small-1],a[small-1]=a[i-1],a[i-1]=tmp;MIN_HEAPIFY(a,small,heap_size);}}void BUILD_MIN_HEAP(int a[],int heap_size) {int i;for(i=(heap_size/2);i>=1;i--)MIN_HEAPIFY(a,i,heap_size);}void HEAPSORT(int a[],int lengh){int i,tmp;int heap_size=lengh;BUILD_MIN_HEAP(a,heap_size);for(i=lengh;i>=2;i--){tmp=a[i-1],a[i-1]=a[0],a[0]=tmp;heap_size--;MIN_HEAPIFY(a,1,heap_size);}}void main(){int a[10]={23,6,21,3,7,5,8,54,14,10};int i;HEAPSORT(a,10);for(i=0;i<10;i++)printf("%d\n",a[i]);}//PARTITION的最初版本#include"stdio.h" int HOARE_PARTITION(int a[],int p,int r){int x,tmp;int i,j;x=a[p-1];i=p-1;j=r+1;while(1){while(a[--j-1]>x);while(a[++i-1]<x);if(i<j)tmp=a[i-1],a[i-1]=a[j-1],a[j-1]=tmp;elsereturn j;}}void QUICK_SORT(int a[],int p,int r){int q;if(p<r){q=HOARE_PARTITION(a,p,r);QUICK_SORT(a,p,q);QUICK_SORT(a,q+1,r);}}void main(){int i;inta[20]={10,58,46,23,26,48,47,59,68,23,12,19,17,24, 43,81,76,72,98,46};QUICK_SORT(a,1,20);for(i=0;i<20;i++)printf("%d\n",a[i]);/*对插入排序来说,当其输入已“几乎”排好序时,运行时间是很快的。
《算法导论》读书笔记(一)
《算法导论》读书笔记(⼀) 本章是本书的开篇,介绍了什么是算法,为什么要学习算法,算法在计算机中的地位及作⽤。
算法(algorithm)简单来说就是定义良好的计算机过程,它取⼀个或⼀组值作为输⼊,并产⽣出⼀个或⼀组值作为输出。
即算法就是⼀系列的计算步骤,⽤来将输⼊数据转换成输出数据。
书中有⼀句话⾮常好: Having a solid base of algorithm knowledge and technique is one characteristic that separates the truly skilled programmers from the novices. 是否具有扎实的算法知识和技术基础,是区分真正熟练的程序员与新⼿的⼀项重要特征。
以这句话激励⾃⼰要努⼒学习算法,夯实基础,成为真正熟练的程序员。
本章通过介绍插⼊排序和归并排序两种常见的排序算法来说明算法的过程及算法分析,在介绍归并排序算法过程中引⼊了分治(divide-and-conquer)算法策略。
1、插⼊排序 输⼊:n个数(a1,a2,a3,...,an) 输出:输⼊序列的⼀个排列(a1',a2',a3',...an')使得(a1'≤a2'≤a3'≤...≤an')。
插⼊排序的基本思想是:将第i个元素插⼊到前⾯i-1个已经有序的元素中。
具体实现是从第2个元素开始(因为1个元素是有序的),将第2个元素插⼊到前⾯的1个元素中,构成两个有序的序列,然后从第3个元素开始,循环操作,直到把第n元素插⼊到前⾯n-1个元素中,最终使得n个元素是有序的。
该算法设计的⽅法是增量⽅法。
书中给出了插⼊排序的为代码,并采⽤循环不变式证明算法的正确性。
我采⽤C 语⾔实插⼊排序,完整程序如下:1 void insert_sort(int *datas,int length)2 {3 int i,j;4 int key,tmp;5 //判断参数是否合法6 if(NULL == datas || 0==length)7 {8 printf("Check datas or length.\n");9 exit(1);10 }11 //数组下标是从0开始的,从第⼆个元素(对应下标1)开始向前插⼊12 for(j=1;j<length;j++)13 {14 key = datas[j]; //记录当前要插⼊的元素15 i = j-1; //前⾯已经有序的元素16 //寻找待插⼊元素的位置,从⼩到到排序,如果是从⼤到⼩改为datas[i]<key17 while(i>=0 && datas[i] > key)18 {19 /×tmp = datas[i+1];20 datas[i+1] = datas[i];21 datas[i] = tmp;×/ 这个过程不需要进⾏交换,因为要插⼊的值保存在key中,没有被覆盖掉,在此感谢”两⽣花“指出问题所在datas[i+1] = datas[i];22 i--; //向前移动23 }24 datas[i+1] = key; //最终确定待插⼊元素的位置25 }26 }插⼊排序算法的分析 算法分析是对⼀个算法所需的资源进⾏预测,资源是指希望测度的计算时间。
算法导论标准答案
算法导论标准答案————————————————————————————————作者:————————————————————————————————日期:2第二章算法入门由于时间问题有些问题没有写的很仔细,而且估计这里会存在不少不恰当之处。
另,思考题2-3 关于霍纳规则,有些部分没有完成,故没把解答写上去,我对其 c 问题有疑问,请有解答方法者提供个意见。
给出的代码目前也仅仅为解决问题,没有做优化,请见谅,等有时间了我再好好修改。
插入排序算法伪代码INSERTION-SORT(A)1 for j ←2 to length[A]2 do key ←A[j]3 Insert A[j] into the sorted sequence A[1..j-1]4 i ←j-15 while i > 0 and A[i] > key6 do A[i+1]←A[i]7 i ←i − 18 A[i+1]←keyC#对揑入排序算法的实现:public static void InsertionSort<T>(T[] Input) where T:IComparable<T>{T key;int i;for (int j = 1; j < Input.Length; j++){key = Input[j];i = j - 1;for (; i >= 0 && Input[i].CompareTo(key)>0;i-- )Input[i + 1] = Input[i];Input[i+1]=key;}}揑入算法的设计使用的是增量(incremental)方法:在排好子数组A[1..j-1]后,将元素A[ j]揑入,形成排好序的子数组A[1..j]这里需要注意的是由于大部分编程语言的数组都是从0开始算起,这个不伪代码认为的数组的数是第1个有所丌同,一般要注意有几个关键值要比伪代码的小1.如果按照大部分计算机编程语言的思路,修改为:INSERTION-SORT(A)1 for j ← 1 to length[A]2 do key ←A[j]3 i ←j-112 31 41 59 26 41 584 while i ≥ 0 and A[i] > key5 do A[i+1]←A[i]6 i ← i − 17A[i+1]←key循环丌变式(Loop Invariant)是证明算法正确性的一个重要工具。
算法导论第三版答案
Procedure BINARY-SEARCH takes a sorted array A, a value , and a range Œlow : : high of the array, in which we search for the value . The procedure compares to the array entry at the midpoint of the range and decides to eliminate half the range from further consideration. We give both iterative and recursive versions, each of which returns either an index i such that AŒi D , or NIL if no utions for Chapter 2: Getting Started
AŒlow : : high contains the value . The initial call to either version should have the parameters A; ; 1; n.
Selected Solutions for Chapter 2: Getting Started
2-3
d. We follow the hint and modify merge sort to count the number of inversions in ‚.n lg n/ time.
To start, let us define a merge-inversion as a situation within the execution of merge sort in which the MERGE procedure, after copying AŒp : : q to L and AŒq C 1 : : r to R, has values x in L and y in R such that x > y. Consider an inversion .i; j /, and let x D AŒi and y D AŒj , so that i < j and x > y. We claim that if we were to run merge sort, there would be exactly one mergeinversion involving x and y. To see why, observe that the only way in which array elements change their positions is within the MERGE procedure. Moreover, since MERGE keeps elements within L in the same relative order to each other, and correspondingly for R, the only way in which two elements can change their ordering relative to each other is for the greater one to appear in L and the lesser one to appear in R. Thus, there is at least one merge-inversion involving x and y. To see that there is exactly one such merge-inversion, observe that after any call of MERGE that involves both x and y, they are in the same sorted subarray and will therefore both appear in L or both appear in R in any given call thereafter. Thus, we have proven the claim.
算法导论 第三版 第34章 答案 英
Chapter34Michelle Bodnar,Andrew LohrApril12,2016Exercise34.1-1Showing that LONGEST-PATH-LENGTH being polynomial implies that LONGEST-PATH is polynomial is trivial,because we can just compute the length of the longest path and reject the instance of LONGEST-PATH if and only if k is larger than the number we computed as the length of the longest path.Since we know that the number of edges in the longest path length is between 0and|E|,we can perform a binary search for it’s length.That is,we constructan instance of LONGEST-PATH with the given parameters along with k=|E|2.If we hear yes,we know that the length of the longest path is somewhere above the halfway point.If we hear no,we know it is somewhere below.Since each time we are halving the possible range,we have that the procedure can require O(lg(|E|))many steps.However,running a polynomial time subroutine lg(n) many times still gets us a polynomial time procedure,since we know that with this procedure we will never be feeding output of one call of LONGEST-PATH into the next.Exercise34.1-2The problem LONGST-SIMPLE-CYCLE is the relation that associates each instance of a graph with the longest simple cycle contained in that graph.The decision problem is,given k,to determine whether or not the instance graph has a simple cycle of length at least k.If yes,output1.Otherwise output0. The language corresponding to the decision problem is the set of all G,k such that G=(V,E)is an undirected graph,k≥0is an integer,and there exists a simple cycle in G consisting of at least k edges.Exercise34.1-3A formal encoding of the adjacency matrix representation is tofirst encode an integer n in the usual binary encoding representing the number of vertices. Then,there will be n2bits following.The value of bit m will be1if there is an edge from vertex m/n to vertex(m%n),and zero if there is not such an edge.1An encoding of the adjacency list representation is a bit morefinessed.We’ll be using a different encoding of integers,call it g(n).In particular,we will place a0immediately after every bit in the usual representation.Since this only doubles the length of the encoding,it is still polynomially related.Also, the reason we will be using this encoding is because any sequence of integers encoded in this way cannot contain the string11and must contain at least one zero.Suppose that we have a vertex with edges going to the vertices in-dexed by i1,i2,i3,...,i k.Then,the encoding corresponding to that vertex is g(i1)11g(i2)11···11g(i k)1111.Then,the encoding of the entire graph will be the concatenation of all the encodings of the vertices.As we are reading through, since we used this encoding of the indices of the vertices,we won’t ever be con-fused about where each of the vertex indices end,or when we are moving on to the next vertex’s list.To go from the list to matrix representation,we can read offall the adjacent vertices,store them,sort them,and then output a row of the adjacency matrix. Since there is some small constant amount of space for the adjacency list repre-sentation for each vertex in the graph,the size of the encoding blows up by at most a factor of O(n),which means that the size of the encoding overall is at most squared.To go in the other direction,it is just a matter of keeping track of the posi-tions in a given row that have ones,encoding those numerical values in the way described,and doing this for each row.Since we are only increasing the size of the encoding by a factor of at most O(lg(n))(which happens in the dense graph case),we have that both of them are polynomially related.Exercise34.1-4This isn’t a polynomial-time algorithm.Recall that the algorithm from Exercise16.2-2had running timeΘ(nW)where W was the maximum weight supported by the knapsack.Consider an encoding of the problem.There is a polynomial encoding of each item by giving the binary representation of its index,worth,and weight,represented as some binary string of length a=Ω(n). We then encode W,in polynomial time.This will have lengthΘ(lg W)=b. The solution to this problem of length a+b is found in timeΘ(nW)=Θ(a∗2b). Thus,the algorithm is actually exponential.Exercise34.1-5We show thefirst half of this exercise by induction on the number of times that we call the polynomial time subroutine.If we only call it zero times,all we are doing is the polynomial amount of extra work,and therefore we have that the whole procedure only takes polynomial time.Now,suppose we want to show that if we only make n+1calls to the polynomial time subroutine.Consider the execution of the program up until just before the last call.At this point,by the inductive hypothesis,we have only taken a polynomial amount of time.This means that all of the data that2we have constructed so far fits in a polynomial amount of space.This meansthat whatever argument we pass into the last polynomial time subroutine willhave size bounded by some polynomial.The time that the last call takes is thenthe composition of two polynomials,and is therefore a polynomial itself.So,since the time before the last call was polynomial and the time of the last callwas polynomial,the total time taken is polynomial in the input.This provesthe claim of the first half of the input.To see that it could take exponential time if we were to allow polynomi-ally many calls to the subroutine,it suffices to provide a single example.Inparticular,let our polynomial time subroutine be the function that squares itsinput.Then our algorithm will take an integer x as input and then squareit lg(x )many times.Since the size of the input is lg(x ),this is only linearlymany calls to the subroutine.However,the value of the end result will bex 2lg(x )=x x =2x lg(x )=2lg(x )2lg(x )∈ω(22lg(x )).So,the output of the functionwill require exponentially many bits to represent,and so the whole programcould not of taken polynomial time.Exercise 34.1-6Let L 1,L 2∈P .Then there exist algorithms A 1and A 2which decide L 1and L 2in polynomial time.We will use these to determine membership in thegiven languages.An input x is in L 1∪L 2if and only if either A 1or A 2returns 1when run on input x .We can check this by running each algorithm separately,each in polynomial time.To check if x is in L 1∩L 2,again run A 1and A 2,and return 1only if A 1and A 2each return 1.Now let n =|x |.For i =1to n ,check if the first i bits of x are in L 1and the last n −i bits of x arein L 2.If this is ever true,then x ∈L 1L 2so we return 1.Otherwise return0.Each check is performed in time O (n k )for some k ,so the total runtime isO (n (n k +n k ))=O (n k +1)which is still polynomial.To check if x ∈L 1,runA 1and return 1if and only if A 1returns 0.Finally,we need to determine ifx ∈L ∗1.To do this,for i =1to n ,check if the first i bits of x are in L 1,and thelast n −i bits are in L ∗1.Let T (n )denote the running time for input of size n ,and let cn k be an upper bound on the time to check if something of length n is in L 1.Then T (n )≤ n i =1cn k T (n −i ).Observe that T (1)≤c since a single bitis in L ∗1if and only if it is in L 1.Now suppose T (m )≤c m k .Then we have:T (n )≤cn k +1+n −1i =0c i k ≤cn k +1+c n k +1=O (n max k,k).Thus,by induction,the runtime is polynomial for all n .Since we have exhib-ited polynomial time procedures to decide membership in each of the languages,they are all in P ,so P is closed under union,intersection,concatenation,com-plement,and Kleene star.Exercise 34.2-13To verify the language,we should let the certificate be the mapping f fromthe vertices of G 1to the vertices of G 2that is an isomorphism between thegraphs.Then all the verifier needs to do is verify that for all pairs of verticesu and v ,they are adjacent in G 1if and only if f (u )is adjacent to f (v )in G 2.Clearly it is possible to produce an isomorphism if and only if the graphs areisomorphic,as this is how we defined what it means for graphs to be isomorphic.Exercise 34.2-2Since G is bipartite we can write its vertex set as the disjoint union S T ,where neither S nor T is empty.Since G has an odd number of vertices,ex-actly one of S and T has an odd number of vertices.Without loss of generality,suppose it is S .Let v 1,v 2,...,v n be a simple cycle in G ,with v 1∈S .Since nis odd,we have v 1∈S ,v 2∈T ,...,v n −1∈T ,v n ∈S .There can be no edgebetween v 1and v n since they’re both in S ,so the cycle can’t be Hamiltonian.Exercise 34.2-3Suppose that G is hamiltonian.This means that there is a hamiltonian cycle.Pick any one vertex v in the graph,and consider all the possibilities of deletingall but two of the edges passing through that vertex.For some pair of edgesto save,the resulting graph must still be hamiltonian because the hamiltoniancycle that existed originally only used two edges.Since the degree of a vertexis bounded by the number of vertices minus one,we are only less than squaring that number by looking at all pairs ( n −12 ∈O (n 2)).This means that we areonly running the polynomial tester polynomially many independent times,sothe runtime is polynomial.Once we have some pair of vertices where deletingall the others coming offof v still results in a hamiltonian graph,we will re-member those as special,and ones that we will never again try to delete.Werepeat the process with both of the vertices that are now adjacent to v ,testinghamiltonicity of each way of picking a new vertex to save.We continue in thisprocess until we are left with only |V |edge,and so,we have just constructed ahamiltonian cycle.Exercise 34.2-4This is much like Exercise 34.1-6.Let L 1and L 2be languages in NP,withverification algorithms A 1and A 2.Given input x and certificate y for a languagein L 1∪L 2,we define A 3to be the algorithm which returns 1if either A 1(x,y )=1or A 2(x,y )=1.This is a polynomial verification algorithm for L 1∪L 2,so NP isclosed under unions.For intersection,define A 3to return 1if and only if A 1(x,y )and A 2(x,y )return 1.For concatenation,define A 3to loop through i =1to n ,checking each time if A 1(x [1..i ],y [1..i ])=1and A 1(x [i +1..n ],y [i +1..n ])=1.Ifso,terminate and return 1.If the loop ends,return 0.This still takes polynomialtime,so NP is closed under concatenation.Finally,we need to check Kleene star.Define A 3to loop through i =1to n ,each time checking if A 1(x [1..i ],y [1..i ])=1,4and y [i +1..n ]is a certificate for x [i +1..n ]being in L ∗1.Let T (n )denote therunning time for input of size n ,and let cn k be an upper bound on the time to verify that y is a certificate for x .Then T (n )≤ n i =1cn k T (n −i ).Observe thatT (1)≤c since we can verify a certificate for a problem of length 1in constant time.Now suppose T (m )≤c m k .Then we have:T (n )≤cn k +1+n −1i =0c i k ≤cn k +1+c n k +1=O (n max k,k).Thus,by induction,the runtime of A 3is polynomial.Note that we onlyneeded to deal with the runtime recursion with respect to the length of x ,sinceit is assumed |y |=O (|x |c )for some constant c .Therefore NP is closed underKleene star.A proof for closure under complement breaks down,however.If a certificatey is given for input x and A 1(x,y )returns false,this doesn’t tell us that y is acertificate for x being in the complement of L 1.It merely tells us that y didn’tprove x ∈L 1.Exercise 34.2-5Suppose that we know that the length of the certificates to the verifier arebounded by n k −1,we know it has to be bounded by some polynomial becausethe verifier can only look at polynomially many bits because it runs in polyno-mial time.Then,we try to run the verifier on every possible assignment to eachbit of the certificates of length up to that much.Then,the runtime of this willbe a polynomial times 2n k −1which is little oh of 2n k .Exercise 34.2-6The certificate in this case would be a list of vertices v 1,v 2,...,v n ,startingwith u and ending with v ,such that each vertex of G is listed exactly once and(v i ,v i +1)∈E for 1≤i ≤n −1.Since we can check this in polynomial time,HAM-PATH belongs to NP.Exercise 34.2-7For a directed acyclic graph,we can compute the topological sort of thegraph by the method of section 22.4.Then,looking at this sorting of thevertices,we say that there is a Hamiltonian path if as we read offthe vertices,each is adjacent to the next,and they are not if there is any pair of vertices sothat one is not adjacent to the next.If we are in the case that each is adjacent to the next,then the topologicalsort itself gives us the Hamiltonian path.However,if there is any pair of verticesso that one is not adjacent to the next,this means that that pair of vertices donot have any paths going from one to the other.This would clearly imply that5there was no Hamiltonian path,because the Hamiltonian path would be going from one of them to the other.To see the claim that a pair of vertices u and v that are adjacent in a topo-logical sort but are not adjacent have no paths going from one to the other, suppose to a contradiction there were such a path from u to v.If there were any vertices along this path they would have to be after u since they are descendants of u and they would have to be before v because they are ancestors of v.This would contradict the fact that we said u and v were adjacent in a topological sort.Then the path would have to be a single edge from u to v,but we said that they weren’t adjacent,and so,we have that there is no such path.Exercise34.2-8Let L be the complement of TAUTOLOGY.Then L consists of all boolean formulasφsuch that there exists an assignment y1,y2,...,y k of0and1to the input variables which causesφto evaluate to0.A certificate would be such an assignment.Since we can check what an assignment evaluates to in polynomial time in the length of the input,we can verify a certificate in polynomial time. Thus,L ∈NP which implies TAUTOLOGY∈co-NP.Exercise34.2-9A language is in coNP if there is a procedure that can verify that an input is not in the language in polynomial time given some certificate.Suppose that for that language we a procedure that could compute whether an input was in the language in polynomial time receiving no certificate.This is exactly what the case is if we have that the language is in P.Then,we can pick our procedure to verify that an element is not in the set to be running the polynomial time procedure and just looking at the result of that,disregarding the certificate that is given.This then shows that any language that is in P is in coNP,giving us the inclusion that we wanted.Exercise34.2-10Suppose NP=co-NP.Let L∈NP\co-NP.Since P⊂NP∩co-NP,and L/∈NP∩co-NP,we have L/∈P.Thus,P=NP.Exercise34.2-11As the hint suggests,we will perform a proof by induction.For the base case,we will have3vertices,and then,by enumeration,we can see that the only Hamiltonian graph on three vertices is K3.For any connected graph on three vertices,the longest the path connecting them can be is2edges,and so we will have G3=K3,meaning that the graph G was Hamiltonian.Now,suppose that we want to show that the graph G on n+1vertices has the property that G3is Hamiltonian.Since the graph G is connected we know6that there is some spanning tree by Chapter23.Then,let v be any internal vertex of that tree.Suppose that if we were to remove the vertex v,we would be splitting up the original graph in the connected components V1,V2,...,V k, sorted in increasing order of size.Suppose that thefirst 1of these components have a single vertex.Suppose that thefirst 2of these components have fewer than3vertices.Then,let v i be the vertex of V i that is adjacent to v in the tree.For i> 1,let x i be any vertex of V i that is distance two from the ver-tex v.By induction,we have Hamiltonian cycles for each of the componentsV2+1,...,V k.In particular,there is a Hamiltonian path from v i to x i.Then,for each i and j there is an edge from x j to v i,because there is a path of length three between them passing through v.This means that we can string together the Hamiltonian paths from each of the components with i> stly,since V1,...,V all consist of single vertices that are only distance one from v,they are all adjacent in G3.So,after stringing together the Hamiltonian paths fori> 1,we just visit all of the single vertices in v1,v2,...,v1in order,then,goto v and then to the vertex that we started this path at,since it was selected to be adjacent to v,this is possible.Since we have constructed a Hamiltonian cycle,we have completed the proof.Exercise34.3-1The formula infigure34.8b is((x1∨x2)∧(¬(¬x3)))∧(¬(¬x3)∨((x1)∧(¬x3)∧(x2)))∧((x1)∧(¬x3)∧(x2)) We can cancel out the double negation to get that this is the same expression as((x1∨x2)∧(x3))∧((x3)∨((x1)∧(¬x3)∧(x2)))∧((x1)∧(¬x3)∧(x2)) Then,thefirst clause can only be true if x3is true.But the last clause can only be true if¬x3is true.This would be a contradiction,so we cannot have both thefirst and last clauses be true,and so the boolean circuit is not satis-fiable since we would be taking the and of these two quantities which cannot both be true.Exercise34.3-2Suppose L1≤P L2and let f1be the polynomial time reduction function such that x∈L1if and only if f1(x)∈L2.Similarly,suppose L2≤P L3and let f2be the polynomial time reduction function such that x∈L2if and only if f2(x)∈L3.Then we can compute f2◦f1in polynomial time,and x∈L1if and only if f2(f1(x))∈L3.Therefore L1≤P L3,so the≤P relation is transitive.7Exercise34.3-3Supposefirst that we had some polynomial time reduction from L to¯L.This means that for every x there is some f(x)so that x∈L ifff(x)∈¯L.This means that x∈¯L iffx∈L ifff(x)∈¯L ifff(x)∈L.So,our poly-time computable function for the reduction is the same one that we had from L≤P¯L.We can do an identical thing for the other direction.Exercise34.3-4We could have instead used as a certificate a satisfying assignment to the input variables in the proof of Lemma34.5.We construct the two-input,poly-nomial time algorithm A to verify CIRCUIT-SAT as follows.Thefirst input is a standard encoding of a boolean combinatiorial circuit C,and the second is a satisfying assignment of the input variables.We need to compute the output of each logic gate until thefinal one,and then check whether or not the out-put of thefinal gate is1.This is more complicated than the approach taken in the text,because we can only evaluate the output of a logic gate once we have successfully determined all input values,so the order in which we examine the gates matters.However,this can still be computed in polynomial time by essentially performing a breath-first search on the circuit.Each time we reach a gate via a wire we check whether or not all of its inputs have been computed. If yes,evaluate that gate.Otherwise,continue the search tofind other gates, all of whose inputs have been computed.Exercise34.3-5We do not have any loss of generality by this assumption.This is because since we bounded the amount of time that the program has to run to be polynomial, there is no way that the program can access more than an polynomial amount of space.That is,there is no way of moving the head of the turning machine further than polynomially far in only polynomial time because it can move only a single cell at a time.Exercise34.3-6Suppose that∅is complete for P.Let L={0,1}∗.Then L is clearly in P,and there exists a polynomial time reduction function f such that x∈∅if and only if f(x)∈L.However,it’s never true that x∈∅,so this means it’s never true that f(x)∈L,a contradiction since every input is in L.Now suppose{0,1}∗is complete for P,let L =∅.Then L is in P and there ex-ists a polynomial time reduction function f .Then x∈{0,1}∗if and only if f (x)∈L .However x is always in{0,1}∗,so this implies f (x)∈L is always true,a contradiction because no binary input is in L .Finally,let L be some language in P which is not∅or{0,1}∗,and let L be any other language in P.Let y1/∈L and y2∈L .Since L∈P,there8exists a polynomial time algorithm A which returns0if x/∈L and1if x∈L. Define f(x)=y1if A(x)returns0and f(x)=y2if A(x)returns1.Then f is computable in polynomial time and x∈L if and only if f(x)∈L .Thus, L ≤P L.Exercise34.3-7Since L is in NP,we have that¯L∈coNP because we could just run our verification algorithm to verify that a given x is not in the complement of L, this is the same as verifying that x is in L.Since every coNP language has its complement in NP,suppose that we let S be any language in coNP and let¯S be its compliment.Suppose that we have some polynomial time reduction f from¯S∈NP to L.Then,consider using the same reduction function.We will have that x∈S iffx∈¯S ifff(x)∈L ifff(x)∈¯L.This shows that this choice of reduction function does work.So,we have shown that the compliment of any NP complete problem is also NP complete.To see the other direction,we just negate everything,and the proof goes through identically.Exercise34.3-8To prove that a language L is NP-hard,one need not actually construct the polynomial time reduction algorithm F to compute the reduction function f for every L ∈NP.Rather,it is only necessary to prove that such an algorithm exists.Thus,it doesn’t matter that F doesn’t know A.If L is in NP,we know that A exists.If A exists,dependent on k and that big-oh constant,we know that F exists and runs in polynomial time,and this is sufficient to prove CIRCUIT-SAT is NP-hard.Exercise34.4-1Suppose that it is a circuit on two inputs,and then,we have n rounds of two and gates each,both of which take both of the two wires from the two gates from the previous round.Since the formulas for each round will consist of two copies of the formulas from the previous round,it will have an exponential size formula.Exercise34.4-2To make this more readable,we’ll justfind the3-CNF formula for each term listed in the AND of clauses forφ on page1083,including the auxiliary variables p and q as necessary.9y=(y∨p∨q)∧(y∨p∨¬q)∧(y∨¬p∨q)∧(y∨¬p∨¬q)(y1↔(y2∧¬x2))=(¬y1∨¬y2∨¬x2)∧(¬y1∨y2∨¬x2)∧(¬y1∨y2∨x2)∧(y1∨¬y2∨x2) (y2↔(y3∨y4))=(¬y2∨y3∨y4)∧(y2∨¬y3∨¬y4)∧(y2∨¬y3∨y4)∧(y2∨y3∨¬y4) (y3↔(x1→x2))=(¬y3∨¬x2∨x2)∧(y3∨¬x1∨¬x2)∧(y1∨x1∨¬x2)∧(y3∨x1∨x2) (y4↔¬y5)=(¬x4∨¬y5∨q)∧(¬x4∨¬y5∨¬p)∧(x4∨y5∨p)∧(x4∨y5∨¬p) (y5↔(y6∨x4))=(¬y5∨y6∨x4)∧(y5∨¬y6∨¬x4)∧(y5∨¬y6∨x4)∧(y5∨y6∨¬x4) (y6↔(¬x1↔x3))=(¬y6∨¬x1∨¬x3)∧(¬y6∨x1∨x3)∧(y6∨¬x1∨x3)∧(y6∨x1∨¬x3).Exercise34.4-3The formula could haveΩ(n)free variables,then,the truth table corre-sponding to this formula would a number of rows that isΩ(2n)since it needs toconsider every possible assignment to all the variables.This then means thatthe reduction as described is going to incrase the size of the problem exponen-tially.Exercise34.4-4To show that the language L=TAUTOLOGY is complete for co-NP,it willsuffice to show that L is NP-complete,where L is the set of all boolean formu-las for which there exists an assignment of input variables which makes it false.We showed in Exercise34.2-8that TAUTOLOGY∈co-NP,which implies L∈NP.Thus,we need only show that L is NP-hard.We’ll give a polynomial timereduction from the problem of determining satisfiability of boolean formulas todetermining whether or not a boolean formula fails to be a tautology.In partic-ular,given a boolean formulaφ,the negation ofφhas a satisfying assignmentif and only ifφhas an assignment which causes it to evaluate to0.Thus,ourfunction will simply negate the input formula.Since this can be done in poly-nomial time in the length of the input,boolean satisfiability is polynomial timereducible to L.Therefore L is NP-complete.By Exercise34.3-7,this implies TAUTOLOGY is complete for co-NP.Exercise34.4-5Since the problem is in disjunctive normal form,we can write it as∨iφiwhere eachφi looks like the and of a bunch of variables and their negations.Then,we know that the formula is satisfiable if and only if any one if theφi aresatisfiable.If aφi contains both a variable and its negation,then it is clearly notsatisfiable,as one of the two must be false.However,if each variable showingup doesn’t have its negation showing up,then we can just pick the appropriatevalue to assign to each variable.This is a property that can be checked in lineartime,by just keeping two bit vectors of length equal to the number of variables,one representing if the variable has shown up negated and one for if the variable10has shown up without having been negated.Exercise34.4-6Let A denote the polynomial time algorithm which returns1if input x is a satisfiable formula,and0otherwise.We’ll define an algorithm A to give a satisfying assignment.Let x1,x2,...,x m be the input variables.In polynomial time,A computes the boolean formula x which results from replacing x1with true.Then,A runs A on this formula.If it is satisfiable,then we have re-duced the problem tofinding a satisfying assignment for x with input variables x2,...,x m,so A recursively calls itself.If x is not satisfiable,then we set x1 to false,and need tofind a satisfying assignment for the formula x obtained by replacing x1in x by false.Again,A recursively calls itself to do this.If m=1,A takes a polynomial-time brute force approach by evaluating the for-mula when x m is true,and when x m is false.Let T(n)denote the runtime of A on a formula whose encoding is of length n.Note that we must have m≤n. Then we have T(n)=O(n k)+T(n )for some k and n =|x |,and T(1)=O(1). Since we make a recursive call once for each input variable there are m recursive calls,and the input size strictly decreases each time,so the overall runtime is still polynomial.Exercise34.4-7Suppose that the original formula was∧i(x i∨y i),and the set of variables were {a i}.Then,consider the directed graph which has a vertex corresponding both to each variable,and each negation of a variable.Then,for each of the clauses x∨y,we will place an edge going from¬x to y,and an edge from¬y to x.Then, anytime that there is an edge in the directed graph,that means if the vertex the edge is coming from is true,the vertex the edge is going to has to be true. Then,what we would need to see in order to say that the formula is satisfiable is a path from a vertex to the negation of that vertex,or vice versa.The naive way of doing this would be to run all pairs shortest path,and see if there is a path from a vertex to its negation.This however takes time O(n2lg(n)),and we were charged with making the algorithm as efficient as possible.First,run the procedure for detecting strongly connected components,which takes linear time.For every pair of variable and negation,make sure that they are not in the same strongly connected component.Since our construction was symmet-ric with respect to taking negations,if there were a path from a variable to its negation,there would be a path going from its negation to itself as well. This means that we would detect any path from a variable to its negation,just by checking to see if they are contained in the same connected component or not. Exercise34.5-1To do this,first,notice that it is in NP,where the certificate is just the injection from G1into G2so that G1is isomorphic to its image.Now,to see that it is NP complete,we will do a reduction to clique.That11。
算法第四版习题答案解析
算法第四版习题答案解析1.1.1 给出以下表达式的值:a. ( 0 + 15 ) / 2b. 2.0e-6 * 100000000.1c. true && false || true && true答案:a.7,b.200.0000002 c.ture1.1.2 给出以下表达式的类型和值:a. (1 + 2.236)/2b. 1 + 2 + 3 + 4.0c. 4.1 >= 4d. 1 + 2 + "3"答案:a.1.618 b. 10.0 c.true d.331.1.3 编写一个程序,从命令行得到三个整数参数。
如果它们都相等则打印equal,否则打印not equal。
public class TestUqual{public static void main(String[] args){int a,b,c;a=b=c=0;StdOut.println("Please enter three numbers");a =StdIn.readInt();b=StdIn.readInt();c=StdIn.readInt();if(equals(a,b,c)==1){StdOut.print("equal");}else{StdOut.print("not equal");}}public static int equals(int a ,int b , int c){if(a==b&&b==c){return 1;}else{return 0;}}}1.1.4 下列语句各有什么问题(如果有的话)?a. if (a > b) then c = 0;b. if a > b { c = 0; }c. if (a > b) c = 0;d. if (a > b) c = 0 else b = 0;答案:a. if (a > b) c = 0; b. if (a > b) { c = 0; }1.1.5 编写一段程序,如果double 类型的变量x 和y 都严格位于0 和1 之间则打印true,否则打印false。
算法导论中文版答案
24.2-3
24.2-4
24.3-1 见图 24-6 24.3-2
24.3-3
24.3-4 24.3-5 24.3-6
24.3-7
24.3-8 这种情况下不会破坏已经更新的点的距离。 24.4**** 24.5****
25.1-1 见图 25-1 25.1-2 为了保证递归定义式 25.2 的正确性 25.1-3
8.3-3 8.3-4
8.3-5(*) 8.4-1 见图 8-4 8.4-2
8.4-3 3/2,1/2 8.4-4(*) 8.4-5(*)
9.1-1
9.1-2 9.2-1 9.3-1
第九章
9.3-2 9.3-3
9.3-4 9.3-5
9.3-6 9.3-7
9.3-8
9.3-9
15.1-1
6.4-4
6.4-5
6.5-1 据图 6-5 6.5-2
6.5-3 6.5-4 6.5-5
6.5-6 6.5-7
6.5-8
7.1-1 见图 7-1 7.1-2
7.1-3 7.1-4 7.2-1 7.2-2
7.2-3 7.2-4 7.2-5
第七章
7.2-6 7.3-1
7.3-2
7.4-1 7.4-2
5.3-6
6.1-1 6.1-2 6.1-3 6.1-4 6.1-5 6.1-6
第6章
6.1-7
6.2-1 见图 6-2 6.2-2
6.2-3
6.2-4
6.2-5 对以 i 为根结点的子树上每个点用循环语句实现 6.2-6
6.3-1
见图 6-3 6.3-2
6.3-3
6.4-1 见图 6-4 6.4-2 HEAPSORT 仍然正确,因为每次循环的过程中还是会运行 MAX-HEAP 的过程。 6.4-3
算法导论中文版答案
} cout<<len<<endl; } return 0; } 15.5-1
15.5-2 15.5-3
15.5-4
16.1-1
第 16 章
16.1-2 16.1-3
16.1-4 16.2-1 16.2-2
16.2-3
16.2-4
16.2-5 16.2-6
16.2-7
25.3-6
5.3-6
6.1-1 6.1-2 6.1-3 6.1-4 6.1-5 6.1-6
第6章
6.1-7
6.2-1 见图 6-2 6.2-2
6.2-3
6.2-4
6.2-5 对以 i 为根结点的子树上每个点用循环语句实现 6.2-6
6.3-1
见图 6-3 6.3-2
6.3-3
6.4-1 见图 6-4 6.4-2 HEAPSORT 仍然正确,因为每次循环的过程中还是会运行 MAX-HEAP 的过程。 6.4-3
6.4-4
6.4-5
6.5-1 据图 6-5 6.5-2
6.5-3 6.5-4 6.5-5
6.5-6 6.5-7
6.5-8
7.1-1 见图 7-1 7.1-2
7.1-3 7.1-4 7.2-1 7.2-2
7.2-3 7.2-4 7.2-5
第七章
7.2-6 7.3-1
7.3-2
7.4-1 7.4-2
16.3-1 16.3-2
16.3-3 16.3-4
16.3-5
16.3-6 那就推广到树的结点有三个孩子结点,证明过程同引理 16.3 的证明。 16.3-7 16.3-8
第 24 章
24.1-1 同源顶点 s 的运行过程,见图 24-4 24.1-2
算法导论习题解答2.3-7
算法导论习题解答2.3-7
•2.3-7 请给出⼀个运⾏为Θ(nlgn)的算法(伪码),使之能在给定⼀个由n个整数构成的集合S和另⼀个整数x时,判断出S中是否存在有两个其和等于x的元素。
解:解题思路:先对集合S进⾏归并排序,然后新建⼀个数组S1,使得S1[i] = x – S[i],再将两个数组并起来。
如果在并的过程中发现有两个元素相等且两个元素⼀个来⾃S,⼀个来⾃S1,则可以确定S中存在有两个其和等于x的元素。
Find whether x exits
1、Sort(S)
2、for i <- 0 to Length(S) – 1
3、 do S1[i] <- x - S[i]
4、for i <- 0 to Length(S) – 1
5、 do Merge( S,S1 )
6、 if S[p] > S1[q]
7、 S0[k] <- S[p] p++ k++
8、 if S[p] < S1[q]
9、 S0[k] <- S[q] q++ k++
10、 if S[p] == S1[q]
11、 return true
12、return false
在第⼀⾏进⾏排序时,时间代价为Θ(nlgn),后来的合并过程时间代价为Θ(n),总的时间代价为Θ(nlgn)。
《算法导论》读书笔记(三)
《算法导论》读书笔记(三) 本章介绍了快速排序及其算法分析,快速排序采⽤的是分治算法思想,对包含n个数的输⼊数组,最坏情况下运⾏时间为θ(n^2),但是平均性能相当好,期望的运⾏时间为θ(nlgn)。
另外快速排序能够就地排序(我理解是不需要引⼊额外的辅助空间,每次划分能确定⼀个元素的具体位置),在虚拟环境中能很好的⼯作。
1、快速排序的描述 快速排序算法采⽤的分治算法,因此对⼀个⼦数组A[p…r]进⾏快速排序的三个步骤为: (1)分解:数组A[p...r]被划分为两个(可能为空)⼦数组A[p...q-1]和A[q+1...r],给定⼀个枢轴,使得A[p...q-1]中的每个元素⼩于等于A[q],A[q+1...r]中的每个元素⼤于等于A[q],q下标是在划分过程中计算得出的。
(2)解决:通过递归调⽤快速排序,对⼦数组A[p...q-1]和A[q+1...r]进⾏排序。
(3)合并:因为两个⼦数组是就地排序,不需要合并操作,整个数组A[p…r]排序完成。
快速排序关键过程是对数组进⾏划分,划分过程需要选择⼀个主元素(pivot element)作为参照,围绕着这个主元素进划分⼦数组。
举个列说明如何划分数组,现有⼦数组A={24,15,27,5,43,87,34},以最后⼀个元素为主元素进⾏划分,划分过程如图所⽰:书中给出了划分过程的伪代码:1 PARTITION(A,p,r)2 x = A[r] //将最后⼀个元素作为主元素3 i = p-14 for j=p to r-1 //从第⼀个元素开始到倒数第⼆个元素结束,⽐较确定主元的位置5 do if A[j] <= x6 i = i+17 exchange A[i] <-> A[j]8 exchange A[i+1]<->A[r] //最终确定主元的位置9 return i+1 //返回主元的位置根据划分过程的为代码,书中⼜给出了快速排序的为代码:1 QUICKSORT(A,p,r)2 if p<r3 q = PARTITION(A,p,r) //确定划分位置4 QUICKSORT(A,p,q-1) //⼦数组A[p...q-1]5 QUICKSORT(Q,q+1,r) //⼦数组A[q+1...r]采⽤C元素实现⼀个完成的快速排序程序,程序如下:1 #include <stdio.h>2 #include <stdlib.h>34 size_t partition(int* datas,int beg,int last);5 void quick_sort(int* datas,int beg,int last);6 void swap(int *a,int *b);78 int main()9 {10 size_t i;11 int datas[10] = {78,13,9,23,45,14,35,65,56,79};12 printf("After quick sort,the datas is:\n");13 quick_sort(datas,0,9);14 for(i=0;i<10;++i)15 printf("%d ",datas[i]);16 exit(0);17 }1819 void swap(int *a,int *b)20 {21 int temp = *a;22 *a = *b;23 *b = temp;24 }2526 size_t partition(int* datas,int beg,int last)27 {28 int pivot = datas[last];29 int i,j;30 i = beg -1;31 for(j=beg;j<last;j++)32 {33 if(datas[j] < pivot)34 {35 i = i+1;36 swap(datas+i,datas+j);37 }38 }39 swap(datas+i+1,datas+last);40 return (i+1);41 }42 void quick_sort(int* datas,int beg,int last)43 {44 int pivot;45 if(beg < last)46 {47 pivot = partition(datas,beg,last);48 quick_sort(datas,beg,pivot-1);49 quick_sort(datas,pivot+1,last);50 }5152 }程序测试结果如下:可以将划分过程之间嵌⼊到快速排序过程中,C语⾔实现如下所⽰:1 void quicksort(int *datas,int length)2 {3 int pivot ,i,j;4 if(length > 1)5 {6 pivot = datas[length-1];7 for(i=0,j=0;j<length-1;j++)8 {9 if(datas[j] < pivot)10 {11 swap(datas+i,datas+j);12 i = i+1;13 }14 }15 swap(datas+i,datas+length-1);16 quicksort(datas,i);17 quicksort(datas+i,length-i);18 }19 }2、快速算法排序的性能 最快情况划分:当划分过程中产⽣的两个区域分别包含n-1个元素和1个元素的时候(即将待排序的数是逆序的),这样第个调⽤过程中每次划分都是不对称的。
《算法导论(原书第3版)》第24章部分题目解答
《算法导论(原书第3版)》第24章部分题⽬解答第24章单源最短路径24.1 Bellman-Ford算法24.1-4思路:先做|V|-1遍松弛操作,然后再做⼀遍松弛操作,对于这次松弛操作中dist值被更新的点,必然包含了每个负环中的⾄少⼀个点。
对于这些点做dfs查找它们能够在图中到达哪些点,所有被搜索到的点即为题⽬要求找的点部分c++代码:#include <bits/stdc++.h>using namespace std;const int maxn = ...;const int inf = 0x3f3f3f3f;//正⽆穷struct E{int x,y,z;//三元组(x,y,z)表⽰⼀条有向边。
从x出发到y,权值为z。
}vector<E> es;//存边vector<int> e[maxn];//模拟邻接链表vector<int> vec;//存起始点void bellman(int s){for(int i = 1; i<=n; i++)d[i]=inf;d[s] = 0;for(int t = 1; t<n; t++){for(auto e:es){if(d[e.x]!=inf && d[e.x]+e.z<d[e.y])d[e.y] = d[e.x] + w;}}for(auto e:es){if(d[e.x]!=inf && d[e.x]+e.z<d[e.y]){vec.push_back(y);}}}int v[maxn];void dfs(int x){v[x] = 1;for(auto y: e){if(!v[y]) dfs(y);}}void solve(int s){bellman(s);for(auto x:vec){if(!v[x]) dfs(x);}for(int i = 1; i<=n; i++){if(v[i]) cout<<"负⽆穷"<<endl;else if(d[i]==inf) cout<<"不可达"<<endl;else cout<<d[i]<<endl;}}24.1-5思路:跑⼀遍Bellman-Ford算法,具体做法如下:1、初始化∀v∈V,d[v]=0。