好玩的数据结构与算法——约瑟夫问题(单向循环链表)

Josephu 问题:

设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到m 的那个人出列,它的下一位又从 1 开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由 此产生一个出队编号的序列。

这个题目可以用数组做,也可以使用单向环形链表做。这里我使用单向循环链表做。

单向循环链表

如图:不带头节点
好玩的数据结构与算法——约瑟夫问题(单向循环链表)_第1张图片
单向循环链表的添加
思路:

  1. 循环到链表尾部
  2. 将新节点的next指向first
  3. 将链表最后一个节点的next指向新的节点
    好玩的数据结构与算法——约瑟夫问题(单向循环链表)_第2张图片
/**
 * @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);
    }
}

约瑟夫问题解决

思路:

  1. 构建一个单向环形链表,first指向第一个节点
  2. 循环找到要出链表的节点,并将该节点出链表
    (1).因为要将查找到的节点出链表,故需要一个辅助变量位于first之前的节点,执行first
    (2)找到要删除的节点之后:

    first = first.next
    temp.next = first

  3. 循环结束的条件是: 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);


    }
}

你可能感兴趣的:(数据结构与算法,链表,数据结构,算法,java)