算法详解之分治法具体实现

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。

分治法解题的一般步骤:

(1)分解,将要解决的问题划分成若干规模较小的同类问题;

(2)求解,当子问题划分得足够小时,用较简单的方法解决;

(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。

一言以蔽之:分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

在认识分治之前很有必要先了解一下递归,当然,递归也是最基本的编程问题,一般接触过编程的人都会对递归有一些认识.为什么要先了解递归呢?你看,根据上面所说的,我们就要将一个问题分成若干个小问题,然后一一求解并且最后合并,这就是一个递归的问题,递归的去分解自身,递归的去解决每一个小问题,然后合并…

关于递归,这里举一个最简单的例子,求N!;

我们只需要定义函数

代码如下:

int calculate(int n)

{

if(n==1)

return 1;

else

return n*calculate(n-1);   //调用自身…

}

好了,有了递归的铺垫,我们下来来看一看一个分治算法的问题,归并排序问题…

基本思想:

将待排序元素分成大小大致相同的2个子集合(递归直到最小的排序单元),分别对2个子集合进行排序,最终将排好序的子集合合并成为所要求的排好序的集合。

下面我们用一张图来展示整个流程,最下面的(姑且叫他第一层)是原始数组分成了8个最小排序问题,各自只有一个元素,故不需要排序,大家可以看到,我们通过分而治之的思想把对最初数组的排序分为了若干个只有一个元素的小数组的排序,然后第二层,我们进行了合并,将每两个最小排序结果合并为有两个元素的数组,然后逐层往上进行合并,就有了最后的结果…

下面我们来看一下这个算法的具体实现,下面的MERGE-SORT (A, p, r)表示对数组A[p->r]的排序过程.其中p->r代表从p到r.

MERGE-SORT (A, p, r)

1.     IF p < r                                                    // 进行A[p->r]的排序过程自然需要p<r的前提条件

2.         THEN q = [(p + r)/2]                           // 将当前的排序问题一分为二,分别进行处理

3.                 MERGE-SORT (A, p, q)                //继续递归看能不能将问题继续一分为二,处理A[p->q]的排序

4.                 MERGE-SORT (A, q + 1, r)          // 继续递归看能不能将问题继续一分为二处理A[q+1->r]的排序

5.                 MERGE (A, p, q, r)                       // 合并当前结果

到这里,分治算法的精髓已经出来了,我们通过递归将问题进行分解到足够小…继而进行结果计算…然后再将结果合并.

下面来处理一下边角料的工作,呵呵,让大家看到一个完整的归并排序的例子,整个算法总结系列我都没有很好的使用伪代码,而是使用我认为广泛使用的C语言代码来进行代码诠释.实际上,描述算法最好还是使用伪代码比较好,这里我对我前面的四篇文章没有使用伪代码而小小的鄙视一下自己,太不专业了..呵呵

以下算法MERGE (A, p, q, r )表示合并A[p->q]和A[q+1->r]这两个已经排序好的数组

MERGE (A, p, q, r )

1.      n1 ← q − p + 1                                                          //计算A[p->q]的长度
2.      n2 ← r − q                                                                //计算A[q+1->r]的长度
3.      Create arrays L[1 . . n1 + 1] and R[1 . . n2 + 1]       //创建两个数组
4.      FOR i ← 1 TO n1
5.            DO L[i] ← A[p + i − 1]
6.      FOR j ← 1 TO n2
7.            DO R[j] ← A[q + j ]        //4-7行是将原数组中A[p->r]的元素取出到新创建的数组,我们的操作是基于临时数组的操作
8.      L[n1 + 1] ← ∞
9.      R[n2 + 1] ← ∞                   //8-9行设置界限..
10.    i ← 1
11.    j ← 1
12.    FOR k ← p TO r
13.         DO IF L[i ] ≤ R[ j]
14.                THEN A[k] ← L[i]
15.                        i ← i + 1
16.                ELSE A[k] ← R[j]
17.                        j ← j + 1                //12-17行进行排序合

这里我还是提供一个具体的实现,请见下面的代码

C语言代码

关于代码注释,请见博客上面的伪代码注释..

代码如下:

#include<stdio.h>
int L[100],R[100];
void merge(int numbers[],int left, int mid, int right)
        {
            int n1=mid-left+1;
            int n2=right-mid;
            int i,j,k;
            for(i=1;i<=n1;i++)
             L[i]=numbers[left+i-1];
            for( j=1;j<=n2;j++)
             R[j]=numbers[mid+j];
            L[n1+1]=99999;
            R[n2+1]=99999;

i=1;
            j=1;

for(k=left;k<=right;k++)
            if(L[i]<=R[j])
               {
                   numbers[k]=L[i];
                   i++;
                   }
                 else
                  {
                       numbers[k]=R[j];
                       j++;
                  }
        }

void mergeSort(int numbers[],int left, int right)

{
    if(left<right)
    {
                int mid;
            mid = (right + left) / 2;
            mergeSort(numbers, left, mid);
            mergeSort(numbers, mid+1, right);
            merge(numbers,left, mid, right);
        }

}

int main()
{
    int numbers[]={5,2,4,6,1,3,2,6};
    mergeSort(numbers,0,7);
    for(int i=0;i<8;i++)
    printf("%d",numbers[i]);
    }

归并排序算法的时间复杂度是O(nlogn),对于冒泡排序的O(n*n),效率还有有比较好的提高..

其实本人原来在学习的时候好长一段时间不理解为什么时间复杂度会是O(nlogn),像冒泡排序就比较好理解,有两个for循环,问题的规模随着n变大而变大,算法时间复杂度自然就是O(n*n),后面花了一些时间来阅读一些资料才明白其原理,这里我已经将资料地址放到了本文最后,有兴趣的也可以去看看.简单的描述一下为什么会是O(nlogn)

大家看看,我们的例子,解一个8个元素的数组,我们用到了几层?是四层,假设我们这里有n个元素,我们会用到多少层?根据一定的归纳总结,我们知道我们会用到(lgn)+1层..(lgn)+1层需要用到lgn层次的合并算法.现在再看看MERGE (A, p, q, r )的复杂度是多少,毫无疑问O(n),故其归并排序的算法时间复杂度是O(nlogn).当然这个结果还可以通过其他的方法计算出来,我这里是口语话最简洁的一种..

下面来一张算法时间复杂度的与n规模的关系图..

(0)

相关推荐

  • 算法详解之分治法具体实现

    分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同.求出子问题的解,就可得到原问题的解. 分治法解题的一般步骤: (1)分解,将要解决的问题划分成若干规模较小的同类问题: (2)求解,当子问题划分得足够小时,用较简单的方法解决: (3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解. 一言以蔽之:分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之. 在认识分治之前很有必要先了解一下递

  • Java垃圾回收之分代收集算法详解

    概述 这种算法,根据对象的存活周期的不同将内存划分成几块,新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法.可以用抓重点的思路来理解这个算法. 新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率.另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销. 新生代 在新生代,每次垃圾收集器都发现有大批对象死去,只有少量存活,采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集:可以参看我之前写的Java垃圾回收之复制算法详解 老年代

  • python查找与排序算法详解(示图+代码)

    目录 查找 二分查找 线性查找 排序 插入排序 快速排序 选择排序 冒泡排序 归并排序 堆排序 计数排序 希尔排序 拓扑排序 总结 查找 二分查找 二分搜索是一种在有序数组中查找某一特定元素的搜索算法.搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束:如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较.如果在某一步骤数组为空,则代表找不到.这种搜索算法每一次比较都使搜索范围缩小一半. # 返回 x 在 ar

  • python算法演练_One Rule 算法(详解)

    这样某一个特征只有0和1两种取值,数据集有三个类别.当取0的时候,假如类别A有20个这样的个体,类别B有60个这样的个体,类别C有20个这样的个体.所以,这个特征为0时,最有可能的是类别B,但是,还是有40个个体不在B类别中,所以,将这个特征为0分到类别B中的错误率是40%.然后,将所有的特征统计完,计算所有的特征错误率,再选择错误率最低的特征作为唯一的分类准则--这就是OneR. 现在用代码来实现算法. # OneR算法实现 import numpy as np from sklearn.da

  • python实现决策树C4.5算法详解(在ID3基础上改进)

    一.概论 C4.5主要是在ID3的基础上改进,ID3选择(属性)树节点是选择信息增益值最大的属性作为节点.而C4.5引入了新概念"信息增益率",C4.5是选择信息增益率最大的属性作为树节点. 二.信息增益 以上公式是求信息增益率(ID3的知识点) 三.信息增益率 信息增益率是在求出信息增益值在除以. 例如下面公式为求属性为"outlook"的值: 四.C4.5的完整代码 from numpy import * from scipy import * from mat

  • java 中归并排序算法详解

    java 中归并排序算法详解 归并排序算法,顾名思义,是一种先分再合的算法,其算法思想是将要排序的数组分解为单个的元素,每个元素就是一个单个的个体,然后将相邻的两个元素进行从小到大或从大到小的顺序排序组成一个整体,每个整体包含一到两个元素,然后对相邻的整体继续"合"并,因为每个整体都是排过序的,因而可以采用一定的算法对其进行合并,合并之后每个整体包含三到四个元素,继续对相邻的整体进行合并,直到所有的整体都合并为一个整体,最终得到的整体就是将原数组进行排序之后的结果. 对于相邻的整体,其

  • python中实现k-means聚类算法详解

    算法优缺点: 优点:容易实现 缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢 使用数据类型:数值型数据 算法思想 k-means算法实际上就是通过计算不同样本间的距离来判断他们的相近关系的,相近的就会放到同一个类别中去. 1.首先我们需要选择一个k值,也就是我们希望把数据分成多少类,这里k值的选择对结果的影响很大,Ng的课说的选择方法有两种一种是elbow method,简单的说就是根据聚类的结果和k的函数关系判断k为多少的时候效果最好.另一种则是根据具体的需求确定,比如说进行衬衫尺寸的聚

  • Java垃圾回收机制算法详解

    概述 Java GC(Garbage Collection,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对JVM中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息的保证JVM中的内存空间,防止出现内存泄露和溢出问题. 在真实工作中的项目中,时不时的会发生内存溢

  • C++ OpenCV实现图像双三次插值算法详解

    目录 前言 一.图像双三次插值算法原理 二.C++ OpenCV代码 1.计算权重矩阵 2.遍历插值 3. 测试及结果 前言 近期在学习一些传统的图像处理算法,比如传统的图像插值算法等.传统的图像插值算法包括邻近插值法.双线性插值法和双三次插值法,其中邻近插值法和双线性插值法在网上都有很详细的介绍以及用c++编写的代码.但是,网上关于双三次插值法的原理介绍虽然很多,也有对应的代码,但是大多都不是很详细.因此基于自己对原理的理解,自己编写了图像双三次插值算法的c++ opencv代码,在这里记录一

  • OpenCV图像分割之分水岭算法与图像金字塔算法详解

    目录 前言 一.使用分水岭算法分割图像 1.cv2.distanceTransform()函数 2.cv2.connectedComponents()函数 3.cv2.watershed()函数 二.图像金字塔 1.高斯金字塔向下采样 2.高斯金字塔向上采样 3.拉普拉斯金字塔 4.应用图像金字塔实现图像的分割和融合 前言 主要介绍OpenCV中的分水岭算法.图像金字塔对图像进行分割的方法. 一.使用分水岭算法分割图像 分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表

随机推荐