006-环形单链表的约瑟夫问题

package com.my.util;
/**
 * 单向链表节点
 * */
public class SingleNode {
	public int value;
	public SingleNode next;
	public SingleNode(int data){
		this.value = data;
	}
}

package com.my.suanfa;

import com.my.util.SingleNode;

/**
 * 环形单链表的约瑟夫问题
 * 方法一:每删除一个节点就要遍历m次,共有n个节点,时间复杂度为O(n * m)
 * 方法二:时间复杂度为O(N)
 * */
public class Solution03 {
	/**
	 * 方法一:每删除一个节点就要遍历m次,共有n个节点,时间复杂度为O(n * m)
	 * */
	public SingleNode josePhusKill1(SingleNode head, int m) {
		//边界条件判断
		if(head == null || head.next == null || m < 1) {
			return head;
		}
		//定义一个节点指向链表的尾节点,因为是环形链表,删除链表操作时刻在进行,所以链表的头结点和尾节点也是时刻变化的
		SingleNode last = head;
		//遍历链表找到初始尾节点的位置
		while(last.next != head) {
			last = last.next;
		}
		int count = 0;
		//只要head != last说明环形链表中的节点数不止一个,就要继续删除操作
		while(head != last) {
			//因为环形链表是时刻变化的,所以每判断一个节点都要重新找到新的head和last
			//只要环形链表中的节点数超过一个,那么head和last之间就永远差一个节点
			//正在遍历的节点是要删除的节点
			if(++count == m) {
				//删除节点
				last.next = head.next;
				//将count重新归零
				count = 0;
			}else {
				//正在遍历的节点不是要删除的节点,说明当前last不是真正的last,则将last指向其后继节点
				last = last.next;
			}
			//完成一个节点的判断之后,因为last与head之间只差一个节点,因此新的head就是last.next
			//head始终记录的是下一个要判断的节点
			head = last.next;
		}
		//删除结束后head == last,说明此事链表中只剩下一个节点,此节点      就是最后剩下的节点
		return last;
	}
	
	/**
	 * 方法二:时间复杂度为O(N)
	 * 假设初始节点个数为i的环最后幸存者的编号为Num(i),
	 * 初始节点个数为i - 1的环最后幸存者的编号为Num(i - 1)
	 * 1:还没有删除节点时报号A,与编号B之间的关系为:B = (A - 1) % i + 1,即报A的是编号B的节点
	 * 2:删除节点s前的编号old,与删除节点后的编号new各节点的编号之家呢关系为:old = (new + s - 1) % i + 1
	 * 报数为 m的节点将会被删除,代入两式化简后得到Num(i - 1)-->new,与Num(i)-->old的关系式只与m,i有关,为:old = (new + m - 1) % i + 1
	 * */	
	public SingleNode josephusKill2(SingleNode head, int m) {
		//边界条件判断
		if(head == null || head.next == head || m < 1) {
			return head;
		}
		//定义临时节点,指向头结点,用来计算初始链表中节点的个数
		SingleNode cur = head.next;
		//计数器用来记录环形链表中的节点的个数
		int tmp = 1;
		//遍历环形链表记录计算初始节点的个数
		while(cur != head) {
			tmp++;
			cur = cur.next;	
		}
		//tmp记录最后存活的节点的编号
		tmp = getLive(tmp, m);
		while(--tmp != 0) {
			//循环结束时head正好指向最后活下来的节点
			head = head.next;
		}
		//重新将活下来的节点的后一个节点设为头结点
		head.next = head;
		return head;
	}
	
	/**
		 * 计算最后活下来的节点报的号
		 * 参数含义:
		 * i:此时环形链表中的节点的个数
		 * m:要删除的节点的编号
		 * 返回值为当前幸存者的编号
		 * */
	public int getLive(int i, int m) {
		//递归结束条件
		if(i == 1) {
			return i;
		}
		return (getLive(i - 1, m) + m - 1) % i + 1;
	}	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值