经典算法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++;//转移到下一个数组元素
15         var_dump($arr);
16         echo ‘<br >‘;
17     }
18     return $arr;
19 }

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

解法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

时间: 2024-10-22 08:00:25

经典算法mark的相关文章

数据挖掘十大经典算法

一. C4.5  C4.5算法是机器学习算法中的一种分类决策树算法,其核心算法是ID3 算法.   C4.5算法继承了ID3算法的优点,并在以下几方面对ID3算法进行了改进: 1) 用信息增益率来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性的不足: 2) 在树构造过程中进行剪枝: 3) 能够完成对连续属性的离散化处理: 4) 能够对不完整数据进行处理. C4.5算法有如下优点:产生的分类规则易于理解,准确率较高.其缺点是:在构造树的过程中,需要对数据集进行多次的顺序扫描和排序,因而导

动态展示十大经典算法

算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序n个项目要Ο(nlogn)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(innerloop)可以在大部分的架构上很有效率地被实现出来. 快速排序使用分治法(Divideandconquer)策略来把一个串行(list)分为两个子串行(sub-lists). 算法步骤: 1.从数列中挑出一个元素,称为“基准”(pivot),

数据挖掘领域十大经典算法初探

译者:July   二零一一年一月十五日 ----------------------------------------- 参考文献:国际权威的学术组织ICDM,于06年12月年评选出的数据挖掘领域的十大经典算法:C4.5, k-Means, SVM, Apriori, EM, PageRank, AdaBoost, kNN, Naive Bayes, and CART.==============博主说明:1.原文献非最新文章,只是本人向来对算法比较敏感.感兴趣,便把原文细看了下,翻译过程中

【白话经典算法系列之十七】 数组中只出现一次的数 其他三次

本文地址:http://blog.csdn.net/morewindows/article/details/12684497转载请标明出处,谢谢. 欢迎关注微博:http://weibo.com/MoreWindows 首先看看题目要求: 数组A中,除了某一个数字x之外,其他数字都出现了三次,而x出现了一次.请给出最快的方法找到x. 这个题目非常有意思,在本人博客中有<位操作基础篇之位操作全面总结>这篇文章介绍了使用位操作的异或来解决——数组中其他数字出现二次,而x出现一次,找出x.有<

三白话经典算法系列 Shell排序实现

山是包插入的精髓排序排序.这种方法,也被称为窄增量排序,因为DL.Shell至1959提出命名. 该方法的基本思想是:先将整个待排元素序列切割成若干个子序列(由相隔某个"增量"的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序. 由于直接插入排序在元素基本有序的情况下(接近最好情况),效率是非常高的,因此希尔排序在时间效率上比前两种方法有较大提高. 以n=10的一个数组49, 38, 65, 97

白话经典算法系列之四 直接选择排序及交换二个数据的正确实现

分类: 白话经典算法系列 2011-08-09 11:15 16682人阅读 评论(29) 收藏 举报 算法面试c 直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接播放排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后. 设数组为a[0…n-1]. 1.      初始时,数组全为无序区为a[0..n-1].令i=0 2.      在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交

白话经典算法系列之七 堆与堆排序

堆排序与高速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先解说下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是全然二叉树或者是近似全然二叉树. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)不论什么一个子节点的键值. 2.每一个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 当父结点的键值总是大于或等于不论什么一个子节点的键值时为最大堆.当父结点的键值总是小于或等于不论什么一个子节点的键值时为最小堆.下图展示一个最小堆

【从零学习经典算法系列】分治策略实例——二分查找

1.二分查找算法简介 二分查找算法是一种在有序数组中查找某一特定元素的搜索算法.搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束:如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较.如果在某一步骤数组 为空,则代表找不到.这种搜索算法每一次比较都使搜索范围缩小一半.折半搜索每次把搜索区域减少一半,时间复杂度为Ο(logn). 二分查找的优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且

24点经典算法

1.概述 给定4个整数,当中每一个数字仅仅能使用一次:随意使用 + - * / ( ) ,构造出一个表达式,使得终于结果为24,这就是常见的算24点的游戏.这方面的程序非常多,一般都是穷举求解.本文介绍一种典型的算24点的程序算法,并给出两个详细的算24点的程序:一个是面向过程的C实现,一个是面向对象的java实现. 2.基本原理 基本原理是穷举4个整数全部可能的表达式,然后对表达式求值. 表达式的定义: expression = (expression|number) operator (ex