该算法为求解环形单链表的约瑟夫问题,有两种方法可以求出剩下的结点。直接上代码,注释里有具体的思路。
package List; /**环形链表 * Created by HH on 2018/3/5. */ public class CircleList { //方法一:时间复杂度O(N*M) public Node findServiorNode(Node head,int m){ int n = 0; //边界判断:若结点为空或只有一个结点或报数小于1,则直接返回头结点 if(head == null || head.next == head || m < 1){ return head; } //定义一个辅助结点,为了在删除结点时做一些辅助功能 Node pre = head.next; //令pre在head结点的前方,即head == pre.next while(pre != head){ pre = pre.next; } //删除结点方法,当剩下多个结点时 while(pre != head){ //每次循环先将n++ n++; //如果n与m相同,则删除当前结点,当前结点用head表示 if(n == m){ //删除head结点,并重新组成链表,此时head重新指向pre.next pre.next = head.next; head.next = null; head = pre.next; //此时重置n n = 0; //否则,head和pre都向下移动一位 }else{ pre = head; head = head.next; } } //此时剩下的结点就是唯一的那个结点了 return head; } //方法二:时间复杂度O(N) public Node findTheOnlyOne(Node head, int m){ //边界判断:若结点为空或只有一个结点或报数小于1,则直接返回头结点 if(head == null || head.next == head || m < 1){ return head; } //辅助结点node,可以有多个用处 Node node = head; //辅助int,占32位,4个字节,4byte,可用来表示链表的个数与留下结点的编号 int len = 1; //循环得到链表的个数 while(node != head){ node = node.next; len++; } //得到留下的结点的编号 len = getLiveNode(len, m); //找到留下的结点 while(--len != 0){ node = node.next; } //令其自身形成循环链表,返回得到所需要的值 node.next = node; return node; } /* *得到编号的算法,该算法是通过归纳得出 *思想是将每个结点都标上标号,然后根据m,每删除一个结点则环形链表的结点生成一个新的编号 * 表达式为编号(i) = (编号(i-1)+ m - 1)% i + 1 * 一层层递归下去,直到i = 1 * 便可得到最后剩余的结点的编号 */ private int getLiveNode(int len, int m){ //如果len == 1,则返回编号1 if(len == 1){ return 1; //否则进入递归函数 }else{ return (getLiveNode(len-1, m) + m - 1)%len + 1; } } }