链表_单向链表,双向链表,环形链表

目录

【链表】

【单向链表】

普通类结构的单向列表(以循环遍历数据)

内部类结构的单向列表(以递归遍历数据)

几个常见单向链表功能实现(以第一个源码例)

【双向链表】

【环形链表】


【链表】

简介:链表是有序列表,链式存储,结点包含data域,next域,各结点间不一定连续存储,链表从根节点有无存储数据可分为:带头链表(根节点有数据),不带头链表(根节点无数据)。链表的实现有很多结构:普通类,结构体,内部类...,数据遍历方式有循环,递归...

分类:单向链表,双向链表,环形链表

【单向链表】

普通类结构的单向列表(以循环遍历数据)

 简介:单向-普通类-循环-有头

结构分析:

1,创建两个类:Node类实现数据存储,输出格式等。LinkedList类实现根节点创建赋值,实现add,addByOrder,remove,show 等对链表的操作方法。

2,add:声明一个辅助结点temp并赋值为root,通过循环遍历链表,每次循环通过temp=temp.next来向后一位,当temp.next==null时为链表末,跳出循环,利用temp.next=newNode添加结点

3,addByOrder:声明一个辅助结点temp并赋值为root,声明一个标志用于标记是否是找到正确位置后跳出循环的,通过循环遍历链表,每次循环通过temp=temp.next来向后一位,当temp.next==null时为链表末,跳出循环,利用temp.next=newNode添加结点。当temp.next.no>newNode.no时查找到了应该插入的位置(升序),改标志为true跳出循环,利用newNode.next=temp.next;temp.next=newNode来插入结点

4,modify:通过类似于上述循环遍历过程,当匹配到与要修改序号相同的结点后,通过temp.next.data=newNode.data来修改结点

5,remove:通过类似于上述循环遍历过程,当匹配到与要删除序号相同的结点后,通过temp.next=temp.next.next来删除结点,该结点将成为垃圾数据被GC回收。

6,show:当链表非空时,遍历打印即可

代码实现:

package cn.dataStructure.demo;

import java.util.LinkedHashSet;

class Node{//结点类
    public int no;
    public String name;
    public Node next;//保存下一个结点
    public Node(int no,String name){
        this.no=no;
        this.name=name;
    }

    @Override
    public String toString() {
        return "["+this.no+"]"+"["+this.name+"]";
    }
}
class LinkedList{
    private Node root=new Node(0,"");//创建根节点
    public void add(Node newNode){//普通add:不按序号存储
        Node temp=root;//辅助结点
        while (true){//查找链表最后结点
            if (temp.next==null){
                break;
            }
            temp=temp.next;//向后一位
        }
        temp.next=newNode;//添加结点
    }
    public void addByOrder(Node newNode){//按顺序add
        Node temp=root;
        boolean flag=false;//是否为重复编号
        while (true){
            if (temp.next==null){//找到链表最后一位
                break;
            }
            if (temp.next.no>newNode.no){//找到合适位置(升序)
                break;
            }
            if (temp.next.no==newNode.no){//找到重复编号
                flag=true;
                break;
            }
            temp=temp.next;//向后一位
        }
        if (flag){
            System.out.println("查找到重复数据,无法加入");
            return;
        }else {
            //插入新结点
            newNode.next=temp.next;
            temp.next=newNode;
        }
    }
    public void modify(Node newNode){//根据Node中的no来匹配要修改的结点
        Node temp=root;
        boolean flag=false;//判断是否找到了序号一样Node
        while (true){
            if (temp.next==null){//遍历完链表,没找到
                break;
            }
            if (temp.next.no==newNode.no){//找到了序号一样的
                flag=true;
                break;
            }
            temp=temp.next;//后退一位
        }
        if (flag){
            temp.next.name=newNode.name;
        }else {
            System.out.println("没有找到要修改结点");
        }
    }
    public void remove(int no){//删除结点
        Node temp=root;
        boolean flag=false;//判断是否找到了序号一样Node
        while (true){
            if (temp.next==null){//遍历完链表,没找到
                break;
            }
            if (temp.next.no==no){//找到了序号一样的
                flag=true;
                break;
            }
            temp=temp.next;//后退一位
        }
        if (flag){
            temp.next=temp.next.next;//删除结点
        }else {
            System.out.println("没有找到要删除结点");
        }
    }
    public void show(){
        if (root.next==null){//判断是否为空链表
            System.out.println("链表为空");
            return;
        }
        Node temp=root;
        while (true){//遍历打印
            if (temp.next==null){
                break;
            }
            System.out.println(temp.next.toString());
            temp=temp.next;//向后一位
        }
    }
}
public class 单链表_LinkedList_普通类循环实现 {
    public static void main(String[] args) {
        LinkedList link=new LinkedList();

//        link.add(new Node(1,"小米"));
//        link.add(new Node(3,"小明"));
//        link.add(new Node(2,"小红"));
//        System.out.println("不按序号add:");
//        link.show();

        link.addByOrder(new Node(1,"小米"));
        link.addByOrder(new Node(3,"小明"));
        link.addByOrder(new Node(2,"小红"));
        System.out.println("按序号add:");
        link.show();

        link.modify(new Node(3,"小华"));

        link.remove(2);
        System.out.println("修改后:");
        link.show();
    }
}

