您的位置:首页 > 理论基础 > 数据结构算法

【数据结构和算法分析】循环链表及约瑟夫问题

2014-07-26 10:53 357 查看

循环链表及约瑟夫问题

循环链表:

                循环链表可以为单链表,也可以为双链表,但我不想把问题搞得那么复杂,姑且就做单链表的循环形式吧。

                我们在实现了链表后,必然会提出一个问题:链表能不能首尾相连?怎样实现?

                答案:能。其实实现的方法很简单,就是将表中最后一个结点的指针域指向头结点即可(P->next = head;)。这种形成环路的链表称为循环链表。

代码如下:

public class MyCircleList {

Node headNode;
Node tailNode;
int length;

public MyCircleList(Node node){
this.headNode = node;
this.tailNode = node;
length = 1;
}
/*
* 默认在尾结点插入
*/
public void insert(Node node){
insert(node, length);
}
/*
* @param Node node 插入的结点 , int index 结点插入的位置
*/
public void insert(Node node,int index){
if (index < 0 || index > length) {
throw new IndexOutOfBoundsException();
}
if (index == 0) {
this.tailNode.next = node;
node.next = this.headNode;
headNode = node;
}else if (index == length) {
tailNode.next = node;
tailNode = node;
node.next = this.headNode;
}else {
Node otherNode = this.headNode;
while (index > 1) {
otherNode = otherNode.next;
index--;
}
node.next = otherNode.next;
otherNode.next = node;
}
length++;
}
/*
* @param int index 删除结点的位置
*/
public void delete(int index){
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException();
}
if (index == 0 && length == 1) {
tailNode = headNode = null;
}else if (index == 0 && length != 1) {
tailNode.next = headNode.next;
headNode = headNode.next;
}else {
Node otherNode = this.headNode;
while (index > 1) {
otherNode = otherNode.next;
index--;
}
otherNode.next = otherNode.next.next;
}
length--;
}

/*
* @param int index 查看  index 处的 data
*/
public Object getData(int index){
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException();
}
Node otherNode = headNode;
for (int i = 0; i < index; i++) {
otherNode = otherNode.next;
}
return otherNode.data;
}
/*
* @param int index 修改的 data 的位置 ,Object data 所需修改的 data
*/
public void setData(int index,Object data){
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException();
}
Node otherNode = headNode;
for (int i = 0; i < index; i++) {
otherNode = otherNode.next;
}
otherNode.data = data;
}
public void print(){
Node otherNode = this.headNode;
for (int i = 0; i < length; i++) {
System.out.print(otherNode.data+" ");
otherNode = otherNode.next;
}
System.out.println();
}

}

class Node{

/*
* 节点类,包括结点的值 data 和指向下一结点的指针 next,单链表适用
*/

Object data;
Node next;

public Node(){
data = null;
next = null;
}

public Node(Object data){
this.data = data;
this.next = null;
}

public Node(Object data,Node nextnode){
this.data = data;
this.next = nextnode;
}

/*
* 判断有无下一结点
* @return boolean 若true,则该结点有下一结点
*/
public boolean hasnext(){
return this.next != null;
}

}

约瑟夫问题:

约瑟夫问题几乎是最经典的用来讲解循环链表的案例了。为什么呢?我们来看看这个问题的描述就会明白了:

有一队由n个冒险家组成的探险队深入到热带雨林中,但他们遭遇到了食人族,食人族的游戏规则是让他们围成一圈,然后选定一个数字m,从第1个人开始报数,报到m时,这个人就要被吃掉了,然后从下一个人开始又重新从1报数,重复这个过程,直到剩下最后一个人,这个人是幸运者,可以离开而不被吃掉。那么问题是,谁是这个幸运者呢?

我们来举个例子:

假设这个探险队有6个探险家,食人族选定的数字m是5,那么在第一轮中,5号会被吃掉,剩下的就是:1, 2, 3, 4, 6总共5个人,然后从6号开始,重新从1开始报5个数:6, 1, 2, 3, 4,所以在第二轮里面被吃掉的就是4号……一直重复这个过程,按顺序应该是:5, 4, 6, 2, 3被吃掉,剩下1号活下来。

解决这个问题并不是只能用循环链表的,但使用循环链表应该是最方便的。我写的代码如下:

public class Josephus {

public static final int N = 6; //代表 6 位人数
public static final int M = 5; //选定的数字为 5

public static void main(String[] args){

//初始化循环链表,构造出来为 1,2,3,4,5,6的链表分别代表六个人
MyCircleList myCircleList = new MyCircleList(new Node(1));
for (int i = 2; i <= N; i++) {
myCircleList.insert(new Node(i));
}

Node pre = null;
Node p = myCircleList.headNode;
for (int i = 0; i < N-1; i++) { //需要遍历的轮次,执行 N-1 次
for (int j = 1; j < M; j++) { //每次遍历的报数,数 M-1 人
pre = p;
p = p.next;
}
System.out.println("出局的是:" + p.data); //输出
pre.next = p.next; //删除一个结点
p = pre.next;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: