Divide and Conquer
本文最后更新于:9 天前
Try to be a good coder and emmm… review algorithm class.XD
残缺棋盘
/*A great number of recrusive...XD*/
#include <iostream>
#include <string.h>
using namespace std;
char mylist[100][100];
int ans=0;
void makeup_board(int tr,int tc, int dr, int dc, int size)
{
int t,s;
if(size<2)
return ;
ans+=1;
t=ans;
s=size/2;
if(dr<tr+s&&dc<tc+s){
//show that the defective part is in the up-left area
makeup_board(tr,tc,dr,dc,s);
mylist[tr+s][tc+s]=t+'A';//emmm, when chessboard big enough, it will beyond 'Z'and there might have amazing thing
mylist[tr+s-1][tc+s]=t+'A';//and there might have amazing thing.
mylist[tr+s][tc+s-1]=t+'A';//so,if you want try a big chessboard, please use numbers.
makeup_board(tr,tc+s,tr+s-1,tc+s,s);
makeup_board(tr+s,tc,tr+s,tc+s-1,s);
makeup_board(tr+s,tc+s,tr+s,tc+s,s);
}else if(dr<tr+s&&dc>=tc+s){//up-right
makeup_board(tr,tc+s,dr,dc,s);
mylist[tr+s][tc+s]=t+'A';
mylist[tr+s][tc+s-1]=t+'A';
mylist[tr+s-1][tc+s-1]=t+'A';
makeup_board(tr,tc,tr+s-1,tc+s-1,s);
makeup_board(tr+s,tc,tr+s,tc+s-1,s);
makeup_board(tr+s,tc+s,tr+s,tc+s,s);
}else if(dr>=tr+s&&dc<tc+s){//down-left
makeup_board(tr+s,tc,dr,dc,s);
mylist[tr+s][tc+s]=t+'A';
mylist[tr+s-1][tc+s]=t+'A';
mylist[tr+s-1][tc+s-1]=t+'A';
makeup_board(tr,tc,tr+s-1,tc+s-1,s);
makeup_board(tr,tc+s,tr+s-1,tc+s,s);
makeup_board(tr+s,tc+s,tr+s,tc+s,s);
}else if(dr>=tr+s&&dc>=tc+s){
makeup_board(tr+s,tc+s,dr,dc,s);
mylist[tr+s-1][tc+s-1]=t+'A';
mylist[tr+s][tc+s-1]=t+'A';
mylist[tr+s-1][tc+s]=t+'A';
makeup_board(tr,tc,tr+s-1,tc+s-1,s);
makeup_board(tr+s,tc,tr+s,tc+s-1,s);
makeup_board(tr,tc+s,tr+s-1,tc+s,s);
}
}
int main()
{
cout<<"Please input the value of k (used to calculate 2^k)"<<endl;
int k,size=1,x,y;
cin>>k;
for(int i=0;i<k;i++)
size*=2;
cout<<"Please input the defective location (x,y) from 1~2^k"<<endl;
cin>>x>>y;
memset(mylist,'#',sizeof(mylist));
makeup_board(0,0,x-1,y-1,size);
for(int i=0;i<size;i++){
for(int j=0;j<size;j++)
cout<<mylist[i][j]<<" ";
cout<<endl;
}
return 0;
}
Merge Sort
#include <iostream>
using namespace std;
void meger(int *mylist,int left,int mid, int right)
{
int i=left, j=mid+1;
int counter=0;
int * ans =new int [right-left+1];
while(i<=mid&&j<=right)
{
if(mylist[i]<=mylist[j]){
ans[counter++]=mylist[i++];
}
else
ans[counter++]=mylist[j++];
}
while(i<=mid)
{
ans[counter++]=mylist[i++];
}
while(j<=right)
ans[counter++]=mylist[j++];
for(int i=left,k=0;i<=right;i++,k++)
mylist[i]=ans[k];
}
void meger_sort(int *mylist,int left, int right)
{
if(left<right)
{
int mid=(left+right)/2;
meger_sort(mylist, left,mid);
meger_sort(mylist,mid+1,right);
meger(mylist,left,mid,right);
}
}
int main()
{
int lenght, *mylist, *ans;
cin>>lenght;
mylist= new int[lenght];
for(int i=0;i<lenght;i++)
cin>>mylist[i];
meger_sort(mylist,0,lenght-1);
for(int i=0;i<lenght;i++)
cout<<mylist[i]<<" ";
cout<<endl;
return 0;
}
金块问题
Max-Min(A[1,n], max, min)
if n==1 max<- min<- a[1]; return ;
if n==2 比较a[1] a[2]大小
else
m<- n/2
Max-Min (A[1,m], max1 , min1 )
Max - Min(A[m+1, n] ,max2 , min2 )
max <- max(max1, max2)
min< - min (min1 , min2)
return ;
/*Easy code without recursive*/
#include <iostream>
using namespace std;
int Minmax(int *mylist,int n, int &maxx, int &minn)
{
if(n<1)
return 0;
if(n==1)
{
maxx=minn=0;
return 1;
}
int start;
if(n%2){
maxx=minn=0;
start=1;
}else{
mylist[0]>mylist[1]?maxx=0,minn=1 : minn=0,maxx=1;
start=2;
}
for(int i=start;i<n;i+=2){
if(mylist[i]>mylist[i+1]){
if(mylist[i]>mylist[maxx])
maxx=i;
if(mylist[i+1]<mylist[minn])
minn=i+1;
}else{
if(mylist[i+1]>mylist[maxx])
maxx=i+1;
if(mylist[i]<mylist[minn])
minn=i;
}
}
return 1;
}
int main()
{
int len;
cout<<"Input the length of array"<<endl;
cin>>len;
int *mylist=new int [len];
cout<<"Input the elements of array"<<endl;
for(int i=0;i<len;i++)
cin>>mylist[i];
int maxx=0,minn=0;
if(Minmax(mylist,len,maxx,minn)){
cout<<"the biggest is ";
cout<<mylist[maxx]<<" the min is "<<mylist[minn]<<"\n";
}
return 0;
}
n==2^k^时复杂度分析
0, n==1
C(n)= 1, n==2
2xC(n/2) + 2 , 其他
C(n)=(3n/2)-2
主项法:a=2,b=2,logba=1,n^1^=n,f(n)==2==θ(1)
C(n)=θ(n)
快速排序
#include <iostream>
using namespace std;
int partation(int * mylist, int left ,int right)
{
int povit=mylist[left];
while(left<right){
while (left<right&&mylist[right]>=povit)
right--;
mylist[left]=mylist[right];
while (left<right&&mylist[left]<=povit)
left++;
mylist[right]=mylist[left];
}
mylist[left]=povit;
return left;
}
void quicksort(int* mylist,int left, int right)
{
if(left>right)
return ;
int j=partation(mylist,left,right);
quicksort(mylist,left,j-1);
quicksort(mylist,j+1,right);
}
int main()
{
int mylist[6]={0,4,5,3,2,6};
quicksort(mylist,0,5);
for(int i=0;i<6;i++)
cout<<mylist[i]<<" ";
cout<<endl;
return 0;
}
时间复杂度分析
最坏情况下
Tmax(n)=O(1),n<=1
=T(n-1)+O(n)=O(n^2^),n>1。即产生的区域分别包括n-1和n个元素
最好情况下
Tmin(n)=O(1),n<=1
=2T(n/2)+O(n)=O(nlogn),n>1。即产生的区域每次都为n/2
平均时间复杂度为θ(nlogn)
证明:
每轮快排将数组分成两部分,用S代表左数据段元素个数,右数据段元素个数为n-s-1,S取0~n-1中任何数的概率为 1/n
T(n)=1/n Σs=0^n-1^ (T(s)+T(n-s-1)) + cn=2/n Σs=0^n-1^ T(S) + cn
由nT(n)-(n-1)T(n-1)=2T(n-1)+O(n)得
T(n)=(1+1/n)T(n-1)+ O(n)
归纳得T(n)=θ(nlogn)
选择问题
对给定序列,选出其中第k小的元素
该问题可在O(nlogn)时间内解决,方法为先排序,再取出a[k-1]元素
若用快速排序,可取得较好的平均复杂度,但最坏复杂度为O(n^2^)
朴素算法(使用快排)
#include <iostream>
using namespace std;
int partation(int * mylist, int left ,int right)
{
int povit=mylist[left];
while(left<right){
while (left<right&&mylist[right]>=povit)
right--;
mylist[left]=mylist[right];
while (left<right&&mylist[left]<=povit)
left++;
mylist[right]=mylist[left];
}
mylist[left]=povit;
return left;
}
int quicksort(int* mylist,int left, int right,int k)
{
if(left==right)
return mylist[left];
int j=partation(mylist,left,right);
if(k==j-left+1)
return mylist[j];
if(k-j+left<1)
return quicksort(mylist,left,j-1,k);
else
return quicksort(mylist,j+1,right,k-j+left-1);
}
int main()
{
int len;
cin>>len;
int *mylist=new int [len];
for(int i=0;i<len;i++)
cin>>mylist[i];
cout<<quicksort(mylist,0,len-1,3)<<endl;
return 0;
}
复杂度分析:
若left和right总是同样大小,或者相差不超过一个元素,那么递归式如下
T(n)=T(n/2)+cn, n>1
d, n<=1
若n==2^k^,可通过迭代方法得到T(n)=θ(n)
即选择较好的分点,可以得到较好的性能。
中间的中间
若仔细地选择支点元素,则最坏情况下的时间开销也可以变成θ(n)
规则:
首先将数组a中的n 个元素分成n/r 组,r 为某一整常数。
除了最后一组外,每组都有r 个元素。然后通过在每组中对r 个元素进行排序来寻找每组中位于中间位置的元素。
最后对所得到的n/r 个中间元素,递归使用选择算法,求得”中间之中间”作为支点元素
当使用中间的中间规则选取支点时,下定理成立
若r=5,且a 中所有元素都不同,那么当n≥24时,有max{| left |, | right | }≤3n/ 4
*有 ⌈1/2 * ⌊n/5⌋ ⌉ 多个中间元素不大于mm。*
大于mm元素的数目有上界
故,当n>=24时,0.7n+1.2≤3n/4
r==5时
T(n) ≤ T(n/5) + T(3n/4) +Θ(n)
归纳法可证明当n≥24时有t(n)≤20cn
r=9, 当n≥90时,有max{ |left|,|right|}≤7n/8
问:用第K小做支点的快速排序时间复杂度•在快速排序算法中,如果我们先调用最坏情形时间复杂度O(n)的选择算法找出第n/2小元素并以此为支点(pivot)对要排序的数组进行分划,则改进后的快速排序算法的最坏情形时间复杂度T(n)是什么?假定n=2k,列出T(n)满足的递归方程并分析.
解答:用第n/2小的元素做支点,把数组分成均等的两半,同时,分化的最坏情形时间代价为O(n)=cn,则改进快速排序算法的最坏时间复杂度为T(n)=2T(n/2)+cn。符合主定理case2,T(n)=Θ(nlogn)。假定n=2k,T(n)=2T(n/2)+cn=2kT(1)+k⋅cn=Θ(n+nlogn)=Θ(nlogn)。
求逆序个数
#include <iostream>
using namespace std;
int sum=0;
void mergecount(int *mylist,int l, int mid,int r, int *temp)
{
int p1=l, p2=mid+1, index=0;
while(p1<=mid&&p2<=r){
if(mylist[p1]<mylist[p2]){
temp[index++]=mylist[p1++];
}else{
temp[index++]=mylist[p2++];
sum+=mid-p1+1;
}
}
while(p1<=mid){
temp[index++]=mylist[p1++];
}
while(p2<=r){
temp[index++]=mylist[p2++];
}
for(int i=0;i<r-l+1;i++)
mylist[l+i]=temp[i];
}
void mergesort(int *mylist, int l, int r,int *temp)
{
if(l<r){
int mid=(l+r)/2;
mergesort(mylist,l,mid,temp);
mergesort(mylist,mid+1,r,temp);
mergecount(mylist,l,mid,r,temp);
}
}
int main()
{
int n;
cout<<"Input the number of data"<<endl;
cin>>n;
int *temp=new int [n];
int *mylist=new int[n];
cout<<"Input datas"<<endl;
for(int i=0;i<n;i++)
cin>>mylist[i];
mergesort(mylist,0,n-1,temp);
cout<<sum<<endl;
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!