上述链表:单向-普通类-循环-有头,但普通类结构的问题在于Node类数据为了LinkedList类的使用便利牺牲了封装性(当然也可以用get方法获取私有成员的形式,但有点麻烦)。为了解决既要访问数据便利,又要维持封装性,那么就启用Java方便的内部类吧!

内部类结构的单向列表(以递归遍历数据)

 简介:单向-内部类-递归-无头

结构分析:

【ILink接口】:统一了链表操作规范,并在隐藏操作细节的同时暴露了链表操作的方法。
【LinkImple】:用于实现ILink中的方法,在其中拥有一个私有内部类 Node结点类,通过该类保存结点信息,该内部类中还有供外部类LinkImple调用的方法如:addNode,toArrayNode,getNode,setNode,removeNode,containsNode…这些方法都沿用了递归的思想,通过Node对象与Node的Next对象,不断递归找到条件满足的状态,实现具体的功能。这些方法给外部类LinkImple的实现方法来使用。
【add方法实现】:外部类先剔除空数据后,开辟一个Node类对象,判定根节点root是否为空,空就对象赋予root,否则通过内部类的addNode方法利用递归的形式找到空结点并赋值。
【count方法实现】:在外部类中有count变量,每当add一个数据时,就加一,在外部类count方法中只需要返回count值即可。
【isEmpty方法实现】:判断count数据是否为0即可。
【toArray方法实现】:在外部类中有一个Object类型的数组叫returnData[],外部类toArray方法中调用isEmpty方法先判断不为空后,通过内部类中的toArray的方法递归的将数据一个个填入returnData中,外部类只需要返回returnData即可。
【get方法实现】:当输入的索引小于链表长度时,通过调用内部类中的getNode方法来递归的寻找索引位置的结点,并返回其数据。
【set方法实现】:与get方法类似
【contains方法实现】:当输入数据不为空时,通过内部类中的containsNode方法来递归的寻找与输入数据相同的结点。找到了返回true。
【remove方法实现】:分为删除根节点与删除子节点之分,外部类中的remove方法先与根节点数据比较,相同时通过root.next赋值给root,来删除根节点。不相同时将此结点做为前一个结点返回给内部类中的removeNode方法通过递归到相同数据的结点时,将此结点的next赋值于上一个结点的next,完成此节点删除。注意外部类要将count数据减一
【clean方法实现】:将root赋值null数据即可。

