程序员都会的五大算法之——分治算法,恶补

程序员都会的五大算法之——分治算法,恶补

首页动作格斗刺客合并更新时间:2024-07-20
一、算法概述

分治法可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。

二、分治策略的基本思想

简单来说,分治算法就是“分而治之”,即就是讲一个规模很大很复杂的问题分 成若干份相同或相似的更小的子问题,直到最后可以简单的直接求解,将所有子问题的解合并即得到原问题的解。

此处,需要注意:

三、时间复杂性

在分治算法中,假设原问题总规模为n,我们假设分解和合并所用时间为C(n)和D(n),假设W(x)为处理一个规模为x的问题所用的时间。每一个子问题是原问题规模的1/a。

如果原问题规模<=最小子问题的规模,既可以直接求解,那么其时间复杂度是一个常数。否则,W(n)=aW(n/a) C(n) D(n);

四、分治精髓

分治法的精髓:

五、经典例子:5.1 二分归并排序

设计思想:


1、将规模为n的原问题划分为规模为n/2的子问题; 2、继续划分,划分至规模为n/4的,继续一直到规模为1,可以直接求解,这块分治就结束了; 3、从规模1到n/2依次陆续归并排好序的子数组,直到原始数组。


伪代(pseudocode):


算法 MergeSort(A, p, r)

输入: 数组A[p......r]

输出:元素按从小到大排序的数组A

1.if p < r 2.then q <- (p r)/2 问题规模划分 3.MergeSort(A, p, q) 子问题1 4.MergeSort(A, q 1, r) 子问题2 5.Merge(A, p, q, r) 综合解


程序代码实现(Java实现):


public static void mergeSort(int[] nums, int begin, int end){ int length = end - begin 1; if(length <= 1){ return; } /*分*/ int mid = (begin end) / 2; mergeSort(nums, begin, mid); mergeSort(nums, mid 1, end); /*治*/ merge(nums, begin, mid, end); } public static void merge(int[] nums, int begin, int mid, inr end){ if(mid>=end){ return; } int length = end - begin 1; int [] temp = new int[length]; int k = 0; int i = begin; int j = mid 1; while(i<=mid&&j<=end){ if(num[i]<num[j]){ temp[k] = num[i]; i ; }else{ temp[k] = num[j]; j ; } k ; } /*传进来前部分已经排完序*/ if(i>mid){ while(j<=end){ temp[k] = nums[j]; j ; k ; } } if(j>end){ while(i<=mid){ temp[k] = num [i]; i ; k } } /*重新存进原数组*/ for(int m = 0; m < length; m ){ num[begin m] = temp[m]; } }


图解:

时间复杂度分析:

假设原问题规模为n,二分归并排序最坏情况: W(n)=2W(n/2) n-1 其中子问题最小规模为1,W(1)=1;

W(n)=nlogn - n 1 时间复杂度为O(nlogn)

5.2 汉诺塔

游戏介绍:

在汉诺塔游戏中,有三个塔座:A、B、C。几个大小各不相同,从小到大一次编号的圆盘,每个圆盘中间有一个小孔。最初,所有的圆盘都在A塔座上,其中最大的圆盘在最下面,然后是第二大,以此类推.

汉诺塔示意

该游戏的目的是将所有的圆盘从A塔移到B塔;C塔用来放置临时圆盘,游戏的规则如下:

设计思想:始终将较小的盘当做是一个整体

游戏完成过程:

在上述图中:我们将盘子由小到大分别记为1,2,3,4,5。

先将1挪到B,2挪到C,再将1从B挪到C,此时A上有3,4,5,B为空,C上有1,2。

再将3挪到B,C上的1也挪到A,C上的2挪到B,A上的1再挪到B,此时A上有45,B上有123,C为空

再将4挪到C,B上的1挪到A,B上的2移到C上,A上的1挪到C上,B上的3挪到A上,C上面的1挪到B上, 有1234

最后将

伪码(pesudocode):


算法Hanoi(A,C,n) // n个盘子从A到C

1.if n = 1 then move(A, C) 2.else Hanoi(A, B, n-1) 3.move(A, C) 4.Hanoi(B,C,n-1) 设n个盘子的移动次数为T(n) T(n) = 2T(n-1) 1 T(1) = 1


程序:


