数据结构与算法(3)——双向链表、环形链表

1.单向链表

1.1.单向链表的缺点分析

(1)查找的方向只能是一个,而双向链表可以向前或向后查找
(2)单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除

class Node{
    public int num;
    public String name;
    public Node pre;
    public Node next;

    public Node(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Node{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }
}

1.2.双向链表的遍历

遍历方法和单向链表一样,只是可以向前也可以向后查找

//遍历双向链表
    public void list(){
        if (head.next == null){
            System.out.println("链表为空");
            return;
        }
        Node temp = head.next;
        while (true){
            if (temp == null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

1.3.双向链表的添加

(1)先找到双向链表的尾节点
(2)temp.next = newNode;
(3)newNode.pre = temp;

//末尾添加节点
    public void add(Node node){
        Node temp = head;
        while (true){
            if (temp.next == null){
                break;
            }
            temp = temp.next;
        }
        temp.next = node;
        node.pre = temp;
    }

1.4.双向链表的删除

(1)直接实现自我删除某个节点
(2)temp.pre.next = temp.next;
(3)temp.next.pre = temp.pre;

//删除一个节点
    public void del(int num){
        if (head.next == null){
            System.out.println("空链表,不能删除");
            return;
        }
        Node temp = head.next;
        boolean flag = false;
        while (true){
            if(temp == null){
                break;
            }
            if (temp.num == num){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            temp.pre.next = temp.next;
            if(temp.next != null) {
                temp.next.pre = temp.pre;//如果删除的是最后一个节点,就不需要执行这条语句
            }
        }else {
            System.out.println("没找到该节点");
        }
    }

1.5.双向链表的修改

//修改一个节点的内容
    public void update(Node node){
        if(head.next == null){
            System.out.println("空链表");
            return;
        }
        Node temp = head;
        boolean flag = false;
        while (true){
            if(temp == null){
                break;
            }
            if(temp.num == node.num){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            temp.name = node.name;
        }
    }

1.6.双向链表按顺序添加

//按标号顺序添加节点
    public void addByOrder(Node node){
        Node temp = head;
        boolean flag = false;
        while (true){
            if (temp.next == null){
                break;
            }
            if(temp.next.num > node.num){
                break;
            }
            if(temp.next.num == node.num){
                flag =true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            System.out.println("已存在不能添加");
            return;
        }else{
            if(temp.next == null){
                temp.next = node;
                node.pre = temp;
            }else{
                node.next = temp.next;
                temp.next.pre = node;
                temp.next = node;
                node.pre = temp;
            }
        }
    }

2.环形链表

应用场景:约瑟夫问题
设编号为1,2,3…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的人再次出列,以此类推直到所有人出列,由此产生的一个出队编号的序列。

2.1 环形链表的创建

(1)先创建一个节点,让first指向该节点,并让first指向自己即first.next = first
(2)每当我们创建一个新节点,便把新节点加入到已有的环形链表

class CircleSingleLinkedList{
    //创建一个first节点,当前没有编号
    private Node1 first = new Node1(-1);

    //添加小孩节点,构建成环形链表
    public void add(int nums){
        //校验nums
        if (nums < 2){
            System.out.println("nums的值不正确");
            return;
        }
        Node1 temp = null;
        //使用for循环来创建环形链表
        for(int i = 1;i<=nums;i++){
            //根据编号创建新节点
            Node1 node1 = new Node1(i);
            //如果是第一个小孩
            if(i == 1){
                first = node1;
                first.next = first; //构成环
                temp = first; //让temp指向第一个节点
            }else{
                temp.next = node1;
                node1.next = first;
                temp = temp.next;
            }
        }
    }

    //根据用户的输入,计算出小孩出圈的顺序\
    /**
     *
     * @param startnum 表示第几个小孩开始数数
     * @param countnum 表示数几下
     * @param nums 表示最初有多少小孩在圈中
     */
    public void count(int startnum,int countnum,int nums){
        //先对数据校验
        if(first == null || startnum < 1 || startnum > nums){
            System.out.println("参数输入有误请重新输入");
            return;
        }
        //创建辅助指针,并让他指向环形链表的最后一个节点
        Node1 temp = first;
        while (true){
            if (temp.next == first){
                break;
            }
            temp = temp.next;
        }
        //报数前先让first和temp移动startnum-1次
        for (int j = 0;j < startnum-1;j++){
            first = first.next;
            temp = temp.next;
        }
        //当小孩报数时,让first和temp同时移动countnum-1次,然后出圈
        //循环直到圈中只有一个节点
        while (true){
            if (temp == first){//环形链表只有一个节点
                System.out.println("最后出圈的小孩为"+first.num);
                break;
            }
            //让first和temp同时移动countnum-1次,出圈
            for (int i = 0;i < countnum-1;i++){
                first = first.next;
                temp = temp.next;
            }
            //此时first指向出圈的节点
            System.out.println("小孩"+first.num+"出圈");
            //小孩出圈
            first = first.next;
            temp.next  = first;
        }
    }
}

2.2 遍历环形链表

(1)创建一个临时指针temp指向first
(2)遍历环形链表,使得temp.next == first 时结束

//遍历当前环形链表
    public void list(){
        //判断链表是否为空
        if (first == null){
            System.out.println("链表为空");
            return;
        }
        //因为first不能动,因此创建一个临时指针temp
        Node1 temp = first;
        while (true){
            System.out.println("小孩的编号"+temp.num);
            if (temp.next == first){
                System.out.println("遍历完毕");
                break;
            }
            temp = temp.next;
        }
    }

2.3 约瑟夫问题的出圈编号

 //根据用户的输入,计算出节点出圈的顺序\
    /**
     *
     * @param startnum 表示第几个节点开始数数
     * @param countnum 表示数几下
     * @param nums 表示最初有多少节点在圈中
     */
    public void count(int startnum,int countnum,int nums){
        //先对数据校验
        if(first == null || startnum < 1 || startnum > nums){
            System.out.println("参数输入有误请重新输入");
            return;
        }
        //创建辅助指针,并让他指向环形链表的最后一个节点
        Node1 temp = first;
        while (true){
            if (temp.next == first){
                break;
            }
            temp = temp.next;
        }
        //报数前先让first和temp移动startnum-1次
        for (int j = 0;j < startnum-1;j++){
            first = first.next;
            temp = temp.next;
        }
        //当小孩报数时,让first和temp同时移动countnum-1次,然后出圈
        //循环直到圈中只有一个节点
        while (true){
            if (temp == first){//环形链表只有一个节点
                System.out.println("最后出圈的小孩为"+first.num);
                break;
            }
            //让first和temp同时移动countnum-1次,出圈
            for (int i = 0;i < countnum-1;i++){
                first = first.next;
                temp = temp.next;
            }
            //此时first指向出圈的节点
            System.out.println("小孩"+first.num+"出圈");
            //节点出圈
            first = first.next;
            temp.next  = first;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值