package cn.dataStructure.demo;
interface ILink<E>
{
    public void add(E e);//链表数据增加
    public int count();//获取链表元素个数
    public boolean isEmpty();//空集合判断
    public Object[] toArray();//返回链表数据
    public E get(int index);//根据索引取得数据
    public void set(int index,E data);//修改链表数据
    public boolean contains(E data);//数据内容查询
    public void remove(E data);//删除链表数据:1.删除根节点,2.删除子节点
    public void clean();//清空链表数据
}
class LinkImple<E> implements ILink<E>
{
    private int count;
    private Node root;
    private int foot;
    private Object [] returnData;

    private class Node
    {
        private E data;
        private Node next;
        public Node(E data){//节点存储数据操作
            this.data=data;
        }
        public void addNode(Node newNode){//节点存储下一个节点操作
            if(this.next==null){//当前对象的下一个节点内存为空时,把传入的节点对象赋给当前对象的下一个节点内存中
                this.next=newNode;
            }else{//当前对象的下一个节点内存非空时,通过递归调用来实现节点对象的保存
                this.next.addNode(newNode);
            }
        }
        public void toArrayNode(){
            LinkImple.this.returnData[LinkImple.this.foot++]=this.data;
            if(this.next!=null){
                this.next.toArrayNode();
            }
        }
        public E getNode(int index){
            if(LinkImple.this.foot++ == index){
                return this.data;
            }else{
                return this.next.getNode(index);//将此处得到的数据再用一个return返回到调用处
            }
        }
        public void setNode(int index,E data){
            if(LinkImple.this.foot++ == index){
                this.data=data;
            }else{
                this.next.setNode(index,data);
            }
        }
        public boolean containsNode(E data){
            if(data.equals(this.data)){
                return true;
            }else if(this.next==null){
                return false;
            }else{
                return this.next.containsNode(data);
            }
        }
        public void removeNode(Node previous,E data){//删除子节点
            if(data.equals(this.data)){
                previous.next=this.next;
            }else if(this.next!=null){
                this.next.removeNode(this,data);
            }
        }
    }

    public void add(E e){
        if(e==null){
            return ;
        }
        Node newNode=new Node(e);
        if(this.root==null){
            root=newNode;
        }else{
            this.root.addNode(newNode);
        }
        count++;
    }
    public int count(){
        return this.count;
    }
    public boolean isEmpty(){
        //return this.root==null;
        return this.count==0;
    }
    public Object[] toArray(){
        if(this.isEmpty()){
            return null;
        }
        this.foot=0;
        this.returnData=new Object[this.count];
        this.root.toArrayNode();
        return returnData;
    }
    public E get(int index){
        if(index>=count){
            return null;
        }
        this.foot=0;
        return this.root.getNode(index);
    }
    public void set(int index,E data){
        if(index>=count){
            return ;
        }
        this.foot=0;
        this.root.setNode(index,data);
    }
    public boolean contains(E data){
        if(data==null){
            return false;
        }
        return this.root.containsNode(data);
    }
    public void remove(E data){//删除根节点
        if(data==null){
            return ;
        }
        if(data.equals(this.root.data)){
            this.root=this.root.next;
        }else{
            this.root.removeNode(this.root,data);
        }
        count--;
    }
    public void clean(){
        this.root=null;
        count=0;
    }
}
public class 单链表_LinkedList_内部类递归实现
{
    public static void main(String agrs[]){
        ILink <String>link=new LinkImple <String> ();
        System.out.println("初始化数据:"+link.count()+"\t 是否为空集合:"+link.isEmpty());
        link.add("hello");
        link.add("world");
        link.add("!");
        System.out.println("结果数据:"+link.count()+"\t 是否为空集合:"+link.isEmpty());
        Object[] result=link.toArray();
        for(Object temp:result){
            System.out.println(temp);
        }
        System.out.println("---------------------根据索引获取链表元素--------------------");
        System.out.println(link.get(1));
        System.out.println("---------------------根据索引修改链表元素--------------------");
        link.set(1,"WORLD");
        System.out.println(link.get(1));
        System.out.println("---------------------根据数组盘判断是否存在对应链表元素--------------------");
        System.out.println(link.contains("WORLD"));
        System.out.println("---------------------根据自动内容删除对应链表元素--------------------");
        link.remove("WORLD");
        Object[] resultB=link.toArray();
        for(Object temp:resultB){
            System.out.println(temp);
        }
        System.out.println("---------------------清空链表元素--------------------");
        link.clean();
        System.out.println(link.isEmpty());
    }
}



