分而治之

  分而治之,模仿了法国皇帝拿破仑在1805年12月2日奥斯特里茨战役中采用的接触策略。奥地利、俄罗斯联军的人数比拿破仑的军队多出大约15000人。奥俄联军向法军右翼发动大规模进攻。拿破仑预见到这一进攻,他向联军中央部位发起冲击,将他们的军队一分为二。因为这两支较小的军队无法分别于拿破仑匹敌,均遭受严重损失,被迫撤退。拿破仑将一支大型军队分割(divide)为两支较小的军队,再分别治服这两支较小的军队,从而治服(conquer)整个大型军队。
  分而治之(divide-and-conquer)方法将同一策略应用于问题的一个实例。也就是说,它将问题的一个实例划分为两个或更多个较小的实例。这些较小的实例通常也是原问题的实例。如果可以轻松获得较小问题实例的答案,那么通过合并这些答案,就能得出原实例的答案。如果这些较小的实例还是太大,难以轻松解决,可以将它们划分为再小一些的实例。一直持续这一实例划分过程,直到其规模小到可以轻松获得答案为止。
  分而治之方法是一种自顶向下(top-down)方法。也就是,通过向下获得较小实例的答案,以获得一个问题顶级实例的答案。其实,这就是递归例程使用的方法。

1. 二分查找

  用分而治之的术语来说,二分查找是在一个有序(非递减顺序)数组中查找键x的位置时,首先将x与数组的中间项进行对比。如果它们相等,则算法完成。如果不相等,则将数组分为两个子数组,一个子数组中包含中间项左侧的所有元素,另一个子数组包含其右侧的所有元素。如果x小于中间项,则将这一过程再应用于左侧子数组。否则,将其应用于其右侧子数组。也就是将x与适当子数组的中间项进行比较。如果它们相等,则算法完成。如果不相等,则将子数组划分为两个更小的数组。一直重复这一过程,直到找出x,或者最终确认x不在数组中。

#####二分查找(递归版本):
  问题:判断x是否在一个大小为n的有序数组S中?
  输入:正整数n;键值x;有序(非递减顺序)数组S,索引范围为1~n.
  输出:location,x在数组S中的位置(若x不在数组S中,则为0).

index location(index low, index high)
{
  index mid;

  if (low > high) 
     reutrn 0;
  
  else {
     mid = (low + high) / 2;
     
     if (x == S[mid]) 
         return mid;
     else if ( x < S[mid])  
         return location(low, mid-1);
     else 
         return location(mid+1, high);
   }
}

####二分查找(迭代版本):

index location(type[] S)
{
  index low, high, mid;

  low = 1; high = n;
  
  while (low <= high)
   {
      mid = (low + high) / 2;
      if (x == S[mid])
             return  mid;
      else if (x < S[mid])
             high = mid - 1;
      else
             low = mid + 1;
   }
      return 0;
}  

2. 合并排序

  我们用两路合并(two-way merging)表示将两个有序数组合并为一个有序数组。重复应用此合并过程,可以完成对一个数组的排序。例如,对于一个包含16个元素的数组来说,可以将其分为两个大小各为8的子数组,对这两个数组进行排序,然后合并它们,生成有序数组。同样,可以将每个大小为8的子数组进一步划分为两个大小为4的子数组,然后对这些子数组进行排序和合并。最终,子数组的大小将变为1,大小为1的数组显然是有序的。这一过程称为“合并排序”。

合并排序:

  问题:将n个元素的数组排列为非递减顺序。
  输入:正整数n;数组S,其索引范围为1~n。
  输出:数组S,其中的元素按非递减顺序排列。

void mergsort(int n, type[] S)
{
  if (n > 1){
   
int h = n / 2;
int m = n - h;
type U[1..h], V[1..m];
将S[1]至S[h]复制到U[1]至U[h];
将S[h+1]至S[n]复制到V[1]至V[m];

mergsort(h, U);
mergsort(m, V);

merge(h, m, U, V, S);
 
  }
}

void merge(int h, int m, type[] U, type[] V, type[] S)
{
  index i, j, k;

  i = 1; j = 1; k = 1;
  
  while ( i <= h && j <= m) 
    {
      if (U[i] < V[j])
        {
          S[k] = U[i];
          i++;
        }
     else
        {
          S[k] = V[j];
          j++;
        }
     k++;
    }
  if ( i > h)
     将V[j]至V[m]复制到S[k]至S[h+m];
  else
     将U[i]至U[h]复制到S[k]至S[h+m];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值