public static void solve(int n){ hanoi(n, "A", "B", "C"); } private static void hanoi(int n ,String a, String b,String c){ if(n==1){ move(n,a,c); } else { hanoi(n-1, a, c, b); // 将前n-1个圆盘从a挪到c,借助b move(n, a, c); // 将第n个直接移到c hanoi(n-1, b, a, c); // 将前n-1个圆盘从b挪到c,借助啊 } } private static void move(int n, String i, String j){ Systtem.out.println("第" n "个盘,从" i "塔移动到" j "塔"); }


时间复杂度分析:

hanoi塔游戏的递推公式为T(n)=2T(n-1) 1,时间复杂度为O(2^n)

六、分治法联系(C语言)6.1 求一组数据中最大的两个数和最小的两个数

输入输出实例:


10 代表10个数 1 3 5 7 9 10 8 6 4 2

max1=10 max2=9 min1=1 min2=2

代码实现:


#include <stdio.h> void maxtwo(int[], int, int, int*, int*); void mintwo(int[], int, int, int*, int*); int main() { int num,i; scanf("%d",&num); int a[num]; for(i=0;i<num;i ) scanf("%d",&a[i]); /********** Begin **********/ int max1, max2, min1, min2; maxtwo(a, 0, num-1, &max1, &max2); printf("max1=%d max2=%d\n",max1,max2); mintwo(a, 0, num-1, &min1, &min2); printf("min1=%d min2=%d\n",min1,min2); /********** End **********/ } void maxtwo(int a[], int i,int j,int *max1,int *max2){ int lmax1,lmax2,rmax1,rmax2; int mid; if(i==j){ *max1=a[i]; *max2=a[i]; }else if (i==j-2){ if (a[i]>=a[i 1]&&a[i 1]>=a[i 2]){ *max1=a[i]; *max2=a[i 1]; }else if (a[i]>=a[i 2]&&a[i 2]>=a[i 1]){ *max1=a[i]; *max2=a[i 2]; }else if (a[i 1]>=a[i]&&a[i]>=a[i 2]){ *max1=a[i 1]; *max2=a[i]; }else if (a[i 1]>=a[i 2]&&a[i 2]>=a[i]){ *max1=a[i 1]; *max2=a[i 2]; }else if (a[i 2]>=a[i]&&a[i]>=a[i 1]){ *max1=a[i 2]; *max2=a[i]; }else{ *max1=a[i 2]; *max2=a[i 1]; } }else{ mid=(i j)/2; maxtwo(a, i,mid,&lmax1,&lmax2); maxtwo(a, mid 1,j,&rmax1,&rmax2); if(lmax1>=rmax1){ *max1=lmax1; if (lmax2>=rmax2){ *max2=lmax2; }else{ *max2=rmax1; } }else{ *max1=rmax1; if (rmax2>=lmax2){ *max2=rmax2; }else { *max2=lmax1; } } } } void mintwo(int a[], int i, int j, int *min1, int *min2){ int lmin1, lmin2, rmin1, rmin2; int mid; if(i == j){ *min1 = a[i]; *min2 = a[i]; } else if(i == j - 2){ if(a[i]<=a[i 1]&&a[i 1]<=a[i 2]){ *min1 = a[i]; *min2 = a[i 1]; }else if(a[i]<=a[i 2]&&a[i 2]<=a[i 1]){ *min1 = a[i]; *min2 = a[i 2]; }else if(a[i 1]<=a[i]&&a[i]<=a[i 2]){ *min1 = a[i 1]; *min2= a[i]; }else if(a[i 1]<=a[i 2]&&a[i 2]<=a[i]){ *min1 = a[i 1]; *min2 = a[i 2]; }else if(a[i 2]<=a[i]&&a[i]<=a[i 1]){ *min1 = a[i 2]; *min2 =a[i]; }else{ *min1 = a[i 2]; *min2 = a[i 1]; } }else{ mid = (i j)/2; mintwo(a, i, mid, &lmin1, &lmin2); mintwo(a, mid 1, j, &rmin1, &rmin2); if(lmin1 <= rmin1){ *min1 = lmin1; if(lmin2 <= rmin2){ *min2 = lmin2; }else{ *min2 = rmin1; } }else{ *min1 = rmin1; if(rmin2 <= lmin2){ *min2 = rmin2; }else{ *min2 = lmin1; } } } }


6.2 分治法求一组数据的和

输入输出示例:


8 代表8个数 18 -38 -16 87 -27 74 31 100

分治法求出数组元素的和为:229


代码实现:


#include "stdio.h" /********** Begin **********/ int calculate_sum(int[], int, int); int main() { int i, num, sum; scanf("%d", &num); int a[num]; for(i = 0; i < num; i ){ scanf("%d", &a[i]); } sum = calculate_sum(a, 0, num-1); printf("分治法求出数组元素的和为:%d",sum); } /********** End **********/ int calculate_sum(int a[], int i, int j){ int mid, left_sum, right_sum; if(i == j){ return a[i]; // }else if(j-1 == 1){ // return a[i] a[j]; }else{ mid = (i j) / 2; left_sum = calculate_sum(a, i, mid); right_sum = calculate_sum(a, mid 1, j); return left_sum right_sum; } }


6.3 求一组数据中第k小的数

输入输出示例:


10 5 代表10个数据,求第5个小的元素 -34 95 -50 67 73 81 -38 10 -11 70

第5小的元素是10


代码实现:


#include<stdio.h> #define ArrLen 20 void mergeSort(int[], int, int); void merge(int[], int, int, int); int main() { int i, num, k; scanf("%d %d", &num, &k); int a[num]; for(i = 0; i < num; i ){ scanf("%d", &a[i]); } mergeSort(a, 0, num-1); printf("第%d小的元素是%d", k, a[k - 1]); return 0; } void merge(int arr[], int start, int mid, int end) { int result[ArrLen]; int k = 0; int i = start; int j = mid 1; while (i <= mid && j <= end) { if (arr[i] < arr[j]){ result[k ] = arr[i ]; } else{ result[k ] = arr[j ]; } } if (i == mid 1) { while(j <= end) result[k ] = arr[j ]; } if (j == end 1) { while (i <= mid) result[k ] = arr[i ]; } for (j = 0, i = start ; j < k; i , j ) { arr[i] = result[j]; } } void mergeSort(int arr[], int start, int end) { if (start >= end) return; int mid = ( start end ) / 2; mergeSort(arr, start, mid); mergeSort(arr, mid 1, end); merge(arr, start, mid, end); }




查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved