算法复习笔记(四)减治法
减治法部分的初步学习,从下面四个例子:
1.折半查找
2.二叉树查找
3.选择问题
4.组合问题中的减治:减治法的核心:
与分治法不同,减治法只针对其部分子问题进行求解,同时也是采取划分后选择计算的思想
补充知识点:
中位数:
中位数是一个非常具有代表性的数字,我们在现实生活中,通常会选择中位数来代表一部分数据的趋势,相比于平均数更加具有代表性。
1.折半查找:
基本思想:
(1)若给定值与中间记录的关键码相等,查找成功
(2)若给定值少于中间记录的关键码,则在中间记录的左半区继续查找
(2)若给定值大于中间记录的关键码,则在中间记录的右半区继续查找
2.1.1折半查找算法实现 :递归
#include<iostream>
using namespace std;
int searchMin(int r[],int low,int heigh,int k)
{
int mid=(low+heigh)/2;
if(low>heigh)
return 0;
if(r[mid]==k)
return mid+1;
else if(r[mid]>k)
return searchMin(r,low,mid-1,k);
else
return searchMin(r,mid+1,heigh,k);
}
int main()
{
int a[10]={1,3,5,8,12};
int k=0;
cin>>k;
cout<<searchMin(a,0,4,k);
return 0;
}
折半查找算法实现 :非递归
#include <iostream>
using namespace std;
int BinSerchMid(int r[],int n,int k)
{ int low=0,high=n-1,mid;
while (low<=high)
{ mid=(low+high)/2;
if ( k<r[mid]) high=mid-1;
else if ( k>r[mid]) low=mid+1;
else return mid+1;
}
return 0;
}
int main( )
{ int a[100],b[100],n,k,i,ans;
cin>>n>>k;
for (i=0; i<n; i++) cin>>a[i];
ans=BinSerchMid(a,n,k);
if (ans==0) cout<<"No !"<<endl; else cout<<ans<<endl;
}
2.二叉查找树
//二叉查找树
#include<iostream>
using namespace std;
struct BiNode
{
int data;
BiNode *lchild;
BiNode *rchild;
};
BiNode * SearachBST(BiNode *root,int k)
{
if(root==NULL)
return NULL;
else if(root->data ==k )
{
cout<<"找到了 "<<root->data<<endl;
return root;
}
else if(k<root->data)
return SearachBST(root->lchild,k);
else
return SearachBST(root->rchild,k);
}
BiNode * InsertBST(BiNode * root,int data)
{
if(root==NULL)
{
root=new BiNode;
root->data=data;
root->lchild=root->rchild=NULL;
return root;
}
if(data<=root->data)
root->lchild=InsertBST(root->lchild,data);
else
root->rchild=InsertBST(root->rchild,data);
return root;
}
BiNode * createBST(int a[],int n)
{
//为 T 申请一个新结点,该结点为叶子
BiNode *T=new BiNode;
T->data=a[0];T->lchild=T->rchild=NULL;
for(int i=0;i<n;i++)
{
InsertBST(T,a[i]);
/*if(T->data==0)
cout<<"T==NULL";*/
}
return T;
}
int main()
{
BiNode * TR=new BiNode;
int r[100]={10,45,67,83,42,58,55,90,70,63};
TR=createBST(r,10);
for(int i=0;i<10;i++)
cout<<SearachBST(TR,r[i])<<" ";
return 0;
}
3.选择问题
思想:快速排序的patition下得出的位置是稳定的
我们通过这位位置和K进行比较 从而找出需要我们排序的那一部分
复杂性分析:深度至少是【log2n】至多是n,所以二叉排序树的查找性能在o(log2n)和o(n)之间
2.3选择问题:
算法分析:
(1)最好情况:每次划分的轴值恰好是序列的中值,这可以保证处理的区间比上次减半,由于在一次划分后,只需处理一个子序列,比较次数的递推式是:
T(n)=T(n/2)+O(n)=O(n)
(2)最差情况:每次划分的轴值恰好是序列的最大值/最小值,这可以保证处理的区间比上次少一,比较次数的递推式是:
T(n)=T(n-1)+O(n)=O(n的平方)
(3)平均情况:假设每次划分的轴值恰好是序列的中的一个随机位置,处理区间按照一种随机的方式减少,可以证明,算法的平均时间是O(n)
//查找第K小的数
#include<iostream>
using namespace std;
int patition(int r[],int low,int heigh)
{
int i=low,j=heigh;
while(i<j &&r[i]<r[j])
j--;
if(i<j&& r[i]>r[j])
{
int temp=r[i];
r[i]=r[j];
r[j]=temp;
i++;
}
while(i<j &&r[i]<r[j])
i++;
if(i<j&& r[i]>r[j])
{
int temp=r[i];
r[i]=r[j];
r[j]=temp;
j++;
}
return i;
}
int searchMinK(int r[],int low,int heigh,int k)
{
int pv=0;
pv=patition(r,low,heigh);
if(pv==k)
return r[k];
else if(pv>k)
return searchMinK(r,low,pv-1,k);
else
return searchMinK(r,pv+1,heigh,k);
}
int main()
{
int a[10]={1,8,4,3,5};
int k=0;
cin>>k;
cout<<searchMinK(a,0,4,k);
return 0;
}
4.组合问题中的减治:
4.1假币问题:
问题描述:
在n枚外观相同的硬币中,有一枚是假币,并且已知假币较轻。可以通过一架天平来任意比较两组硬币,从而得知两组硬币的重量是否相同,或者哪一组更轻一些,但不知道轻多少,假币问题是要求设计一个高效的算法来检测出这枚假币。
算法思路:
解决这个问题的最自然的想法就是一分为二,也就是把硬币分成两组。把n枚硬币分成两组,每组有枚硬币,如果n为奇数,就留下一枚硬币,然后把两组硬币分别放到天平的两端。
如果两组硬币的重量相同,那么留下的硬币就是假币;否则,用同样的方法对较轻的那组硬币进行同样的处理,假币一定在较轻的那组里。
算法分析:
在假币问题中,每次用天平比较后,只需解决一个规模减半的同样的问题,所以,它属于一个减治算法。该算法在最坏情况下满足如下的递推式:
算法的复杂性为O(log2n)。
Important!!!
假币问题的改进算法:考虑不是把硬币分成两组,而是分成三组,前两组有n/3组硬币,其余的硬币作为第三组,将前两组硬币放到天平上。
如果他们的重量相同,则假币一定在第三组中,用同样的方法对第三组进行处理;如果前两组的重量不同,则假币一定在较轻的那一组中,用同样的方法对较轻的那组硬币进行处理。
显然这个改进算法存在递推式:
算法的复杂性为O(log3n)。
示例代码:
const int N=8; //假设求解8枚硬币问题
int a[N]={2,2,1,2,2,2,2,2];//真币的重量是2,假币的重量是1
int Coin(int low,int high,int n)//在a[low]-a[high]中查找假币
{
int i,num1,num1,num3;//num1,num2和num3存储3组硬币的个数
int add1=0,add2=0;//add1和add2存储前两组硬币重量和
if(n==1)
return low+1;//返回序号,即下标+1
if(n%3 =0) //三组硬币的个数相同
num1=num2=n/3;
else
num1=num2=n/3+1;//前两组由[n/3]枚硬币
num3=n-num1-num2;
for(i=0;i<num1;i++) //计算第一组硬币的重量和
add1=add1+a[low+1];
for(i=num1;i<num1+num2;i++)
add2=add2+a[low+1];
if(add1<add2) //在第1组查找,下标范围是low-low+num1-1
return Coin(low,low+num1-1,num1);
else if (add1>add2)//在第2组查找,下标范围是low+num1到low+num1+num2-1
return Coin(low+num1,low+num1+num2-1,num2);
else //在第1组查找,
return Coin(low+num1+num2,high,num3)'
}
推荐阅读