单向循环链表并不复杂,主要要和约瑟夫问题结合起来,才能更好地感受到单向循环链表的好处。
约瑟夫问题
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
java实现单向循环列表(不带头结点)
package com.study;
public class TestCircleLinkList {
public static void main(String[] args) {
CircleLinkList circleLinkList = new CircleLinkList();
circleLinkList.create(4);
circleLinkList.show();
System.out.println("===========");
circleLinkList.deleteOrder(2,2);
}
}
//结点
class Node {
int data;
Node next = null;//指向下一结点
public Node(int data){
this.data = data;
}
}
//链表
class CircleLinkList{
Node first = null;//指向首结点
Node curNode = null;//指向尾结点
/*
* 创建指定结点个数的循环链表
* */
public void create(int n){
if(n<1){
System.out.println("请输入大于0的整数");
return;
}
for(int i=1;i<=n;i++){
Node newNode = new Node(i);
if(i==1){
first = newNode;
curNode = newNode;
newNode.next = first;
}else{
curNode.next = newNode;
curNode = newNode;
curNode.next = first;
}
}
}
/*
* @param start:从第几个结点开始
* @param m:计算几次
* */
public void deleteOrder(int start ,int m){
if(first==null || start < 1 || m < 0){
System.out.println("输入有误");
return;
}
for(int i=1;i<start;i++){//将first和curNode指针指向对应位置
if(start==1){
break;
}
first = first.next;
curNode = curNode.next;
}
Node temp = curNode;
int count = 1;
while(temp!=first){
if(count == m){
System.out.println(first.data);
first = first.next;
temp.next = first;
count = 1;
}else{
first = first.next;
temp = temp.next;
count++;
}
}
System.out.println(first.data);
}
/*
* 遍历循环链表
* */
public void show(){
if(first == null){
System.out.println("当前链表为空");
return;
}
Node temp = first;
while(temp.next!=first){
System.out.println(temp.data);
temp = temp.next;
}
System.out.println(temp.data);
}
}
对于单项循环链表,我们要注意一下几点
1.要有两个指针辅助,first和curNode指针。前者指向第一个结点,后者指向最后一个结点。循环链表虽然首尾相连,但是我们人为给它设定了第一个结点和最后一个结点。
2.对于约瑟夫问题,我们需要再借助一个temp指针,开始时指向最后一个结点(curNode指向的结点);当开始报数报到M的时候,让first和temp同时往前移动m-1次(因为first指向的的人也会报一次数);然后让first = first.next, temp.next=first,这样就达到了删除的目的。然后循环往复,直至结束。