初始化数据:0	 是否为空集合:true
结果数据:3	 是否为空集合:false
hello
world
!
---------------------根据索引获取链表元素--------------------
world
---------------------根据索引修改链表元素--------------------
WORLD
---------------------根据数组盘判断是否存在对应链表元素--------------------
true
---------------------根据自动内容删除对应链表元素--------------------
hello
!
---------------------清空链表元素--------------------
true

我喜欢这种单向链表,但在同等条件下,递归性能弱于循环,所以在链表较长的情况下采用循环的形式遍历数据,会更好

几个常见单向链表功能实现(以第一个源码例)

1,求单链表有效结点个数

非空-->循环遍历计数

public static int getLength(Node root){
        if (root.next==null){
            return 0;
        }
        int length=0;
        Node temp=root.next;
        while (temp!=null){
            length++;
            temp=temp.next;
        }
        return length;
    }

2,查找单链表的倒数第K个结点

非空-->遍历获取链表长度length-->遍历找到第length-k个结点

public static Node findLastIndexNode(Node root,int index){
        if (root.next==null){
            return null;
        }
        int length=0;
        Node temp=root.next;
        while (temp!=null){
            length++;
            temp=temp.next;
        }
        if (index>length||index<=0){
            return null;
        }
        temp=root.next;
        for (int i=0;i<length-index;i++){
            temp=temp.next;
        }
        return temp;
    }

3,单向链表翻转

空或只有一个有效结点-->非空多个结点-->新定义一个存放翻转链表的链表-->遍历原链表,每遍历一个结点就把结点摘下来按在新链表的最前面(将结点从原链表拆下并与新链表的原先最前结点相连,将拆下的结点与新链表的头结点相连)-->最后将新链表的有效结点串连入原头结点即可

 

public static void reverseList(Node root){
        if (root.next==null||root.next.next==null){
            return;
        }
        Node reverse=new Node(0,"");//新建翻转链表根节点
        Node temp=root.next;
        Node next=null;//由于temp在拆结点时next会发生变化,所以要先保存一下
        while (temp!=null){
            next=temp.next;
            temp.next=reverse.next;//将结点从原链表拆下并与新链表的原先最前结点相连
            reverse.next=temp;//将拆下的结点与新链表的头结点相连
            temp=next;//将原结点向后一位
        }
        root.next=reverse.next;//将翻转好的有效新链表接入原头结点之下
    }

4,逆序打印链表

非空-->遍历压栈-->遍历出栈(利用了栈的先入后出原则。不能用3中翻转后再打印,这样破坏了原链表。需要导入java.util.stack)

public static void reversePrint(Node root){
        if (root.next==null){
            return;
        }
        Node temp=root.next;
        Stack <Node> stack=new Stack<>();//创栈
        while (temp!=null){
            stack.push(temp);//遍历压栈
            temp=temp.next;
        }
        while (stack.size()>0){
            System.out.println(stack.pop());//stack先入后出
        }
    }

5,合并两个有序链表,合并之后仍有序

【方法1】循环顺序合并

新建混合顺序链表link3-->当两个链表都非空-->谁结点小把谁添加到link3的链表中(千万注意:要把链表中对应的结点初始化。由于循环的link1,link2链表结点的next域里很可能语句有了数据,所以一定要把结点中数据值重新赋给一个新建的Node 对象中,再调用add方法。否则给link3添加的link1,link2中的链表会连接到链表其余的结点上,最终link3链表将无限长,add方法将进入死循环。我哭了,为了这个bug,我deBug了一晚上!!)该方法前提两个链表是顺序链表

