目录
一、梵塔问题
Void Hanoi(int n,int A,int B,int C)
{
if(n>0) {
Hanoi(n-1,A,C,B);
move(n,A,B);
Hanoi(n-1,C,B,A);
}
}
分析时间复杂度
M(1)=1
M(n)=2M(n-1)+1
= 2[2M(n-2)+1]+1
= 22M(n-2)+2+1
= 23M(n-3)+22+2+1
=……
=2iM(n-i)+2i-1+2i-2+……+2+1
=2iM(n-i)+2i - 1
suppose i= n -
Then M(n)= 2n-1 + 2n-1-1=2n-1
二、假硬币问题
一个包里有16个硬币,其中有一个假的,假的那个比正常的轻,有一个天平。
问:如何找到这个假硬币。
把硬币分成A,B 两堆,每堆8个,假的在轻的一堆。重复此步骤。
三、金块问题
一个包里有若干金块,给你一个称,用最小的比较次数求得最重块与最轻块。
当n<=2时,可以直接得到。
当n>2时,将其分为A和B
分别找到的A的最重块HA、最轻块LA;B的最重块HB、最轻块LB
则最重块为HA与HB中的最大值
最轻块为LA与LB中的最小值。
public class TEST {
public static void main(String args[]) {
int a[]= {65,120,100,60,130,100,190};
int [] result=fun(a,0,a.length-1);
System.out.println(result[0]);
System.out.println(result[1]);
}
private static int[] fun(int[] a,int l,int r) {
if(r-l+1<=2) {
int res[]=new int[2];
res[0]=a[l]<a[r]?a[l]:a[r];
res[1]=a[l]<a[r]?a[r]:a[l];
return res;
}
int res[]=new int[2];
int A[]=fun(a,l,l+(r-l)/2);
int B[]=fun(a,l+(r-l)/2+1,r);
res[0]=A[0]<B[0]?A[0]:B[0];
res[1]=A[1]>B[1]?A[1]:B[1];
return res;
}
}
四、分治
1. 把一个问题分解成两个或者更多的子问题
2. 递归的解决子问题
3. 通过组合这些解获得原始(较大)问题的解
时间复杂度分析:
五、例子一——二分查找
public class ER_FEN_Research {
package 算法;
public class ER_FEN_Research {
public static void main(String args[]) {
int a[]= {1,2,3,4,5,6,7,8,9,10};
int result1=fun1(a,-1);
System.out.println(result1);
int result2=fun2(a,7,0,a.length-1);
System.out.println(result2);
}
public static int fun1(int a[],int key) {
int l=0;
int r=a.length-1;
while(l<=r) {
int mid=(r+l)/2;
if(a[mid]<key)
l=mid+1;
else if(a[mid]==key)
return mid;
else if(a[mid]>key)
r=mid-1;
}
return -1;
}
public static int fun2(int a[],int key,int left,int right) {
int mid=(left+right)/2;
if(left>right) {
return -1;
}
if(a[mid]==key)
return mid;
else if(a[mid]<key)
return fun2(a,key,mid+1,right);
else
return fun2(a,key,left,mid-1);
}
}
分析时间复杂度:
六、例子——斯特拉森矩阵(Strassen’s matrix)乘法
怎么改进呢??
idea1:
这里对 T(n)=8T(n/2)+φ(n2)中的φ(n2)进行解释:
如图 ae+bg实际上是n2/4个加法,而不是一个加法
所以一共4*(n2/4)=n2个加法
idea2:即斯特拉森解法
一共是7个乘法
时间复杂度:
k=2,b=2, a=7。
7=a>bk=4
所以是
这种情况 T(n)=O(22.81)
比刚刚的算法要好。
理论上目前为止这个问题的最优解是O(22.376)
七、例子——棋盘问题
有一个n*n(n=2k)大小的棋盘,其中之一是一个特殊的网格。用L型板盖住这张棋盘。任意两个三格板不能重叠
解法:
令t (k) 为覆盖一个2k×2k 残缺棋盘所需要的时间。当k= 0时,s i z e等于1,覆盖它将花费常数时间d。当k > 0时,将进行4次递归的函数调用,这些调用需花费的时间为 4t (k-1 )。除了这些时间外, if 条件测试和覆盖3个非残缺方格也需要时间,假设用常数c 表示这些额外时间。可以得到以下递归表达式:
八、例子——归并排序
伪代码
Void MergeSort(type a[ ], int left, int right)
{
if left<right //at least two elements
{
find the middle index “mid”
Recursive invoke itself from “left” to “mid”
Recursive invoke itself from “mid” to “right”
Merge two sorted subresult
}
}
package 算法;
public class merge_sort {
public static void main(String args[]) {
int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
mergeSort(a,0,a.length-1);
for(int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
}
public static void mergeSort(int a [],int left ,int right) {
if(left<right) {
int mid=(left+right)/2;
mergeSort(a,left ,mid);
mergeSort(a,mid+1,right);
merge(a,left,mid,right);
}
}
private static void merge(int[] a, int left, int mid, int right) {
// TODO Auto-generated method stub
int b[]=new int[right-left+1];//临时空间
int l1=left;
int l2=mid+1;
int r1=mid;
int r2=right;
int k=0;
int i=l1,j=l2;
while(i<=r1&&j<=r2) {
if(a[i]<a[j]) {
b[k]=a[i];
i++;
}
else {
b[k]=a[j];
j++;
}
k++;
}
while(j<=r2) {
b[k]=a[j];
j++;
k++;
}
while(i<=r1) {
b[k]=a[i];
i++;
k++;
}
//复制部分
for(int p=0;p<right-left+1;p++) {
a[left+p]=b[p];
}
}
}
分析时间复杂度
合并长度为n1和n2的数组最多需要比较n1+n2-1次
所以,归并排序在最坏情况时间复杂度:
平均情况≈最坏情况
空间复杂性:需要额外的N个位置
九、例子——快排
步骤:
(Divide): take a[p] as pivot. Divide a[p:r] into a[p:q-1], a[q] and a[q+1:r].
(conquer):Recursive quicksort to a[p:q-1] and a[q+1:r].
伪代码:
void QuickSort(Type a[ ], int p,int r)
{
if(p<r){
int q=Partition(a,p,r);
//Partition函数负责将a进行一次分割,返回分割元素的位置
QuickSort(a,p,q-1);//对左半段排序
QuickSort(a,q+1,r);//对右半段排序
}
}
public class fast_sort {
public static void main(String args[]) {
int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
fastSort(a,0,a.length-1);
for(int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
}
public static void fastSort(int[] a, int i, int j) {
// TODO Auto-generated method stub
if(i<j) {
int p=partition(a,i,j);
//System.out.println(p);
fastSort(a, i, p-1);
fastSort(a, p+1, j);
}
}
private static int partition(int[] a, int left, int right) {
// TODO Auto-generated method stub
int i=left;
int j=right+1;
int x=a[i];
while(true) {
while(i<a.length-1&&a[++i]<x);
while(a[--j]>x);
if(i>=j) break;
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
a[left]=a[j];
a[j]=x;
return j;
}
}
时间复杂度:
Best case: split in the middle — Θ( n log n)
Worst case: sorted array! — Θ( n2)
Average case: random arrays — Θ( n log n)
没有比较排序算法运行得比nlogn好。
十、例子——线性时间选择
选择n个元素中第i小的元素
idea1:先排序再选择
idea2:随机选择分治算法
package 算法;
public class random_select {
public static void main(String args[]) {
int a[]= {12,34,56,76,89,100,44,37,1,3,56,70};
int r=RandomizedSelect(a,0,a.length-1,5);
System.out.println(r);
}
public static int RandomizedSelect(int[] a, int i, int j, int k) {
// TODO Auto-generated method stub
if(i==j)return a[i];
int p=randomizedPartition(a,i,j);
int m=p-i+1;
if(k<=m) return RandomizedSelect(a,i,p,k);
else return RandomizedSelect(a,p+1,j,k-m);
}
//和快排中分块代码一样的。
private static int randomizedPartition(int[] a, int left, int right) {
// TODO Auto-generated method stub
int i=left;
int j=right+1;
int x=a[i];
while(true) {
while(i<a.length-1&&a[++i]<x);
while(a[--j]>x);
if(i>=j) break;
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
a[left]=a[j];
a[j]=x;
return j;
}
}
时间复杂度: