分治法(思想篇)

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 << ‘\n‘;

  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

   

原文地址:https://www.cnblogs.com/nibaba131237/p/12326038.html

时间: 2024-10-11 14:33:31

分治法(思想篇)的相关文章

分治法思想与体会

分治,即分而治之,是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同.递归地解决这些子问题,然后将各子问题的解合并得到原文题的解.其算法设计模式如下: divide-and-conquer(P){ if (|P|<=n0) adhoc(P); divide P into smaller subinstances P1,P2,...,Pk; for (i=1; i<=k; i++) yi = divide-and-conquer(Pi); return merge

基于分治法的找秩(maxium rank finding)策略

rank finding 问题的概述如下: 在一群离散的点中,检查对于每个点,是否有x值和y值都小于它的点,若有,则该点的秩为这些小于它的点的数量. 分治法思想: 以界面上所有点中x值的中位数为基准,递归地把目标区域分成两部分,直到每个区域中的点在一个以下,对于每一个细小的区域,检查其left child区域中的点的y值和right child区域中的点的y值,以对right child中的点的秩进行统计. 该方法通过分治排除了水平位置不符合标准的点,有效的减少了统计次数. 代码示例如下: #i

2.3.1 分治法

插入排序使用了增量方法:在排序子数组A[1..j-1]后,将单个元素A[j]插入子数组的适当位置,产生排序好的子数组A[1..j]. 分治法,该算法的最坏情况运行时间比插入排序要少得多.分治算法的优点之一是,通过使用第4章介绍的技术往往很容易确定其运行时间. 分治法 许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题.这些算法典型地遵循分治法思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子

【分治法】最接近点对问题(转)

转自:http://blog.csdn.net/liufeng_king/article/details/8484284 问题场景:在应用中,常用诸如点.圆等简单的几何对象代表现实世界中的实体.在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息.例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点.这类问题是计算几何学中研究的基本问题之一. 问题描述:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,

递归与分治法

分治法思想 把问题分解为k个规模较小的子问题,这些子问题(互相独立且)结构与原来问题的结构相同,再递归地求解这些子问题. 问题分解成子问题:(divide) 当达到某个阈值n0时,给出直接求解的方法:(conquer) 最后把各个子问题的解合并起来,得到原来问题的解:(merge) 算法设计伪代码 时间复杂性 分析分治法的运行时间,先列出递归方程,例如 典型例子 mergesort等 原文地址:https://www.cnblogs.com/eniac1946/p/8733531.html

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

1.对分治法思想的体会 分治法就是将问题分解为规模更小的子问题,通过一一解决这些子问题,再将这些已经解决了的子问题合并起来,就得到了原问题的解.通过分治法的思想,我们可以更加轻松的解决规模很大的问题,也使得代码更加清晰,易于理解.我们在大一的时候其实已经接触过分治法的思想了,像二分查找,就是利用了分治法思想,使得查找算法的时间复杂度更低. 2.结对编程情况汇报 我和我的搭档一般情况下是独立编程,但是当我们其中一个或着两个人遇到编程难题时,就会互相讨论,一起寻找合适的算法去解决问题. 在之后的学习

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

一.对分治思想的体会 1.帮助我们解决问题. 分治法,对大的问题拆封成规模较小的问题,我们求解小问题,再把小问题的答案合并起来,得出大问题的答案.大问题思考起来比较乱,犯迷糊,不易想到解决方法,比如汉诺塔问题.分解成小问题,我们就容易想出方法来解决问题 2.时间复杂度低 分治法思想核心是递归,递归的时间复杂度低.算法的复杂度低,提高了算法的质量 二.结对编程情况汇报 3道题都做完且答案正确,第三道题,没有按时间复杂度为O(logn)编程,后来查找资料知道如何写出O(logn)的算法.第一题,开始

LeetCode刷题总结-双指针、位运算和分治法篇

本文总结LeetCode上有关双指针.位运算和分治法的算法题,推荐刷题总数14道.具体考点分析如下图: 一.双指针 1.字符串和数组问题 题号:424. 替换后的最长重复字符,难度中等 题号:828. 独特字符串,难度困难 题号:923. 三数之和的多种可能,难度中等 2.实际场景应用问题 题号:826. 安排工作以达到最大收益,难度中等 3.元素对问题 题号:986. 区间列表的交集,难度中等 二.位运算 1.字符串和数组问题 题号:137. 只出现一次的数字 II,难度中等 题号:318.

分治法(实战篇之二分查找)

啥也不说,先上题!!! 二分查找 Description 给你n个整数和一个整数k.问:n个数中是否存在整数k,如果存在,输出“Yes”,否则输出“No”. Input 输入包含多组测试用例,对于每组测试用例: 输入一个数字n (1 <= n <= 10^7) 接下来输入n个不同的数字Ai( |Ai| <= 10^7). 接下来输入整数k ( |k| <= 10^7).代表要找的数字k Output n个数中是否存在整数k,如果存在输出“Yes”,否则输出“No”.每组数据占一行.

专题:分治法

分治法(Divide and Conquer) 作为五大算法之一的分治法,可算是最早接触的一种算法.分治法,与其说是一种算法,不如将其称为策略来的更贴切一些.算法的思想就是将大问题分成小问题,并解决小问题之后合并起来生成大问题的解. 分治法的精髓: 分--将问题分解为规模更小的子问题: 治--将这些规模更小的子问题逐个击破: 合--将已解决的子问题合并,最终得出“母”问题的解: 分治法的作用,自然是让程序更加快速地处理问题.比如一个n的问题分解成两个n/2的问题,并由两个人来完成,效率就会快一些