算法笔记-分治法

 分治法:将问题分割,从一个大块分解为一个个小块,将问题规模变小。先解决小问题,在不停的整合结果集。 

(1)合并排序

  思路:把数组(假如长度为8)中每一个元素分为一个数组(因为这个数组只有一个元素,所以可以看成是有序数组)。然后不停的把两个有序组数组成一个有序数组(两个数组谁的第一个小就array_shift谁)。过程图:

   代码:

 1 <?php
 2 $arr = [42, 15, 20, 6, 8, 38, 50, 12];
 3 
 4 function sliceArr($arr)                 //切分
 5 {
 6     $arr = array_chunk($arr, 1);
 7     while (count($arr) != 1)
 8         $arr = array_chunk ($arr, 2);
 9     return $arr;
10 }
11 
12 function mergeArr($arr)                 
13 {
14     if (is_array($arr[0][0])) {
15         $arr = [mergeArr($arr[0]), mergeArr($arr[1])];
16     }
17     $c = count($arr[0]) + count($arr[1]);
18     for ($i=0; $i<$c; $i++) {         //把两个有序数组合并成一个有序
19         $ret[] = (empty($arr[1]) || (! empty($arr[0]) && $arr[0][0] < $arr[1][0])) ? array_shift($arr[0]) : array_shift($arr[1]);
20     }
21     return $ret;
22 }
23 print_r(mergeArr(sliceArr($arr, 1)));
View Code

  (2)快速排序

  思路:不停的根据数组的第一个值把数组分为三部分(大于,小于和等于这个值),然后再拼到一起。

  代码:

 1 <?php
 2 $arr = [42, 15, 20, 6, 8, 38, 50, 12];
 3 
 4 function quickSort($arr)
 5 {
 6     if (count($arr) < 2)
 7         return $arr;
 8     
 9     $max = $min = $mid = array();
10     foreach ($arr as $v) {
11         $v <= $arr[0] ? $v == $arr[0] ? $mid[] = $v : $min[] = $v : $max[] = $v;
12     }
13     return array_merge(quickSort($min), $mid, quickSort($max));
14 }
15 print_r(quickSort($arr));
View Code

 (3)大数相加

  思路:比如3431+235可以转换为(1+5)+(30+30)+(400+200)+(3000),可以用数组保存数组,比如3431可以表示为[1, 3, 4, 1],其中键值表示位数,比如$arr[0]表示个位(10的0次方),$arr[1]表示十位(10的1次方)。注意处理进位问题

  代码:

 1 <?php
 2 function add($n1, $n2)
 3 {
 4     list($narr1, $narr2) = is_array($n1) ? [$n1, $n2] : [splitNum($n1), splitNum($n2)];
 5     $c = count($narr1) > count($narr2) ? count($narr1) : count($narr2);
 6     for ($i=0; $i<$c; $i++) {                                                   //各位次依次相加
 7         list($res[], $carry) = ($sum = $narr1[$i] + $narr2[$i] + $carry) >= 10 ? [$sum - 10, 1] : [$sum, 0];       
 8     }
 9     $carry && $res[] = $carry;                                                  //进位
10     return $res;
11 }
12 
13 function splitNum($n)       //例如:将213转换为[3, 1, 2]
14 {
15     $res = [];
16     while($n != 0) {
17         list($res[], $n) = [$n % 10, intval($n / 10)];
18     }
19     return $res;
20 }
View Code

 (4)大数相乘

  思路:比如3431*235可以转换为(3*10^3 + 4*10^2 + 3*10^1 + 1*10^0)*(2*10^2 + 3*10^1 + 5*10^1),然后乘法分配律。比如计算3*10^3 * 3*10^1,只计算3*3就可以了,转换为数组之后,数组前面填充(3+1)个0,然后再利用上面的加法,把全部项加到一起

  代码:

 1 <?php
 2 function add($n1, $n2)
 3 {
 4     list($narr1, $narr2) = is_array($n1) ? [$n1, $n2] : [splitNum($n1), splitNum($n2)];
 5     $c = count($narr1) > count($narr2) ? count($narr1) : count($narr2);
 6     for ($i=0; $i<$c; $i++) {                                                   //各位次依次相加
 7         list($res[], $carry) = ($sum = $narr1[$i] + $narr2[$i] + $carry) >= 10 ? [$sum - 10, 1] : [$sum, 0];       
 8     }
 9     $carry && $res[] = $carry;                                                  //进位
10     return $res;
11 }
12 
13 function mult($n1, $n2)
14 {
15     list($narr1, $narr2, $res) = [splitNum($n1), splitNum($n2), []];
16     foreach ($narr1 as $k1 => $v1) {
17         foreach ($narr2 as $k2 => $v2) {
18             //array_merge后面的是相乘的结果,前面的是进位 比如200 * 80 = 2*8 000 表示为[0, 0, 0, 6, 1]
19             $tmp = array_merge(array_fill(0, $k1 + $k2, 0), splitNum($v1 * $v2));
20             $res = add($tmp, $res);
21         }
22     }
23     return $res;
24 }
25 
26 function splitNum($n)       //例如:将213转换为[3, 1, 2]
27 {
28     $res = [];
29     while($n != 0) {
30         list($res[], $n) = [$n % 10, intval($n / 10)];
31     }
32     return $res;
33 }
34 print_r(mult(3278, 29926));
View Code

猜你喜欢

转载自www.cnblogs.com/wangjianheng/p/11720375.html