使用环形链表解决约瑟夫(丢手帕)问题

约瑟夫问题:

  设编号为1,2,3...n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依此类推,直到所有人出列为止,由此产生一个出队编号的序列,并求出最后出列的人是哪个?

1.先死后活:

将问题简化成:有4个人围坐一圈,从编号1的人开始报数,数到2的人出列,依次类推。

2.创建节点类:

 1 /**
 2  * 节点类
 3  * @author SCOTT
 4  *
 5  */
 6 public class Child {
 7     /*编号*/
 8     int no;
 9     /*下一个节点*/
10     Child next;
11     
12     public Child(int no){
13         this.no = no;
14     }
15 }

3.创建环形链表,并返回第一个节点

 1 /**
 2      * 创建一个环形链表
 3      * @param head 指定第一个节点的头指针
 4      * @param num 环形链表个数
 5      * @return 返回指定第一个节点的头指针
 6      */
 7     private static Child createCircleLink(Child head,int num){
 8         //要报障head不改变,让cur游走各个节点之间
 9         Child cur = null;
10         for(int i=0;i<num;i++){
11             Child child = new Child(i+1);
12             if(i==0){
13                 head = child;
14                 child.next = child;
15             }else{
16                 cur.next = child;
17                 child.next = head;
18             }
19             cur = child;
20         }
21         return head;
22     }

4.测试环形链表是否创建成功

 1 /**
 2      * 遍历环形链表节点
 3      * @param head
 4      */
 5     private static void ergodicCircleLink(Child head){
 6         if(head == null)
 7             return;
 8     
 9         Child cur = head;
10         while(cur.next != head){
11             System.out.println("当前节点:"+cur.hashCode()+"\t下一个节点:"+cur.next.hashCode());
12             cur = cur.next;
13         }
14         //当前cur指向最后一个节点
15         System.out.println("当前节点:"+cur.hashCode()+"\t下一个节点:"+cur.next.hashCode());
16     }

5.环形链表创建成功后,就可以玩游戏了

 1 /**
 2      * 从第一个人开始数,数2下
 3      * @param head
 4      * @param k 从k开始数
 5      * @param m 数m下
 6      * @param n 链表个数
 7      */
 8     private static void play(Child head, int k, int m, int n){
 9         if(head == null)
10             return;
11         
12         if(!(k>=1 && k<= n)){
13             System.out.println("指定人数不在范围内,"+k+">"+n);
14             return;
15         }
16         
17         //指向当前节点
18         Child cur = head;
19         //需要一个与cur相邻的并位于cur后一位的指针,初始位置让他指向最后一个节点,便于删除节点
20         Child tail = null;
21         while(cur.next != head){
22             cur = cur.next;
23         }
24         tail = cur;
25         cur = head;
26         
27         //找到第k个位置
28         for(int i=0;i<k-1;i++){
29             cur = cur.next;
30             tail = tail.next;
31         }
32         
33         while(cur != tail){
34             //开始数数:移动一次,代表数了2下
35             for(int i=0;i<m-1;i++){
36                 cur = cur.next;
37                 tail = tail.next;
38             }
39             System.out.println("第"+cur.no+"号小朋友出列!");
40             //删除cur指向的节点
41             //让cur再往下移一个节点
42             cur = cur.next;
43             tail.next = cur;
44         }
45         System.out.println("获胜的小朋友:"+tail.no);
46     }

6.测试:

 1 public static void main(String[] args) {
 2         int n = 4;
 3         int m = 2;
 4         int k = 1;
 5         //指向环形链表的第一个节点,便于链表的操作
 6         Child head = null;
 7         head = createCircleLink(head, n);
 8         //ergodicCircleLink(head);
 9         play(head, k, m, n);
10 }

posted @ 2014-06-23 12:12  楪夕  阅读(714)  评论(0编辑  收藏  举报