分治法(思想篇)

Posted nibaba131237

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分治法(思想篇)相关的知识,希望对你有一定的参考价值。

To iterate is human, to reverse, divine. // 迭代乃人工, 递归显神通。

虽说如此,但是我们发现很多时候我们用到的是迭代,而不是递归 ???

举个栗子 1.数组求和

1.1迭代法 

1 int sum1(int A[], int n){
2     int sum = 0;                        //O(1)
3     for(int i = 0; i < n; i++){     //O(n)
4         sum += A[i];                  //O(1)
5     }
6     return sum;                        //O(1)
7 }
8 //无论 A[] 内容如何,都有:T(n) = 1 + n * 1 + 1 = O(n)

 

此处可用减而治之的思想//单调性

将原问题分成两个子问题,

令其中一个问题规模缩减,并治理,//未被加和的A[i]

另一个问题规模不变,也进行治理,//每次加和的A[i]

最后将两个子问题合并。

1.2线性递归法

 

void sum2(int A[], int n){
    return (n < 1) ? 
        0 : sum2(A, n - 1) + A[n - 1];
}//T(n) = O(n)

 

2.数组倒置

其实这个在c++里有相应的函数,但是作为acmer,凡是用啥都得自己造 /坚强

2.1递归法

void Reverse(int* A, int lo, int hi){
    if(lo < hi){//需要两个递归基
        swap(A[lo], A[hi]);
        reverse(A, lo + 1, hi - 1);
    }
    else{
        //考虑 0 和 1 两个特殊情况
        return;
    }
}

在此之外,我特地查了一下c++库中reverse()的代码

// reverse algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::reverse
#include <vector>       // std::vector

int main () {
  std::vector<int> myvector;

  // set some values:
  for (int i=1; i<10; ++i) myvector.push_back(i);   // 1 2 3 4 5 6 7 8 9

  std::reverse(myvector.begin(),myvector.end());    // 9 8 7 6 5 4 3 2 1

  // print out content:
  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout <<   << *it;
  std::cout << 
;

  return 0;
}
//template <class BidirectionalIterator>
//  void reverse (BidirectionalIterator first, BidirectionalIterator last)
//{
//  while ((first!=last)&&(first!=--last)) {
//    std::iter_swap (first,last);
//    ++first;
//  }
//}           //用了一个双向迭代器,思路大致一样

总的来说,就是一个分而治之的思想

将原问题分解成两个子问题,

同时进行治理,

最后合并治理。

由此我们是不是也能对刚刚第一个数组求和用这种方法呢

1.3二分递归法

void sum3(int A[], int lo, int hi){
    if(lo == hi){
        return A[lo];
    }
    else{
        int mi = (lo + hi) >> 1;
        return sum3(A, lo, mi) + sum3(A, mi + 1, hi);
    }
    
}     // T(n) = O(n)

3.MAX

在区间 [lo, hi)里找出最大的两个整数A[x1] 和 A[x2]   //A[x1] > A[x2]

要求元素比较次数尽可能的少

3.1迭代

 

 1 void max1(int A[], int lo, int hi, int &x1, int &x2){
 2     for(x1 = lo, int i = lo + 1; i < hi; i++){
 3         if(A[x1] < A[i]){
 4             x1 = i;
 5         }
 6         
 7     } //比较了n - 1次
 8     for(x2 = lo, int i = lo + 1; i < x1; i++){
 9         if(A[x2] < A[i]){
10             x2 = i;
11         }
12         
13     }
14     for(int i = x1 + 1; i < hi; i++){
15         if(A[x2] < A[i]){
16             x2 = i;
17         }
18     }
19 
20 }  // 总共比较了2 * n - 3 次

 

我们不妨改变一下思路:先选 A[lo] 和A[lo + 1] 为基准找出两者间最大数的下标给x1,最小数下标给x2,

           然后让A[x2] 和后面的元素比较,如果有比这个元素大的交换下标,

           让A[x2]再和A[x1]比较,如果比这个元素大,那么再交换下标。

 

 

void max1(int A[], int lo, int hi, int &x1, int &x2){
    if(A[x1 = lo] > A[x2 = lo + 1]){
        swap(x1, x2);
    }
    for(int i = lo + 2; i < hi; i++){
        if(A[x2] < A[i]){
            if(A[x1] < A[x2 = i]){
                swap(x1, x2);
            }
        }
    }

}  // best  比较了1 + (n - 2) * 1 = n - 1 次
   // worst 比较了1 + (n - 2) * 2 = 2 * n - 3 次

似乎没有改变最糟糕的情况

不妨递归 + 分治

仿照上面二分递归的方法

思路:在数组两边选出x1L, x2L, x1R, x2R  // x1L  >  x2L, x1R  >  x2R  

   比较两个最大的x1L, x1R, 两个中最大的即为x1

void max2(int A[], int lo, int hi, int &x1, int &x2){
    if(lo + 2 == hi){
        return;
    }
    else if(lo + 3 == hi){
        return;
    }
    else{
        int mi = (lo + hi) >> 1;
        int x1L, x2L, x1R, x2R;
        max2(A, lo, mi + 1, x1L, x2L);
        max2(A, mi + 1, hi, x1R, x2R);
        if(A[x1L] > A[x1R]){
            x1 = x1L;
            x2 = (x2L > x1R)? x2L : x1R;
        }
        else{
            x1 = x1R;
            x2 = (x2L > x1L)? x2L : x1L;
        }
        
    }

}  //T(n) = 2 * T(n / 2) + 2 = 5 * n / 3 - 2

 

 

   

以上是关于分治法(思想篇)的主要内容,如果未能解决你的问题,请参考以下文章

算法导论—分治法思想动态规划思想贪心思想

对分治法思想的体会

分治法

递归与分治思想:治思想 && 折半查找法(迭代 && 递归)

分治法思想与体会

对分治法思想的体会 & 结对编程情况汇报