Josephu 问题:
设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到m 的那个人出列,它的下一位又从 1 开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由 此产生一个出队编号的序列。
这个题目可以用数组做,也可以使用单向环形链表做。这里我使用单向循环链表做。
单向循环链表
如图:不带头节点
单向循环链表的添加
思路:
- 循环到链表尾部
- 将新节点的next指向first
- 将链表最后一个节点的next指向新的节点
/**
* @Description: addNode 添加节点
*/
public void addNode(CircleNode circleNode){
if (first == null){//第一次添加节点,没有一个数据
first = new CircleNode();
first.setNo(circleNode.getNo());
first.setNext(first);
}else {
CircleNode cur = first;
// 表示是否添加重复节点
boolean flag = false;
while (true){
if (cur.getNo() == circleNode.getNo()){ //节点重复
flag = true;
break;
}
if (cur.getNext() == first){ //到了尾部
break;
}
cur = cur.getNext();
}
if (flag){
System.out.printf("节点no=%d重复,不能添加",circleNode.getNo());
return;
}
cur.setNext(circleNode);
circleNode.setNext(first);
}
}
约瑟夫问题解决
思路:
- 构建一个单向环形链表,first指向第一个节点
- 循环找到要出链表的节点,并将该节点出链表
(1).因为要将查找到的节点出链表,故需要一个辅助变量位于first之前的节点,执行first
(2)找到要删除的节点之后:first = first.next
temp.next = first - 循环结束的条件是: first == null
/**
* @Description: outNode 出节点
* @param: [start 从哪一个节点开始, num:第多少个节点结束]
*/
public void outNode(int start, int num){
if (first == null){
System.out.println("链表为空~~~");
return;
}
CircleNode temp = first;
while (temp.getNext() != first){ // 将temp指向first节点的前一个节点
temp = temp.getNext();
}
for (int i = 0; i < start - 1; i++) { // 将first移向开始的节点
first = first.getNext();
temp = temp.getNext();
}
while (first != null){ // 全部节点出去的条件
for (int i = 0; i < num - 1; i++) { // first移动要要出去的节点位置
first = first.getNext();
temp = temp.getNext();
}
System.out.println("出节点"+first);
first = first.getNext();
temp.setNext(first); //将temp指向first的下一个节点,这样原来位置的first节点就出了
if (first.getNext() == first){ //只有最后一个节点
System.out.println("出节点"+first);
first = null;
}
}
}
附完整代码:
节点:
public class CircleNode {
private int no;
private CircleNode next;
public CircleNode(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public CircleNode getNext() {
return next;
}
public void setNext(CircleNode next) {
this.next = next;
}
@Override
public String toString() {
return "CircleNode{" +
"no=" + no +
'}';
}
public CircleNode() {
}
}
单向循环链表的操作
public class CircleSignalLinkList {
private CircleNode first = null;
/**
* @Description: addNode 添加节点
*/
public void addNode(CircleNode circleNode){
if (first == null){//第一次添加节点,没有一个数据
first = new CircleNode();
first.setNo(circleNode.getNo());
first.setNext(first);
}else {
CircleNode cur = first;
// 表示是否添加重复节点
boolean flag = false;
while (true){
if (cur.getNo() == circleNode.getNo()){ //节点重复
flag = true;
break;
}
if (cur.getNext() == first){ //到了尾部
break;
}
cur = cur.getNext();
}
if (flag){
System.out.printf("节点no=%d重复,不能添加",circleNode.getNo());
return;
}
cur.setNext(circleNode);
circleNode.setNext(first);
}
}
/**
* @Description: show 显示节点
*/
public void show(){
CircleNode cur = first;
while (true){
System.out.println(cur);
if (cur.getNext() == first){
break;
}
cur = cur.getNext();
}
}
/**
* @Description: outNode 出节点
* @param: [start 从哪一个节点开始, num:第多少个节点结束]
*/
public void outNode(int start, int num){
if (first == null){
System.out.println("链表为空~~~");
return;
}
CircleNode temp = first;
while (temp.getNext() != first){ // 将temp指向first节点的前一个节点
temp = temp.getNext();
}
for (int i = 0; i < start - 1; i++) { // 将first移向开始的节点
first = first.getNext();
temp = temp.getNext();
}
while (first != null){ // 全部节点出去的条件
for (int i = 0; i < num - 1; i++) { // first移动要要出去的节点位置
first = first.getNext();
temp = temp.getNext();
}
System.out.println("出节点"+first);
first = first.getNext();
temp.setNext(first); //将temp指向first的下一个节点,这样原来位置的first节点就出了
if (first.getNext() == first){ //只有最后一个节点
System.out.println("出节点"+first);
first = null;
}
}
}
}
测试:
public class TestCircleSignalLinkList {
public static void main(String[] args) {
CircleSignalLinkList circleSignalLinkList = new CircleSignalLinkList();
CircleNode circleNode = null;
for (int i = 1; i <= 5; i++) {
circleNode = new CircleNode(i);
circleSignalLinkList.addNode(circleNode);
}
circleSignalLinkList.show();
System.out.println("出节点");
circleSignalLinkList.outNode(1,2);
}
}