链表java实现(二)单向循环链表基本使用及使用单向循环链表解决Josephu问题

一、基本介绍

  顾名思义,在内存中单向循环链表就是将单链表的最后一个节点的next再指向单链表的第一个节点,在逻辑结构上就是将原来的一条链变成首尾相接的一个圆环,从而实现单链表的一个循环。如下图(此处为无头节点的单向链表):
单向循环链表内存结构
单向循环链表逻辑结构图

二、单向循环链表的使用

1、定义单向循环链表基本结构

class Node1 {
    //这里的变量用private修饰,所以下面需要定义get、set方法
    //和单链表相同,此处可以声明多个data
    private int data;
    private Node1 next;
	//空参构造器
    public Node1() {
    }
	//带参构造器
    public Node1(int data) {
        this.data = data;
    }
    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public Node1 getNext() {
        return next;
    }

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

2、同样我们再定义一个类来添加增删改查基本操作(与普通单链表相似,但要让尾节点指向首节点)

class CircleSingleLinkedList1 {
    //创建一个first节点
    private Node1 first;

    //添加节点,这里默认data唯一,不可重复添加
    public void addNode(Node1 node) {
        if (first == null) {
            first = node;
            first.setNext(first);
            return;
        }
        Node1 temp = first;
        boolean flag = false;
        while (true) {
            if (temp.getNext() == first) {
                break;
            }
            if (node.getData() == temp.getData()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            System.out.printf("节点%d已经存在,不可重复添加\n",node.getData());
        } else {
            temp.setNext(node);
            node.setNext(first);
        }
    }

    //按data的大小排序添加,这里默认data唯一,不可重复添加
    public void addNodeByOrder(Node1 node) {
        if (first == null) {
            first = node;
            first.setNext(first);
            return;
        }
        Node1 temp = first;
        boolean flag = false;
        while (true) {
            if (temp.getNext() == first) {
                break;
            }
            if (temp.getNext().getData() > node.getData()) {
                break;
            } else if (temp.getNext().getData() == node.getData()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            System.out.printf("节点%d已经存在,不可重复添加\n",node.getData());
        } else {
            node.setNext(temp.getNext());
            temp.setNext(node);
        }
    }

    //删除节点
    public void deleteN(int no) {
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        Node1 temp = first;
        boolean flag = false;

        if (no == first.getData()) {
            while (true) {
                if (temp.getNext() == first) {
                    break;
                }
                temp = temp.getNext();
            }
            first = first.getNext();
            temp.setNext(first);
        }

        while (true) {
            if (temp.getNext() == first) {
                break;
            }
            if (no == temp.getNext().getData()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            temp.setNext(temp.getNext().getNext());
        } else {
            System.out.println("未找到该节点");
        }
    }

    //修改链表
    public void update(Node1 node) {
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        Node1 temp = first;
        boolean flag = false;
        while (true) {
            if (temp.getNext() == first) {
                break;
            }
            if (node.getData() == temp.getData()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            temp.setData1(node.getData1());
        } else {
            System.out.println("未找到该节点");
        }

    }

    //遍历
    public void showNode() {
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        Node1 curNode = first;
        while (true) {
            System.out.printf("节点%d的data是%d\n", curNode.getData(),curNode.getData1());
            if (curNode.getNext() == first) {
                break;
            }
            curNode = curNode.getNext();
        }
    }
}

3、写一个Demo来测试一下

/**
 * @author dankejun
 * @create 2020-03-31 16:18
 */
public class CircleSingleLinkedListExer {
    public static void main(String[] args) {
        CircleSingleLinkedList1 linkedList1 = new CircleSingleLinkedList1();
        Node1 node1 = new Node1(1, 10);
        Node1 node2 = new Node1(2, 20);
        Node1 node3 = new Node1(3, 30);
        Node1 node4 = new Node1(4, 40);
//自定义顺序添加节点
//        linkedList1.addNode(node1);
//        linkedList1.addNode(node2);
//        linkedList1.addNode(node3);
//        linkedList1.addNode(node4);
		//按data大小添加节点
        linkedList1.addNodeByOrder(node1);
        linkedList1.addNodeByOrder(node3);
        linkedList1.addNodeByOrder(node2);
        linkedList1.addNodeByOrder(node4);
        System.out.println("原来的链表为:");
        linkedList1.showNode();
        System.out.println();


        Node1 node5 = new Node1(3, 50);
        linkedList1.update(node5);
        System.out.println("修改后的链表为:");
        linkedList1.showNode();
        System.out.println();
        
        linkedList1.deleteN(2);
        System.out.println("删除后的链表为:");
        linkedList1.showNode();
    }

}

测试结果:
循环链表增删改查测试结果

三、Josephu(约瑟夫)问题

	有n个人,编号为1~n,从第k个人开始报数,从1开始报,报到m的人会出列,然后从第m+1个人开始,重复以上过程。在出列了n-1个人后,
问出列的序列和最后一个人的编号是?

1、问题分析

  我们可以用一个不带头节点的循环链表来处理Josephu问题。先创建一个有n个节点的循环单链表,然后从k节点起从1开始计数,计到m,就把当前节点从链表中删除,然后从m+1个节点起再从1开始计数,知道链表中只剩下一个节点为止。
需要注意:

  • 我们通过移动first指针,使first指针指向要出列的人,将其删除,所以需要创建一个辅助指针指向链表尾。
  • 由于从k个人开始报数,所以在报数前,需要将first和helper移动k-1次。
  • 当报数时,将first和helper移动m-1次。
  • 将first指针所处的节点删除,再将first和helper向后移动一个节点:
    first = first.next
    helper.next = first

2、代码实现

1、定义一个链表结构

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;
    }
}

2、创建一个类来定义基本方法

class CircleSingleLinkedList {
    //创建一个first节点
    private Boy first = null;

    //添加一个节点
    public void addBoy(int nums) {
        if (nums < 1) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null;//辅助节点
        for (int i = 1; i <= nums; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                first.setNext(first);
                curBoy = first;
            } else {
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }

    //遍历
    public void showBoy() {
        if (first == null) {
            System.out.println("链表为空");
            return;
        }
        Boy curBoy = first;
        while (true) {
            //System.out.printf("小孩的编号%d\n", curBoy.getNo());
            if (curBoy.getNext() == first) {
                break;
            }
            curBoy = curBoy.getNext();
        }
        
    }

    //根据用户输入计算出出圈的顺序

    /**
     *
     * @param startnum  表示从第几个小孩开始
     * @param countnum  表示数几下
     * @param nums  表示最初有几个小孩
     */
    public void countBoy(int startnum, int countnum, int nums) {
        if (first == null || startnum < 1 || startnum > nums) {
            System.out.println("参数输入有误");
            return;
        }
        Boy helper = first;
        while (true) {
            if (helper.getNext() == first) {
                break;
            }
            helper = helper.getNext();
        }
        for (int i = 0; i < startnum - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        while (true) {
            if (helper == first) {//只剩一个节点
                break;
            }
            for (int i = 0; i < countnum - 1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //此时first节点指向出圈的节点
            System.out.printf("小孩%d出圈\n",first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的是%d\n", first.getNo());
    }

}

3、写一个Demo来测试

/**
 * @author dankejun
 * @create 2020-03-27 17:33
 */
public class Josepfu {
    public static void main(String[] args) {
        CircleSingleLinkedList list = new CircleSingleLinkedList();
        list.addBoy(5);
        list.showBoy();

        list.countBoy(1, 2, 5);
    }
}

测试结果:
约瑟夫问题测试结果

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值