约瑟夫问题(利用双向循环链表)

7 篇文章 0 订阅
2 篇文章 0 订阅

问题:

M个人围成一圈,从第一个开始报数,第N个将被杀掉,最后剩下一个,其余人都将被杀掉。例如M=6,N=5,被杀掉的顺序是:5,4,6,2,3。

解决问题的思路

利用环形链表的闭合性,移动链表指针并删除元素,直至最后一个元素。

源代码

List接口
package com.zarek;

public interface List<E> {
	 static final int ELEMENT_NOT_FOUND = -1;
	
	/**
	 * 
	 * @return 元素个数
	 */
	int size();
	
	/**
	 * 清空元素
	 */
	void clear();
	
	/**
	 * 返回指定位置的元素
	 * @param index
	 * @return
	 */
	int indexOf(E element);
	
	/**
	 * 判断元素是否存在
	 * @param element
	 * @return 布尔值
	 */
	boolean contains(E element);
	/**
	 * 判断是否为空
	 * @return
	 */
	boolean isEmpty();
	
	/**
	 * 向指定位置添加元素并返回原来位置的元素
	 * @param index
	 * @param element
	 * @return 原来位置的元素
	 */
	void add(int index,E element);
	
	/**
	 * 向末尾添加元素并返回原来位置的元素
	 * @param element
	 * @return 原来位置的元素
	 */
	void add(E element);
	/**
	 * 删除指定位置的元素,并返回删除元素
	 * @param index
	 * @return 被删除的元素
	 */
	E remove(int index);
	/**
	 * 获取指定位置的元素并返回
	 * @param index
	 * @return 指定位置的元素
	 */
	E get(int index);
	/**
	 * 设置指定位置的元素,并返回原来此位置的元素
	 * @param index
	 * @param element
	 * @return 原来的元素
	 */
	E set(int index,E element);
}

抽象类AbstractList
package com.zarek;

public abstract class AbstractList<E> implements List<E> {

	protected int size;
	public E[] elements;
	
	/**
	 * 
	 * @return 元素个数
	 */
	public int size() {
		return size;
	}
	
	/**
	 * 是否为空
	 * @return 布尔值
	 */
	public boolean isEmpty() {
		return size == 0;
	}
	
	/**
	 * 判断元素是否存在
	 * @param element
	 * @return 布尔值
	 */
	public boolean contains(E element) {
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}
	
	/**
	 * 向动态数组末尾添加元素并返回原来位置的元素
	 * @param element
	 * @return 原来位置的元素
	 */
	public void add(E element) {
		add(size,element);
	}
	
	
	protected void OutOfBounds(int index) {
		throw new IndexOutOfBoundsException("index:"+index+",size:"+size);
	}
	
	/**
	 * 检查添加元素的index值是否越界
	 * @param index
	 */
	protected void rangeCheckForAdd(int index) {
		if(index < 0 || index > size) {
			OutOfBounds(index);
		}
	}
	
	protected void rangeCheck(int index) {
		if(index < 0 || index >= size) {
			OutOfBounds(index);
		}
	}
}

双向循环链表

对约瑟夫问题最重要的成员和方法
current 指向当前节点的指针
reset() 让current指向first
next() 让current往后走一步,也就是current = current.next
remove() 删除current当前指向的节点

package com.zarek.circle;

import com.zarek.AbstractList;

public class CircleLinkedList<E> extends AbstractList<E> {
	private Node<E> first;
	private Node<E> last;
	private Node<E> current;
	
	private static class Node<E>{
		E element;
		Node<E> prev;
		Node<E> next;
		public Node(Node<E> prev,E element,Node<E> next) {
			this.prev = prev;
			this.element = element;
			this.next = next;
		}
	}

	
	
	/**
	 * 让current指向first
	 */
	public void reset() {
		current = first;
	}
	
	/**
	 * 让current往后走一步,也就是current = current.next
	 * @return
	 */
	public E next() {
		if(current == null)return null;
		current = current.next;
		return current.element;
	}
	