public static LinkedList conbineByOreder(LinkedList link1,LinkedList link2){
        LinkedList link3=new LinkedList();
        Node temp1=link1.root.next;
        Node temp2=link2.root.next;
        while (temp1!=null && temp2!=null){
            if (temp1.no < temp2.no){
                Node node=new Node(temp1.no,temp1.data);//就是这,要把链表中对应的结点初始化,否则你会哭
                link3.add(node);
                temp1=temp1.next;
            }else {
                Node node=new Node(temp2.no,temp2.data);//就是这,要把链表中对应的结点初始化,否则你会哭
                link3.add(node);
                temp2=temp2.next;
            }
        }
        if (temp1==null){
            while (temp2!=null){
                Node node=new Node(temp2.no,temp2.data);//就是这,要把链表中对应的结点初始化,否则你会哭
                link3.add(node);
                temp2=temp2.next;
            }
        }else {
            if (temp2==null){
                while (temp1!=null){
                    Node node=new Node(temp1.no,temp1.data);//就是这,要把链表中对应的结点初始化,否则你会哭
                    link3.add(node);
                    temp1=temp1.next;
                }
            }
        }
        return link3;
    }

【方法2】递归顺序合并(需要无头的链表,或者把有头链表无头化)

获取链表最前的有效结点(有头根结点.next)-->方法体内先判断两个链表的node1,node2哪个结点为空,谁空了返回对方的值-->都不为空时node1.no与node2.no比较,谁小赋值给node3,再将node3.next通过递归赋值,递归的参数为 小的node.next和大的node。该方法前提两个链表是顺序链表

public static Node cobineByOrder(Node linkValue1,Node linkValue2){
        if (linkValue1==null){
            return linkValue2;
        }
        if (linkValue2==null){
            return linkValue1;
        }
        Node linkValue3=null;//混合顺序链表
        //递归形成混合顺序链表
        if (linkValue1.no < linkValue2.no){
            linkValue3=linkValue1;
            linkValue3.next=cobineByOrder(linkValue1.next,linkValue2);
        }else {
            linkValue3=linkValue2;
            linkValue3.next=cobineByOrder(linkValue1,linkValue2.next);
        }
        return linkValue3;
    }

单向链表已经可以实现不少功能了,但单向链表只能正向打印,而实现逆向打印很麻烦,且无法实现节点自我删除,要依靠辅助结点指向要删除结点的上一个再删除。解决方案:双向链表

【双向链表】

所谓双向,只不过是Node结点中多了一个pre用来存储上一个结点,下面以 双向-普通类-循环-有头 链表为例:

结构分析:

1,show,modify方法不变

2,add方法要添加 newNode.pre=temp 来连接上一个结点。addByOrder方法在插入结点时要先进行空判断(防止在链末添加结点时,下一个结点为null没有pre,再连pre时引起的空指向异常),非空 temp.next.pre=newNode; newNode.pre=temp 来连接上一个结点

3,新增reverseShow方法,用于逆向打印:非空-->遍历到最后-->利用pre向前遍历

4,remove方法与addByOrder方法一样在插入结点时要先进行空判断,非空 temp.next.next.pre=temp.pre 来覆盖上一个结点连接

package cn.dataStructure.demo;
class DNode{//结点类
    public int no;
    public String data;
    public DNode next;//保存下一个结点
    public DNode pre;//保存上一个结点
    public DNode(int no,String data){
        this.no=no;
        this.data =data;
    }

