JAVA数据结构-04 单向环形链表解决约瑟夫问题
问题描述:num个人围成一圈,从第start个开始报数,第k个出圈,最后剩下一个,求出圈的顺序。
构建一个单向环形链表
思路:
- 先创建第一个节点,让这个节点的next指针域指向自己,并形成环形
- 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表即可
- 遍历时判断遍历一圈结束的条件.temp.next == first
数据节点:
class Child{
public int no;
public Child next;
public Child(int no) {
this.no = no;
}
@Override
public String toString() {
return "Child{" +
"no=" + no +
'}';
}
}
构建单向环形链表结构:
class CircleSingleLinkedList{
//创建第一个节点
Child first;
//添加指定个数的节点
public void addChile(int num){
if(num <1 ){
System.out.println("num 错误");
return ;
}
//创建一个单节点环形链表
first = new Child(1);
first.next = first;
//添加指定个数的元素
Child temp = first;
for (int i = 2; i <= num ; i++) {
Child child = new Child(i);
temp.next = child;
child.next = first;
temp = temp.next;
}
}
//遍历一圈
public void showList(){
if(first == null){
System.out.println("链表为空");
return ;
}
Child temp = first;
while (true){
System.out.println(temp);
if(temp.next == first) break;
temp = temp.next;
}
}
}
根据k值,完成出圈的顺序
思路:
- 因为单向链表删除节点(出圈)时,需要将指针指向待删除节点的前一个位置.
- 因此需要创建一个辅助指针temp,指向报数小孩的前一个位置.所以初始化时,temp指向first指针的前一个位置(环形队列的最后一个元素).
- 再将temp移动start-1个位置,移动到开始报数的元素的前一个位置
- 当小孩报数时,让temp指针向前移动k-1个单位,到达预删除节点的前一个位置,这时让节点出圈
- temp.next=temp.next.next;
- 当链表中只有一个节点,此时temp=temp.next, 此时循环结束,将队列中最后一个节点出圈.
/**
*
* @param start 从第几个位置开始
* @param k 数几下
* @param num 有多少小孩
*/
public void excuteJosephu(int start,int k,int num){
//添加小孩
addChile(num);
//
System.out.println("出圈前队列:");
showList();
//初始化:让temp指向 start的前一个位置(队列末尾)
Child temp = first;
while (temp.next != first){
temp = temp.next;
}
for (int i = 0; i < start-1 ; i++) {
temp = temp.next;
}
//
System.out.println("开始出圈:");
while(true){
if(temp.next == temp) break; //当链表中只有一个值时结束寻新欢
for (int i = 0; i <k-1 ; i++) {
temp = temp.next;
}
//将temp节点的下一个节点出圈
Child out = temp.next;
temp.next = temp.next.next;
System.out.println("出圈节点"+out);
}
//将循环链表中最后一个元素出圈
System.out.println("最后出圈节点"+temp);
}