	/**
	 * 删除当前节点,并将current = current.next
	 * @return
	 */
	public E remove() {
		if(current == null)return null;
		Node<E> next = current.next;
//		remove(indexOf(current.element));
		E element = remove(current);
		if(size == 0) {
			current = null;
		}else {
			current = next;
		}
		return element;
	}
	
	@Override
	public void clear() {
		first = null;
		last = null;
		size = 0;
	}

	@Override
	public int indexOf(E element) {
		Node<E> node = first;
		if(element == null) {
			for (int i = 0; i < size; i++) {
				if(node.element == null)return i;
				node = node.next;
			}
		}else {
			for (int i = 0; i < size; i++) {
				if(element.equals(node.element))return i;
				node = node.next;		
			}
		}
		return ELEMENT_NOT_FOUND;
	}


	@Override
	public void add(int index, E element) {
		rangeCheckForAdd(index);

		
		if(index == size) {
			Node<E> node = new Node<E>(last, element, first);
			if(last == null) {//这是链表添加的第一个元素
				first = node;
				last = node;
				node.prev = node;
				node.next = node;
			}else {
				last.next = node;
				last = node;
				first.prev = node;	
			}	
		}else {
			Node<E> next = node(index);
			Node<E> prev = next.prev;
			Node<E> node = new Node<E>(prev, element, next);
			prev = node;
			
			if(next == first) {//index == 0
				first = node;
			}
		}
		size++;
	}

	@Override
	public E remove(int index) {
		rangeCheck(index);
		return remove(node(index));
	}

	private E remove(Node<E> node) {
		if(size == 1) {
			first = null;
			last = null;
		}else {
			node.prev.next = node.next;
			node.next.prev = node.prev;
			
			if(node == first) {//如果删除的是第一个节点
				first = node.next;
			}
			if(node == last) {//如果删除的是最后一个节点
				last = node.prev;
			}
		}
		
		size--;
		return node.element;
	}
	
	@Override
	public E get(int index) {
		return node(index).element;
	}

	@Override
	public E set(int index, E element) {
		Node<E> node = node(index);
		E oldE = node.element;
		node.element = element;
		return oldE;
	}
	
	public Node<E> node(int index){
		rangeCheck(index);
		if(index < (size >> 1)) {
			Node<E> node = first;
			for (int i = 0; i < index; i++) {
				node = node.next;
			}
			return node;
		}else {
			Node<E> node = last;
			for (int i = size - 1; i > index; i--) {
				node = node.prev;
			}
			return node;
		}
	
	}
	
	@Override
	public String toString() {
		StringBuilder sBuilder = new StringBuilder();
		sBuilder.append("size:").append(size).append("[");
		Node<E> node = first;
		for (int i = 0; i < size; i++) {
			if(node.prev == null) {
				sBuilder.append("null_");
			}else {
				sBuilder.append(node.prev.element).append("_");
			}
			sBuilder.append(node.element);
			if(node.next == null) {
				sBuilder.append("_null");
			}else {
				sBuilder.append("_").append(node.next.element).append(",");
			}
			node = node.next;
		}
		sBuilder.append("]");
		
		return sBuilder.toString();
	}
	
}

测试代码
package com.zarek;

import com.zarek.circle.CircleLinkedList;

public class Main {

	public static void main(String[] args) {
		josephus(3);
	}
	
	static void josephus(int n) {
		CircleLinkedList<Integer> list = new CircleLinkedList<Integer>();
		for (int i = 1; i < 9; i++) {
			list.add(i);
		}
//		指向头节点
		list.reset();
//		n为要过第几个人删除
		int init = n;
		System.out.println("删除的顺序是:");
		while(!list.isEmpty()) {
			 
//			无论数几个人删除,都可以通过下面的代码执行
			while(init-- > 1) {
				list.next();
			}
//			list.next();
//			list.next();
			int element = list.remove();
			System.out.println(element);
			if(list.size == 0) {
				System.out.println("最后留下的是:"+element);
			}
			init = n;
		}
	}
}

总结

约瑟夫问题使用循环链表(单向循环链表和双向循环链表)比较容易解决

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值