    @Override
    public String toString() {
        return "["+this.no+"]"+"["+this.data +"]";
    }
}
class DoubleLinkedList{//双向-普通类-有头-循环
    private DNode root=new DNode(0,"");//创建根节点
    public DNode getRoot(){
        return this.root;
    }
    public void add(DNode newNode){//普通add:不按序号存储
        DNode temp=this.root;//root不能动,需要辅助结点
        while (true){//查找链表最后结点
            if (temp.next==null){
                break;
            }
            temp=temp.next;//向后一位
        }
        temp.next=newNode;//尾结点双向添加
        newNode.pre=temp;
    }
    public void addByOrder(DNode newNode){//按顺序add
        DNode temp=root;
        boolean flag=false;//是否为重复编号
        while (true){
            if (temp.next==null){//找到链表最后一位
                break;
            }
            if (temp.next.no>newNode.no){//找到合适位置(升序)
                break;
            }
            if (temp.next.no==newNode.no){//找到重复编号
                flag=true;
                break;
            }
            temp=temp.next;//向后一位
        }
        if (flag){
            System.out.println("查找到重复数据,无法加入");
            return;
        }else {
            //插入新结点
            newNode.next=temp.next;
            temp.next=newNode;
            //在逆向连接的时候注意,若处于链尾时,next为空没有pre不能先前连,否则会空指向异常
            if (temp.next!=null){
                temp.next.pre=newNode;
                newNode.pre=temp;
            }else {
                newNode.pre=temp;
            }
        }
    }
    public void show(){
        if (root.next==null){//判断是否为空链表
            System.out.println("链表为空");
            return;
        }
        DNode temp=root;
        while (true){//遍历打印
            if (temp.next==null){
                break;
            }
            System.out.println(temp.next.toString());
            temp=temp.next;//向后一位
        }
    }
    public void modify(DNode newNode){//根据DNode中的no来匹配要修改的结点
        DNode temp=root;
        boolean flag=false;//判断是否找到了序号一样DNode
        while (true){
            if (temp.next==null){//遍历完链表,没找到
                break;
            }
            if (temp.next.no==newNode.no){//找到了序号一样的
                flag=true;
                break;
            }
            temp=temp.next;//后退一位
        }
        if (flag){
            temp.next.data =newNode.data;
        }else {
            System.out.println("没有找到要修改结点");
        }
    }
    public void remove(int no){//删除结点
        DNode temp=root;//注意这里是头结点所以temp.next=temp.next.next没问题,如果不是头结点而是root.next,需要进行空判断
        boolean flag=false;//判断是否找到了序号一样DNode
        while (true){
            if (temp.next==null){//遍历完链表,没找到
                break;
            }
            if (temp.next.no==no){//找到了序号一样的
                flag=true;
                break;
            }
            temp=temp.next;//后退一位
        }
        if (flag){
            temp.next=temp.next.next;//删除正向连接,这里不会空指向异常,因为有空的头结点
            if (temp.next!=null){//不能是temp.next.next因为此处temp.next已经被上面的语句替换!
                temp.next.next.pre=temp.pre;//删除逆向连接,这里会空指向异常,要空判断
            }

        }else {
            System.out.println("没有找到要删除结点");
        }
    }
    public void reverseShow(){//逆向打印
        if (root.next==null){//判断是否为空链表
            System.out.println("链表为空");
            return;
        }
        DNode temp=root;
        while (temp.next!=null){//遍历到链表最后
            temp=temp.next;
        }
        while (temp!=root){//逆向打印
            System.out.println(temp);
            temp=temp.pre;
        }
    }

}
public class 双向链表_DoubleLinkedList {
    public static void main(String[] args) {
        DoubleLinkedList dlink=new DoubleLinkedList();
        dlink.addByOrder(new DNode(1,"one"));
        dlink.addByOrder(new DNode(4,"three"));
        dlink.addByOrder(new DNode(2,"two"));
        dlink.addByOrder(new DNode(3,"three"));
        System.out.println("按顺序add:");
        dlink.show();

        System.out.println("修改结点:");
        dlink.modify(new DNode(4,"four"));
        dlink.show();

        System.out.println("删除结点:");
        dlink.remove(4);
        dlink.show();

        System.out.println("逆向打印:");
        dlink.reverseShow();
    }
}

 

按顺序add:
[1][one]
[2][two]
[3][three]
[4][three]
修改结点:
[1][one]
[2][two]
[3][three]
[4][four]
删除结点:
[1][one]
[2][two]
[3][three]
逆向打印:
[3][three]
[2][two]
[1][one]

 

