单向循环链表解决约瑟夫问题

单向循环链表解决约瑟夫问题

什么是单向循环链表

单向循环链表基本与单向链表相同,唯一的区别就是单向循环链表的尾节点指向的不是null,而是头节点(注意:不是头指针).
因此,单向循环链表的任何节点的下一部分都不存在NULL值。

img

由于单向循环链表的特性,它在处理一些环状数据的时候十分有效.大名鼎鼎的约瑟夫环问题就可以用循环单向链表求解,下面我们会有进一步的介绍。

由于单向循环链表和单向链表的差别真的不大,增添改查原理都相同。因此在这里我们不详细讲解,只提供源码。(如果你还是不理解的话,这里有单向链表的传送门)

什么是约瑟夫环问题

大家知道什么是约瑟夫环吗?

各位读者是不是心里想着:“知道谁还看你在这废话。”然后悄悄地攥紧了拳头?别,各位大哥大姐千万别激动,给小弟点时间,让小弟为你排忧解难。

约瑟夫问题为:设编号为1,2,……n得n个人围坐一圈,约定编号为k(k大于等于1并且小于等于n)的人从1开始报数,数到m的那个人出列。它的下一位继续从1开始报数,数到m的人出列,依次类推,直到所有人都出列为止。

这就是著名的“约瑟夫环”问题,是不是还挺有趣的感觉(有趣个鬼不为了面试、加薪谁搞这东西)。

代码演示

结点类

package com.ma.linked;
//结点类
public class Boy {
    //结点编号
    private int no;
    //指向下一个结点
    private Boy next;

    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}

package com.ma.linked;

public class CircleSingleLinkedTest {
    private Boy first = new Boy(-1);

    //构建环形链表
    public void addBoy(int num){
        if (num < 1){
            System.out.println("数据不正常");
            return;
        }
        Boy temp = first;
        for (int i = 1; i <= num; i++) {

            Boy boy = new Boy(i);
            //判断你是否是第一个小孩
            if (i == 1){
                first = boy;
                first.setNext(first);
                temp = first;
            }else {
                temp.setNext(boy);
                boy.setNext(first);
                temp = boy;
            }
        }
    }


    //查看链表中的结点
    public void showBoy(){
        if (first == null){
            System.out.println("该链表为空");
            return;
        }

        Boy boy = first;

        while (true){
            System.out.println("孩子的编号为"+boy.getNo());
            if (boy.getNext() == first){
                break;
            }
            boy = boy.getNext();
        }
    }


    //当调用该方法,输入第几个小孩子开始数数,输几次,环形链表中总共几个孩子
    public void countBoy(int startNo,int countNum,int nums){
        if (first==null||startNo<1||startNo>nums){
            System.out.println("参数输入错误");
            return;
        }
        //定义辅助指针,指向环形链表中最后一个结点
        Boy helper = first;
        while (true){
            if (helper.getNext() == first){
                break;
            }
            helper = helper.getNext();
        }

        //寻找初始位置,把first定义为起始位置
        for (int i = 0; i < startNo-1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }

        //当小孩报数时,数到m的孩子出列,让first和helper移动m-1次即可,找到了已出列的孩子
        while (true){
            if (first == helper){break;}

            for (int i = 0; i < countNum-1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }

            System.out.println("编号"+first.getNo()+"的孩子出列");
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.println("编号为"+first.getNo()+"的孩子最后出列");

    }
}

测试代码

package com.ma.linked;

public class LinkedTest3 {
    public static void main(String[] args) {
        CircleSingleLinkedTest circleSingleLinkedTest = new CircleSingleLinkedTest();
        circleSingleLinkedTest.addBoy(5);
        circleSingleLinkedTest.showBoy();
        circleSingleLinkedTest.countBoy(1,2,5);
    }
}

kedTest();
circleSingleLinkedTest.addBoy(5);
circleSingleLinkedTest.showBoy();
circleSingleLinkedTest.countBoy(1,2,5);
}
}


  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是单向循环链表约瑟夫问题的代码实现: ``` #include <iostream> using namespace std; // 定义单向循环链表节点 struct Node { int data; // 数据域 Node* next; // 指针域 Node(int val = 0) : data(val), next(nullptr) {} // 构造函数 }; // 定义单向循环链表类 class CirLinkedList { public: CirLinkedList(); // 构造函数 ~CirLinkedList(); // 析构函数 void CreateList(int n); // 创建长度为n的单向循环链表 void Josephus(int m); // 约瑟夫问题,每m个节点删掉一个 private: Node* first; // 链表头指针 }; CirLinkedList::CirLinkedList() { first = nullptr; // 初始化链表为空 } CirLinkedList::~CirLinkedList() { // 释放链表内存 Node* temp = first; while (temp) { Node* p = temp; temp = temp -> next; delete p; } } void CirLinkedList::CreateList(int n) { if (n < 1) return; first = new Node(1); // 创建第一个节点 Node* temp = first; // 创建剩余的n-1个节点 for (int i = 2; i <= n; ++i) { Node* p = new Node(i); temp -> next = p; temp = temp -> next; } temp -> next = first; // 最后一个节点指向头结点,形成单向循环链表 } void CirLinkedList::Josephus(int m) { // 如果链表为空,直接退出 if (first == nullptr) return; Node* pre = nullptr; // pre指向待删除节点的前一个节点 Node* cur = first; // cur指向待删除节点 // 找到待删除节点,确保链表中至少有两个节点 while (cur -> next != cur) { // 移动m-1步,找到待删除节点 for (int i = 0; i < m - 1; ++i) { pre = cur; cur = cur -> next; } pre -> next = cur -> next; // 将待删除节点从链表中删除 cout << cur -> data << " "; // 输出删除的节点 delete cur; // 释放删除的节点内存 cur = pre -> next; // 更新待删除节点为下一个节点 } // 输出链表剩余节点 cout << cur -> data << endl; } int main() { int n, m; cin >> n >> m; // 输入链表长度n和每次删掉的节点数m CirLinkedList cll; cll.CreateList(n); // 创建单向循环链表 cll.Josephus(m); // 解决约瑟夫问题 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值