首页 > 代码库 > 笔试算法题(11):Josephus环 & Fibonacci序列

笔试算法题(11):Josephus环 & Fibonacci序列

出题:Josephus Cycle,约瑟夫环问题。k个数字连成一个环,第一个数字为1。首先从1开始计数删除第m个数字;然后从上次被删除的数字的下一个数字开始计数,删除第m个数字;重复进行第二步直到只剩下一个数字;输出最后剩下的一个数字;

分析:

  • 解法1:考虑到问题的特殊性,可以使用哑元素表示删除的元素从而避免由于删除元素带来的额外操作,所以链表实现的话不用考虑删除操作,数组实现的话不用考虑内存移动操作。当然完全可以不适用哑元素,对于链表而言可以节省查找时间,数组的话需要增加数组元素的平移开销;
  • 解法2:直接使用数学推导,设最终剩下的数字为f(k,m),其中(k为所有数字个数,m为跨距),可以推导当k=1时,f(k,m)=0;当k>1时,f(k,m)=[f(k-1,m)+m]%k。具体推导过程可以参考网页:(其实推导过程可以更简单)
    http://zhedahht.blog.163.com/blog/static/2541117420072250322938/

解题:

 1 struct Node {
 2         int v;
 3         Node *next;
 4 };
 5 /**
 6  * 哑元素为-1
 7  * 简单起见总长度缺省为8,m为指定的跨距
 8  * */
 9 int ListJose(int m) {
10         Node* b1=new Node(); b1->v=1;
11         Node* b2=new Node(); b2->v=2;b1->next=b2;
12         Node* b3=new Node(); b3->v=3;b2->next=b3;
13         Node* b4=new Node(); b4->v=4;b3->next=b4;
14         Node* b5=new Node(); b5->v=5;b4->next=b5;
15         Node* b6=new Node(); b6->v=6;b5->next=b6;
16         Node* b7=new Node(); b7->v=7;b6->next=b7;
17         Node* b8=new Node(); b8->v=8;b7->next=b8; b8->next=b1;
18 
19         Node *current=b1, *temp;
20         int count;
21 
22         while(true) {
23                 /**
24                  * 每删除一个数字都需要重设count
25                  * */
26                 count=1;
27                 temp=current->next;
28                 /**
29                  * 这个循环用于寻找从current开始计数
30                  * 的第m个数
31                  * */
32                 while(true) {
33                         if(temp->v != -1) {
34                                 count++;
35                         }
36                         if(count == m) break;
37                         temp=temp->next;
38                 }
39                 /**
40                  * 此时temp索引的就是需要删除的第m
41                  * 个数
42                  * */
43                 temp->v=-1;
44                 /**
45                  * 这个循环用于寻找被删除元素的下一个元素
46                  * 注意需要是非-1值的节点
47                  * */
48                 temp=temp->next;
49                 while(temp->v == -1) {
50                         temp=temp->next;
51                 }
52                 /**
53                  * 此时temp索引的就是下一个可计数的元素,但是
54                  * 检查其是否就是current本身,是的话说明已经
55                  * 只有一个元素;否则更新current,进入下一次
56                  * 循环
57                  * */
58                 if(temp == current) break;
59                 else current=temp;
60         }
61         return current->v;
62 }
63 /**
64  * 数组的实现与链表类似,不过需要使用%取模操作符使得数组变成循环
65  * 结构
66  * */
67 int ArrayJose(int m) {}

 

出题:Fibonacci数列定义如下,要求用O(N)的时间找到第n个元素,O(logN)呢
            0          n=0
      f(n) = 1       n=1
             f(n-1)+f(n-2)   n>=2

分析:

  • 解法1:如果使用Top Down的分解方式,f(n)=f(n-1)+f(n-2), n>=2,这样下一层需要处理的项变成2n-3,再下一层变成4n-12,6n-17,……,树的总深度为n,时间复杂度为O(N3),此策略最大的缺点在于重复计算;
  • 解法2:如果使用Bottom Up的分解方式,所以BU可以利用n-1和n-2的结果计算n,时间复杂度为O(N),基本满足要求。因此使用递归实现并不一定适合递归形式定义的命题;
  • 解法3:利用矩阵性质:{f(n), f(n-1), f(n-1), f(n-2)}={1, 1, 1, 0}n-1,可以在O(logN)的时间复杂度下求得结果,具体思路请参见:
    http://zhedahht.blog.163.com/blog/static/25411174200722991933440/

解题:

 1 double Fibonacci(double n) {
 2         double first=0.0;
 3         double second=1.0;
 4         double current=0.0;
 5         
 6         for(int i=2;i<=n;i++) {
 7                 current=first+second;
 8                 
 9                 first=second;
10                 second=current;
11         }
12         
13         return current;
14 }