【环形链表】

就是把非环形链表的尾结点的next域指向根节点,下面以 环形-单向-普通类-循环-无头 为例(顺手解决约瑟夫Josephu问题):

 结构分析:

1,add方法:是否为第一个结点,是就自成一环-->不是就遍历到最后-->向环中插入结点

2,AutoAdd方法:也就是一个循环里面每次自动创建结点后调用add方法添加,建立从1-n的顺序环链

3,count方法:用于解决约瑟夫问题,通过建立两个辅助结点(temp,select)。先移动开始结点数-1次,使select移动到开始的结点处,后移动开始结点数-2次,使temp移动到select之后,通过定数循环,在每轮循环后打印select结点,并将select向后一位,利用temp删除刚刚被选中的结点即可

package cn.dataStructure.demo;
class CNode{
    private int data;
    private CNode next;

    public CNode(int no){
        this.data =no;
    }

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

    public CNode getNext() {
        return next;
    }

    public int getData() {
        return data;
    }

    @Override
    public String toString() {
        return "["+this.data +"]";
    }
}
class CirleLinkedList{
    private CNode root;

    public CNode getRoot(){
        return root;
    }

    public void add(CNode newNode){
        if (root==null){//当添加第一个结点时自成一环
            root=newNode;
            root.setNext(newNode);
        }
        CNode temp=root;
        while (temp.getNext()!=root){//循环遍历到环的最后
            temp=temp.getNext();
        }
        //向环中插入结点
        temp.setNext(newNode);
        temp.getNext().setNext(root);
    }
    public void show(){
        CNode temp=root;
        while (true){
            System.out.println(temp);
            if (temp.getNext()==root){//判断是否遍历完毕
                break;
            }
            temp=temp.getNext();
        }
    }
    public void AutoAdd(int num){
        if (num<1){//不足1,无法形成环
            return;
        }
        CNode node;
        for (int i=1;i<=num;i++){
            node=new CNode(i);
            add(node);
        }
    }

    /**
     * 解决约瑟夫问题
     * @param all 总人数
     * @param startNo 从第几个人开始
     * @param num 数多少下
     */
    public void count(int all,int startNo,int num){
        if (root==null||startNo<1||startNo>all){//输入验证
            System.out.println("输入参数有误");
            return;
        }
        //需要两个辅助结点,用于确定被选中人,和被选中人的前一个人,便于结点删除
        CNode temp=root;
        CNode select=root;
        for (int i=0;i<startNo-1;i++){//先移动startNo-1次,使select移动到开始的结点处
            select=select.getNext();
        }
        for (int i=0;i<startNo-2;i++){//后移动startNo-2次,使temp移动到select之后
            temp=temp.getNext();
        }
        while (true){//模拟报数选人
            if (temp==select){//只剩一个人了
                System.out.printf("幸运儿为:%d",select.getData());
                break;
            }
            for (int i=0;i<num-1;i++){//自己也要报数,所以-1
                temp=temp.getNext();
                select=select.getNext();
            }
            System.out.printf("编号%d的被选中\n",select.getData());
            //删除被选中结点
            select=select.getNext();
            temp.setNext(select);
        }

    }
}
public class 环形单向链表_CirleLinkedList_约瑟夫问题 {
    public static void main(String[] args) {
        CirleLinkedList cLink=new CirleLinkedList();
//        cLink.add(new CNode(1));
//        cLink.add(new CNode(2));
//        cLink.add(new CNode(3));
//        cLink.add(new CNode(4));
        cLink.AutoAdd(10);
//        cLink.show();
        cLink.count(10,2,3);
    }
}
编号4的被选中
编号7的被选中
编号10的被选中
编号3的被选中
编号8的被选中
编号2的被选中
编号9的被选中
编号6的被选中
编号1的被选中
幸运儿为:5

 

【数据结构与算法整理总结目录 :>】<-- 宝藏在此(doge)  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值