使用环形链表解决约瑟夫(丢手帕)问题
约瑟夫问题:
设编号为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 }