算法之分治算法

来源:互联网 发布:随身带着淘宝去异界301 编辑:程序博客网 时间:2024/05/01 07:03

1       分治思想

 (Divide-and-Conquer)

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

2       分治法解题的一般步骤

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

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

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

 通用框架:用递归的方式划分到一定程度

   分解 直接或递归求解子问题 组合

在递归求解子问题时同样要注意理清递归关系、递归出口、参数设置等问题。

divide_and_conquer(data){            if(data数据量较小)直接处理data得到结果,或者有个结束的判断,因为这个是在递归;            else            {                       A.将data分为多个部分data1,data2,.....datan                       并分别递归调用divide_and_conquer处理各个部分!                       B.合并data1,data2,....datan处理之后的结果!             }}


 

3       实例1---归并排序

思想:分而治之

每个递归过程涉及三个步骤:

1、 分解:把待排序的n个元素的序列分解成两个子序列,每个子序列包括n/2个元素。

2、 治理:把每个子序列分别调用归并排序MergeSort,进行递归操作。

3、 合并:合并两个排好序的子序列,生成排序结果

 

归并排序的工作原理:

1、 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列。

2、 设定两个指针,最初位置分别为两个已经排序序列的起始位置

3、 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置

4、 重复步骤3直到某一指针达到序列尾

5、 将另一序列剩下的所有元素直接复制到合并序列尾

代码:

//我认为此题的关键时理解了temparray 这个数组,它是临时的存放每一次的排序结果
//分治还有个明显的特点是left和right的下标,也就是说有一个分的标志void Merge(int* array, int* tempararay, int left, int middle, int right){//左指针尾int leftend = middle-1;//右指针头int rightstart = middle;int tempIndex = left;//数组合并后的length长度int tempLength = right - left + 1;//相当于排序,把临时的结果放入到tempararay中while( (left <= leftend) && (rightstart <= right) ){if(array[left]<array[rightstart]){tempararay[tempIndex++] = array[left++];}else{tempararay[tempIndex++] = array[rightstart++];}}//判断左序列是否结束while(left <= leftend)tempararay[tempIndex++] = array[left++];while(rightstart <= right)tempararay[tempIndex++] = array[rightstart++];//交换临时排序的数据for(int i=0; i<tempLength; i++){array[right] = tempararay[right];cout<<array[right]<<" ";right--;}if(tempLength)cout<<endl;}void MergeSort(int* array, int* temparray, int left, int right){if(left < right){//1、分解划分成前后两部分int middle = (left+right)/2;//2、对分解治理MergeSort(array, temparray, left, middle);//2、对分解治理MergeSort(array, temparray, middle+1, right);//3、合并Merge(array, temparray, left, middle+1, right);}}int main(){int a[] = {13,7,8,3,60,50,4};int len = sizeof(a)/sizeof(int);//需要申请一块临时的内存保存排序结果int* b = new int[len]; MergeSort(a,b,0,len-1);for(int i=0; i<len; i++){cout<<a[i]<<",";}}


4       快速排序

 分而治之

//一趟排序,每一次一趟之后,前半部分是小的,后半部分是大的int Partition(int *array, int low, int high){//*array = *(array+low);int pivotKey = *(array+low);while(low<high){while(low<high && *(array+high)>pivotKey)--high;*(array+low) = *(array+high);while(low<high && *(array+low)<pivotKey)++low;*(array+high) = *(array+low);}*(array+low) = pivotKey;return low;}//再写分治法的代码:void QSort(int *array, int low, int high){if(low<high){//1. 分解int middle = Partition(array, low, high);//2. 递归求解QSort(array, low , middle-1);QSort(array, middle+1, high);//3. 合并:由于分解出的两个字序列是就地进行的,所以不需要再次合并}}void BinarySort(int *array, int length, int data){QSort(array,0,length-1);}


5       循环赛日程

n个选手的比赛日程可以通过n/2个选手的比赛日程来确定,递归调用,直到剩下两个选手的时候,比赛日程就可以很容易的确定

先计算出一部分,然后另一部分赋值

/// <param name="index">起始选手编号</param> /// <param name="num">选手的人数(因为是分治,所以每次砍半)</param>void GameCal(int index, int num){if( num == 2){GameList[index-1,0] = index;GameList[index-1,1] = index+1;GameList[index,0] = index+1;GameList[index,1] = index;}else{//分而递归GameCal(index, num/2);GameCal(index + num/2, num/2);//合并//将“左下角”填充到“右上角”//控制横坐标for(int i=index; i<index+num/2; i++){//控制纵坐标for(int j=num/2; j<num; j++){GameList[i-1][j] = GameList[(i-1) + num/2][j - num/2];}}//用于将“左上角”填充到“右下角”//控制横for(int i=index+num/2; i<index+num; i++){//控制纵坐标for(int j=num/2; j<num; j++){//对角赋值                        GameList[i - 1][j] = GameList[(i - 1) - num / 2][j - num / 2];}}}}

 

6        二分搜索法

int BinarySearch(int a[], int &x, int left, int right){while(left < right){int middle = (left + right)/2;if(a[middle] == x) return middle;if(a[middle] < x) right = middle - 1;else left = middle + 1;}}

7   最近点对问题

1)蛮力法:
计算每两个之间的距离,然后比较,也就是计算(n-1)与n个元素之间的比较
2)分治法:
     a.  找中线把n个元素分成左右两部分分别求得两边的最短距离,然后取两者中的最小者,计为len
     b.  在中线两边分别取len的距离。记录该距离范围内点的个数,中线左边有L个元素,右边有R个元素。
     c.  求左边元素到右边元素的距离看其是否小于之前记录的len,小则记录下来。
     d.  此时的右边元素只取y值和左边元素y值距离小于1的(减少循环次数)。
     e.  循环结束即可找到最小的距离

8   Strassen矩阵乘法

9 大整数乘法