分治法(第k小元素poj
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
}
}
合并排序算法分析
• 时间复杂度分析: 0 若n=1
C(n)=
2C( n / 2 )+n-1 若n≥2
求解递推式得: C(n)=nlogn-n+1
6.4 分治范式
• (1)划分步骤 • (2)治理步骤 • (3)组合步骤
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征: • 该问题的规模缩小到一定的程度就可以容易地解决; • 该问题可以分解为若干个规模较小的相同问题,即
} 人们从大量实践中发现,在用分治法设计算法时, 最好使子问题的规模大致相同。即将一个问题分成 大小相等的k个子问题的处理方法是行之有效的。 这种使子问题规模大致相等的做法是出自一种平衡 (balancing)子问题的思想,它几乎总是比子问题 规模不等的做法要好。
分治法的复杂性分析
(1)划分步骤 将规模为n的问题分成k个规模为n/m的子问题 (2)治理步骤 解k个规模为n/m的子问题 (3)组合步骤 将k个子问题的解合并为原问题的解 设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。 组合步骤需用f(n)个单位时间。 用T(n)表示该分治法解规模为n的问题所需的计算时间,则有:
•
if (x < a[mid])
•
return BSearch(a,n,x,low,mid-1);
•
else
•
return BSearch(a,n,x,mid+1,high);
•
}
•}
二分搜索算法分析
• 时间复杂度分析: 1 若n=1
C(n)= C( n / 2 )+1 若n ≥ 2
求解递推式得:
棋盘覆盖问题 n=8
6.1.2一个简单例子
• 问题一:在一个整数组A[1...n]中, 同时寻找最大值和最小值
• 方法一:顺序扫描法 S1: min=A[1];max=A[1]; S2: 扫描数组,对i从2到n做:
S2a: 如果A[i]<min,则min=A[i]; S2b: 如果A[i]>max,则max=A[i]; S3: 返回x,y的值
c(n) log n 1
6.3 合并排序
• 原问题:对A[1...n]排序 • 用分治策略分析: (1)划分:将A[1...n]分为A[1...n/2]
A[n/2+1...n]两部分; (2)治理: 分别对A[1...n/2] 和
A[n/2+1...n]排序 (3)组合:将两个有序子序列合并为一 个有序序列
• 比较次数:2n-2
• 方法二:分治法 • 基本思想: (1)划分:将数组分割成两半 (2)治理:在每一半中找到最大值和最小
值。
(3)合并:返回两个最大值中的最大值和 两个最小值中的最小值。
• 算法6.1 • Minmax(int low,int high) • {if high-low=1 then • if A[low]<A[high] then return(A[low],A[high]) • else return(A[high],A[low]) • end if • else • mid=(low+high)/2 • (x1,y1)=minmax(low,mid) • (x2,y2)=minmax(mid+1,high) • x=min{x1,x2}; y=max(y1,y2) • return(x,y) • endif
• while (high >= low){ //待搜区间非空
•
int mid = (low+ high )/2;
•
if (x == a[mid]) return mid; //查找成功
•
if (x < a[mid]) high = mid-1;
•
else low = mid+1;
•
}
• return -1; //查找失败,返回-1
=
1 1 2
cn
= 3cn
3
• 分析方法一:
(1)在最坏情况下,算法需要O(n2)计算 时间
(2)若改进算法一,在划分时随机选取一 个元素作为基准元素,算法可以在O(n) 平均时间内找出n个输入元素中的第k小 元素。但在最坏情况下,算法仍需要 O(n2)计算时间。我们要找一个字最坏情 况下只需O(n)时间的算法。
该问题具有最优子结构性质 • 利用该问题分解出的子问题的解可以合并为该问题
的解; • 该问题所分解出的各个子问题是相互独立的,即子 因这能为条否问问特利题题征用之的是分涉间计应治及不算用法到包复分完含杂治全公性法取共一的决的般前于效子是提问率问随,题题着它是如。问也否果题是具各规大有子模多这问的数条题增问特是加题征不, 而可如独增以果立加满具的,足备因的了则此,前分大此两治部特条法分征特要问反征做题映,许满了而多足递不这归具必个思备要特想第的征的三工。应条作用特,征重,复则地 可解以公考共虑的子贪问心题算,法此或时动虽态然规也划可。用分治法,但一般 用动态规划较好。
•} • a[i]=temp;
• return(i)}
• 方法一: (1)以元素mm为基准,将A[1...n]拆分为三组:
小于mm的元素 mm 大于mm的元素
并返回基准元素的下标j (2)若j==k, 则A[j]即第k小的元素;
若j>k,丢弃A[j...n],在A[1...j-1]中寻找第k小的 元素; 若j<k,丢弃A[1...j] ,在A[j+1...n]中寻找第k-j小的 元素;
• 启发二:如果能在每一个划分步骤后,问 题的规模以一个常数因子被减小,则原问 题 可在O(n)时间内解决。
假设算法丢弃1/3并对剩余的2/3部分递 归,假定在每次调用中,算法对每个元素 耗费的时间不超过常数c,则算法所需的 全部时间为:
cn+(2/3)cn +(2/3)2cn+... +(2/3)jcn+...
分治法的基本步骤
divide-and-conquer(P)
{ if ( | P | <= n0) adhoc(P); //解决小规模的问题 divide P into smaller subinstances P1,P2,...,//分解问题
for (i=1,i<=k,i++) yi=divide-and-conquer(Pi); //递归的解各子问题 return merge(y1,...,yk); //将各子问题的解合并为原问题的解
第六章 分 治
6.1 引言
• 分治法的设计思想是,将一个难以直接解 决的大问题,分割成一些规模较小的相同 问题,以便各个击破,分而治之。
• 战略 • 算法设计技术
划分——治理——组合
6.1.1算法总体思想
• 对这k个子问题分别求解。如果子问题的规模仍然不够 •小将,要则求再解划的分较为大k个规子模问的题问,题如分此割递成归k个的更进小行规下模去的,直 到子问问题题规。模足够小,很容易求出其解为止。
• 可将规模为n的问题分解为多少个规模为n/2的子问题?为 什么不一定是两个?
• 将求出的小规模的问题的解合并为一个更大规模的问题 的解,自底向上逐步求出原来问题的解。
T(n)
=
n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
T(n)
=n
T(n/2)
T(n/2)
T(n/2)
T(n/2)
• 直到问题规模足够小,很容易求出其解为止。
T(n)
=n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
• 算法6.5 区间划分算法
• partition(int a[],int low,int high) • {//以a[low]为基准元素,对a[low...high]进行划分
• int i=low,j=high; • temp=a[low]; • //取第一个对象为进行调整的标准对象
• while(i<j) • { while(i<j&&temp<=a[j])j--; //在数组的右端扫描 • if(i<j)a[i++]=a[j]; • while(i<j&&a[i]<temp)i++; //在数组的左端扫描 • if(i<j)a[j--]=a[i];
•}
• 算法6.3 二分搜索的递归算法
• int BSearch(int a[], int n, int x,int low,int high )
• { if(low>high)return -1 //待搜区间为空
• else
• { int mid = (low+ high )/2;
•
if (x == a[mid]) return mid; //查找成功
讨论(1)
• 请将以上算法改写为C程序 注意: (1)以上算法要求函数同时返回两个
值,显然是不符合C语言语法的,请给出 你的解决方案
(2)C中函数参数的传递是传值传递
• Minmax(int A[],int low,int high,int *x,int *y) • {if ( high-low==1) • if (A[low]<A[high]){*x= A[low];*y=A[high];} • else {*x=A[high];*y=A[low]);} else {mid=(low+high)/2; • minmax(A,low,mid,&x1,&y1); • minmax(A,mid+1,high,&x2,&y2); • x=min{x1,x2}; y=max(y1,y2); }}
• 时间复杂度分析: 1 若n=2
C(n)= 2C(n/2)+2 若n>2
求解递推式得:
C(n)=3n/2-2
6.2 二分搜索
• 问题:在一个有序序列中搜索给定值x, 若找到,返回x所在位置,否则返回查找 失败标志-1。
二分搜索过程
1 4 5 7 8 9 10 12 15 22 23 27 32 35
• 算法6.4:合并排序算法 MergeSort(int A[],int n)
{Msort(A,1,n);}
MSort(int A[],int low,int high) {if(low<high) //若区间有两个或两个以上元素
{mid=(low+high)/2; //计算划分点 Msort(A,low,mid); //治理左半个区间 Msort(A,mid+1,high); //治理右半个区间 Merge(A,low,mid,high); //组合步骤
• 启发一:受快速排序的启发,先对序列进行 划分,如对如下序列找第4小的元素
13,4,6,19,16,8,5,3,17,21
以第一个元素为基准,把比13小的移到它的左 边,比13大的移到它的右边
[4,6 , 8,5,3,]1 3,[19,16,17 ,21]
• 比13小的有5个元素,那么第4小的元素一定 在13的左边,此时,可以丢弃13及13右边的 元素。
T (n)
O(1) kT(n / m)
f
(n)
n 1 n 1
通过迭代法求得方程的解:T (n)
nlogm k
logm n1
kj
f
(n / m j )
j0
6.5 寻找中项和第k小元素
• 问题:在序列A[1...n]中寻找第k小的元素 • 当k=1,求序列中的最小值; • 当k=n,求序列中的最大值。简单!Θ(n) 其他情况下,如n=100,k=40 ? 方法一: 对A[1...n]排序,A[40]即所求。 时间复杂度为:Ω(nlogn) 寻找一个O(n)的算法!
(3)问题的关键是基准元素的选取
• 关键问题:如何选择基准元素? 方法一:以区间左端元素为基准元素 方法二:随即选取基准元素
在最坏情况下,效果极不理想,不能保证 问题的规模以一个常数因子被减小
• 中项的概念
序列A[1...n]的中项是序列中第
mid
因A[mid]<22,丢弃A[mid]及 12 15 22 23 27 32 35
其左边所有元素
mid
12 15 22
mid 22
mid
查找成功!
• 算法6.2 二分搜索的非递归算法
• int BinarySearch(int a[], int n, int x )
• { int low=0;high=n-1;
}
合并排序算法分析
• 时间复杂度分析: 0 若n=1
C(n)=
2C( n / 2 )+n-1 若n≥2
求解递推式得: C(n)=nlogn-n+1
6.4 分治范式
• (1)划分步骤 • (2)治理步骤 • (3)组合步骤
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征: • 该问题的规模缩小到一定的程度就可以容易地解决; • 该问题可以分解为若干个规模较小的相同问题,即
} 人们从大量实践中发现,在用分治法设计算法时, 最好使子问题的规模大致相同。即将一个问题分成 大小相等的k个子问题的处理方法是行之有效的。 这种使子问题规模大致相等的做法是出自一种平衡 (balancing)子问题的思想,它几乎总是比子问题 规模不等的做法要好。
分治法的复杂性分析
(1)划分步骤 将规模为n的问题分成k个规模为n/m的子问题 (2)治理步骤 解k个规模为n/m的子问题 (3)组合步骤 将k个子问题的解合并为原问题的解 设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。 组合步骤需用f(n)个单位时间。 用T(n)表示该分治法解规模为n的问题所需的计算时间,则有:
•
if (x < a[mid])
•
return BSearch(a,n,x,low,mid-1);
•
else
•
return BSearch(a,n,x,mid+1,high);
•
}
•}
二分搜索算法分析
• 时间复杂度分析: 1 若n=1
C(n)= C( n / 2 )+1 若n ≥ 2
求解递推式得:
棋盘覆盖问题 n=8
6.1.2一个简单例子
• 问题一:在一个整数组A[1...n]中, 同时寻找最大值和最小值
• 方法一:顺序扫描法 S1: min=A[1];max=A[1]; S2: 扫描数组,对i从2到n做:
S2a: 如果A[i]<min,则min=A[i]; S2b: 如果A[i]>max,则max=A[i]; S3: 返回x,y的值
c(n) log n 1
6.3 合并排序
• 原问题:对A[1...n]排序 • 用分治策略分析: (1)划分:将A[1...n]分为A[1...n/2]
A[n/2+1...n]两部分; (2)治理: 分别对A[1...n/2] 和
A[n/2+1...n]排序 (3)组合:将两个有序子序列合并为一 个有序序列
• 比较次数:2n-2
• 方法二:分治法 • 基本思想: (1)划分:将数组分割成两半 (2)治理:在每一半中找到最大值和最小
值。
(3)合并:返回两个最大值中的最大值和 两个最小值中的最小值。
• 算法6.1 • Minmax(int low,int high) • {if high-low=1 then • if A[low]<A[high] then return(A[low],A[high]) • else return(A[high],A[low]) • end if • else • mid=(low+high)/2 • (x1,y1)=minmax(low,mid) • (x2,y2)=minmax(mid+1,high) • x=min{x1,x2}; y=max(y1,y2) • return(x,y) • endif
• while (high >= low){ //待搜区间非空
•
int mid = (low+ high )/2;
•
if (x == a[mid]) return mid; //查找成功
•
if (x < a[mid]) high = mid-1;
•
else low = mid+1;
•
}
• return -1; //查找失败,返回-1
=
1 1 2
cn
= 3cn
3
• 分析方法一:
(1)在最坏情况下,算法需要O(n2)计算 时间
(2)若改进算法一,在划分时随机选取一 个元素作为基准元素,算法可以在O(n) 平均时间内找出n个输入元素中的第k小 元素。但在最坏情况下,算法仍需要 O(n2)计算时间。我们要找一个字最坏情 况下只需O(n)时间的算法。
该问题具有最优子结构性质 • 利用该问题分解出的子问题的解可以合并为该问题
的解; • 该问题所分解出的各个子问题是相互独立的,即子 因这能为条否问问特利题题征用之的是分涉间计应治及不算用法到包复分完含杂治全公性法取共一的决的般前于效子是提问率问随,题题着它是如。问也否果题是具各规大有子模多这问的数条题增问特是加题征不, 而可如独增以果立加满具的,足备因的了则此,前分大此两治部特条法分征特要问反征做题映,许满了而多足递不这归具必个思备要特想第的征的三工。应条作用特,征重,复则地 可解以公考共虑的子贪问心题算,法此或时动虽态然规也划可。用分治法,但一般 用动态规划较好。
•} • a[i]=temp;
• return(i)}
• 方法一: (1)以元素mm为基准,将A[1...n]拆分为三组:
小于mm的元素 mm 大于mm的元素
并返回基准元素的下标j (2)若j==k, 则A[j]即第k小的元素;
若j>k,丢弃A[j...n],在A[1...j-1]中寻找第k小的 元素; 若j<k,丢弃A[1...j] ,在A[j+1...n]中寻找第k-j小的 元素;
• 启发二:如果能在每一个划分步骤后,问 题的规模以一个常数因子被减小,则原问 题 可在O(n)时间内解决。
假设算法丢弃1/3并对剩余的2/3部分递 归,假定在每次调用中,算法对每个元素 耗费的时间不超过常数c,则算法所需的 全部时间为:
cn+(2/3)cn +(2/3)2cn+... +(2/3)jcn+...
分治法的基本步骤
divide-and-conquer(P)
{ if ( | P | <= n0) adhoc(P); //解决小规模的问题 divide P into smaller subinstances P1,P2,...,//分解问题
for (i=1,i<=k,i++) yi=divide-and-conquer(Pi); //递归的解各子问题 return merge(y1,...,yk); //将各子问题的解合并为原问题的解
第六章 分 治
6.1 引言
• 分治法的设计思想是,将一个难以直接解 决的大问题,分割成一些规模较小的相同 问题,以便各个击破,分而治之。
• 战略 • 算法设计技术
划分——治理——组合
6.1.1算法总体思想
• 对这k个子问题分别求解。如果子问题的规模仍然不够 •小将,要则求再解划的分较为大k个规子模问的题问,题如分此割递成归k个的更进小行规下模去的,直 到子问问题题规。模足够小,很容易求出其解为止。
• 可将规模为n的问题分解为多少个规模为n/2的子问题?为 什么不一定是两个?
• 将求出的小规模的问题的解合并为一个更大规模的问题 的解,自底向上逐步求出原来问题的解。
T(n)
=
n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
T(n)
=n
T(n/2)
T(n/2)
T(n/2)
T(n/2)
• 直到问题规模足够小,很容易求出其解为止。
T(n)
=n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
• 算法6.5 区间划分算法
• partition(int a[],int low,int high) • {//以a[low]为基准元素,对a[low...high]进行划分
• int i=low,j=high; • temp=a[low]; • //取第一个对象为进行调整的标准对象
• while(i<j) • { while(i<j&&temp<=a[j])j--; //在数组的右端扫描 • if(i<j)a[i++]=a[j]; • while(i<j&&a[i]<temp)i++; //在数组的左端扫描 • if(i<j)a[j--]=a[i];
•}
• 算法6.3 二分搜索的递归算法
• int BSearch(int a[], int n, int x,int low,int high )
• { if(low>high)return -1 //待搜区间为空
• else
• { int mid = (low+ high )/2;
•
if (x == a[mid]) return mid; //查找成功
讨论(1)
• 请将以上算法改写为C程序 注意: (1)以上算法要求函数同时返回两个
值,显然是不符合C语言语法的,请给出 你的解决方案
(2)C中函数参数的传递是传值传递
• Minmax(int A[],int low,int high,int *x,int *y) • {if ( high-low==1) • if (A[low]<A[high]){*x= A[low];*y=A[high];} • else {*x=A[high];*y=A[low]);} else {mid=(low+high)/2; • minmax(A,low,mid,&x1,&y1); • minmax(A,mid+1,high,&x2,&y2); • x=min{x1,x2}; y=max(y1,y2); }}
• 时间复杂度分析: 1 若n=2
C(n)= 2C(n/2)+2 若n>2
求解递推式得:
C(n)=3n/2-2
6.2 二分搜索
• 问题:在一个有序序列中搜索给定值x, 若找到,返回x所在位置,否则返回查找 失败标志-1。
二分搜索过程
1 4 5 7 8 9 10 12 15 22 23 27 32 35
• 算法6.4:合并排序算法 MergeSort(int A[],int n)
{Msort(A,1,n);}
MSort(int A[],int low,int high) {if(low<high) //若区间有两个或两个以上元素
{mid=(low+high)/2; //计算划分点 Msort(A,low,mid); //治理左半个区间 Msort(A,mid+1,high); //治理右半个区间 Merge(A,low,mid,high); //组合步骤
• 启发一:受快速排序的启发,先对序列进行 划分,如对如下序列找第4小的元素
13,4,6,19,16,8,5,3,17,21
以第一个元素为基准,把比13小的移到它的左 边,比13大的移到它的右边
[4,6 , 8,5,3,]1 3,[19,16,17 ,21]
• 比13小的有5个元素,那么第4小的元素一定 在13的左边,此时,可以丢弃13及13右边的 元素。
T (n)
O(1) kT(n / m)
f
(n)
n 1 n 1
通过迭代法求得方程的解:T (n)
nlogm k
logm n1
kj
f
(n / m j )
j0
6.5 寻找中项和第k小元素
• 问题:在序列A[1...n]中寻找第k小的元素 • 当k=1,求序列中的最小值; • 当k=n,求序列中的最大值。简单!Θ(n) 其他情况下,如n=100,k=40 ? 方法一: 对A[1...n]排序,A[40]即所求。 时间复杂度为:Ω(nlogn) 寻找一个O(n)的算法!
(3)问题的关键是基准元素的选取
• 关键问题:如何选择基准元素? 方法一:以区间左端元素为基准元素 方法二:随即选取基准元素
在最坏情况下,效果极不理想,不能保证 问题的规模以一个常数因子被减小
• 中项的概念
序列A[1...n]的中项是序列中第
mid
因A[mid]<22,丢弃A[mid]及 12 15 22 23 27 32 35
其左边所有元素
mid
12 15 22
mid 22
mid
查找成功!
• 算法6.2 二分搜索的非递归算法
• int BinarySearch(int a[], int n, int x )
• { int low=0;high=n-1;