• 经典算法mark


      在平时找工作的时候,或多或少会遇到一些算法问题,很多都是比较经典或者网上已经流传很久的。只是我们没有接触过,所以不知道怎么解决。

      在这儿,我自己总结一些我遇到的一些经典算法,给自己增加一点记忆,也给需要的朋友看到学习一下。

    1. 倒水问题

        如题:一个容量为5升的杯子和一个容量为3升的杯子,水不限使用,要求精确得到4升水。

      这类题一般会有两种出题方式:

      A.简答

        这儿先给出简答的答案:其实结果又很多种,这儿给出倒水次数最少的一种。

        

      B.编程实现

        解法也比较多,我首先想到的DFS(深度优先)搜索,每次我们有6种选择,只要不断的尝试下去,总可以搜索完所有的状态,找到一个解。也可以用宽度优先搜索(BFS)。

      程序代码后续补上。

      后续也有其他版本的倒水问题,如:有三个容器,分别为20升,13升,7升。20升的容器装满水,要使20升和13升的容器里分别装10水,如何做到?

        简答的步骤和前面类似:

        

    2.猴子选大王问题

    题目:

    n只猴子围坐成一个圈,按顺时针方向从1到n编号。
    然后从1号猴子开始沿顺时针方向从1开始报数,报到m的猴子出局,再从刚出局猴子的下一个位置重新开始报数,
    如此重复,直至剩下一个猴子,它就是大王。

    设计并编写程序,实现如下功能:
    (1)   要求由用户输入开始时的猴子数$n、报数的最后一个数$m。
    (2)   给出当选猴王的初始编号。

    这个题直接想到的就是循环链表实现,或者数组实现。

    下面直接贴出代码:

     解法1:

     1 <?php
     2 $arr = array('1','2','3','4','5','6','7');//示例数组
     3 echo '<pre>The King is :<br/>';
     4 print_r(king($arr,11));
     5 function king($arr,$count){
     6     $i = 1;//从1开始
     7     while(count($arr) > 1){
     8         if($i%$count == 0){//用求余,计算数到的位,如果求余为0,所数到的位消除,压出数组中
     9             unset($arr[$i-1]);
    10         }else{//数到的位不是结束,把这一位放到数组末尾,并消掉这个位
    11             array_push($arr,$arr[$i-1]);
    12             unset($arr[$i-1]);
    13         }
    14         $i++;//转移到下一个数组元素17     
    15   }
    16 return $arr; 17 }

    这种实现方式是使用数组,如果当前数不是选中的,则将其移到数组最后。算法复杂度较高,当数据量较大时,处理效率低。

    解法2:

     1 /**
     2  *
     3  * @param int $n
     4  *            开始时的猴子数量
     5  * @param int $m
     6  *            报道的最后一个数
     7  *            (报到这个数的猴子被淘汰,然后下一个猴子重新从①开始报数)
     8  * @return int 猴子的初始编号
     9  */
    10 function monkeySelectKing($n, $m)
    11 {
    12     // 猴子的初始数量不能小于2
    13     if ($n < 2) {
    14         return false;
    15     }
    16     
    17     $arr = range(1, $n);
    18     // 将猴子分到一个数组里, 数组的值对应猴子的初始编号
    19     $unsetNum = 0;
    20     // 定义一个变量,记录猴子的报数
    21     
    22     for ($i = 2; $i <= $n * $m; $i ++) 
    23     // 总的循环次数不知道怎么计算,
    24     {
    25         // 不过因为循环中设置了return,所以$m*$len效率还可以
    26         foreach ($arr as $k => $v) {
    27             $unsetNum ++; // 每到一个猴子, 猴子报数+1
    28                          
    29             // 当猴子的报数等于淘汰的数字时:淘汰猴子(删除数组元素)
    30                          // 报数归0(下一个猴子从1开始数)
    31             if ($unsetNum == $m) {
    32                 // echo "<pre>";//打开注释,可以看到具体的淘汰过程
    33                 // print_r($arr);
    34                 unset($arr[$k]);
    35                 // 淘汰猴子
    36                 $unsetNum = 0;
    37                 // 报数归零
    38                 if (count($arr) == 1) 
    39                 // 判断数组的长度, 如果只剩一个猴子, 返回它的值
    40                 {
    41                     return reset($arr);
    42                 }
    43             }
    44         }
    45     }
    46 }
    47 
    48 var_dump(monkeySelectKing(100, 2));

    这种算法实现的复杂度为O(nm);当nm比较大时,这个效率就比较低。

    更简单的实现方式:也叫约瑟夫环的数学解法

    原理

    无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。
    为了讨论方便,先把问题稍微改变一下,并不影响原意:

      问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

      我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
          k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2
      并且从k开始报0。

    现在我们把他们的编号做一下转换:
    k     --> 0
    k+1   --> 1
    k+2   --> 2
    ...
    ...
    k-2   --> n-2
    k-1   --> n-1

    变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n

    如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

    令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

    递推公式
    f[1]=0;
    f[i]=(f[i-1]+m)%i;  (i>1)

    有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1

    最后得出的解法就是:

     1 /**
     2  * 约瑟夫环的数学解法  
     3  */
     4 function yuesefu($n,$m) {
     5     $r=0;
     6     for($i=2; $i<=$n; $i++) {
     7 
     8         $r=($r+$m)%$i;
     9     }
    10     return $r+1;
    11 }
    12 print_r(yuesefu(100,2));

    有关约瑟夫环的数学解法来自于:http://www.cppblog.com/Victordu/archive/2008/02/22/43082.html

  • 相关阅读:
    分布式集群系统下的高可用session解决方案
    Google大数据三篇著名论文中文版
    12款免费与开源的NoSQL数据库介绍
    揭密Node.js 盛行的因由(转载)
    lion.ec开源框架简介(原创)
    2.2 Hadoop Studio 使用 [Hadoop学习笔记]
    spring3.2+ehcache 注解使用
    Venus 是一个简单的、高性能、高并发能力的java 开源Remoting框架
    ZooKeeper原理分析
    Netty系列之Netty高性能之道(转载InfoQ)
  • 原文地址:https://www.cnblogs.com/lingshao/p/5648493.html
Copyright © 2020-2023  润新知