【Java数据结构与算法】基础篇(2):单链表、双链表、单向环形链表、约瑟夫环

10 篇文章 0 订阅
9 篇文章 0 订阅

大家好,我是皮皮猫吖!

每文一言:昨天已经过去,明天一切未知,但"今天”是上帝赐给我们的“礼物”。

本篇文章:

本篇文章主要是关于java数据结构与算法的一些基本知识:单链表、双向链表、单向循环链表、约瑟夫环。

正文如下:

1、单链表

1)链表是什么?

链表是有序的列表,但是它在内存中是存储如下:

在这里插入图片描述

① 链表是以节点的方式来存储的,是一种链式存储

② 每个链节点包含data域、next 域【指向下一个节点】

③ 发现链表的各个节点不一定是连续存储

④ 链表分为带头节点的链表和没有头节点的链表,需要根据实际的需求来确定

在这里插入图片描述

2)单链表的创建【在链表末尾添加数据】:

① 添加数据:

  • 创建一个head头节点,用来作为这个单链表的头节点
  • 每添加一个节点,就将该节点直接加入到链表最后

② 遍历:

  • 通过头节点,创建一个临时变量,用来遍历单链表
package com.data.structure.linkedList;

/**
 * @author imppm
 * @create 2021-02-10-17:04
 */
public class LinkedListDemo {
    public static void main(String[] args) {
        HeroNode hero1 = new HeroNode(1,"小猫", "xmbb");
        HeroNode hero2 = new HeroNode(2,"臭皮皮", "cpp");
        HeroNode hero3 = new HeroNode(3,"皮皮猫吖", "ppmy");

        LinkedHeroNode linkedHeroNode = new LinkedHeroNode();
        linkedHeroNode.add(hero1);
        linkedHeroNode.add(hero2);
        linkedHeroNode.add(hero3);

        linkedHeroNode.list();
    }
}


class LinkedHeroNode{
    private final HeroNode head = new HeroNode(0,"","");

    //添加节点到单向链表
    //1.找到当前链表的最后一个节点
    //2.在当前链表的末尾添加上当前节点
    public void add(HeroNode heroNode){
        HeroNode current = head;
        //带头结点的遍历方式
        while (current.getNext() != null) {
            //找到链表最后
            //向后移动
            current = current.getNext();
        }
        //退出循环,此时current指向链表的最后
        current.setNext(heroNode);
    }

    //显示链表【遍历】
    public void list(){
        if(head.getNext() != null){
            HeroNode current = head.getNext();
            while(current!=null){
                System.out.println(current);
                current = current.getNext();
            }
        }else{
            System.out.println("链表为空!");
        }
    }
}

//定义HeroNode,每个heroNode都是一个节点
class HeroNode{
    private int no;
    private String name;
    private String nickName;
    private HeroNode next;//指向下一个heroNode节点

    public HeroNode(int no, String name, String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    public HeroNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }


}

3)单链表的创建【根据数据中的序号值添加数据】

① 添加数据:

  • 创建一个head头节点,用来作为这个单链表的头节点
  • 添加节点,在添加节点之前,需要比较添加节点的数据的序号值大于某个节点,小于另外一个节点,正好添加到中间位置

② 遍历:

  • 通过头节点,创建一个临时变量,用来遍历单链表
package com.data.structure.linkedList;

/**
 * 单链表添加数据
 * @author imppm
 * @create 2021-02-28-22:01
 */
public class LinkedListDemo_1 {
    public static void main(String[] args) {
        LinkedListStudentNode list = new LinkedListStudentNode();

        StudentNode student1 = new StudentNode(1, "小猫",'女',null);
        StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
        StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
        StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
        StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);

        list.add(student1);
        list.add(student2);
        list.add(student3);
        list.add(student4);

        list.addStudentByNo(student1);
        list.addStudentByNo(student0);
        list.list();
    }
}


class LinkedListStudentNode{
    //单链表的头节点
    private final StudentNode head = new StudentNode();

    //判断单链表是否为空
    private boolean isEmpty(StudentNode studentNode){
        return studentNode.getNext() == null;
    }

    //添加数据到单链表到末尾处
    public void add(StudentNode node){
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
        }
        current.setNext(node);
        System.out.println(node+"链表末尾添加数据成功");
    }

    //按照单链表no值添加数据
    public void addStudentByNo(StudentNode studentNode){
        //获取单链表头节点
        StudentNode current = head;
        //用于判断是否可以插入
        //false:可以插入
        //true:不可以插入,单链表中已存在与该no的数据
        boolean flag = false;
        while(true){
            //判断当前节点的下一个节点是否为空
            if(current.getNext()==null){
                break;
            }

            //判断当前节点的下一个节点的no值是否大于要插入的节点的值
            if(current.getNext().getNo() > studentNode.getNo()){
                //找到需要插入的位置
                break;
                //判断当前节点的下一个节点的no与要插入节点的no大小是否相等
            } else if(current.getNext().getNo() == studentNode.getNo()){
                //标记为设置为true,表示不可插入
                flag = true;
                break;
            }

            //节点的指针后移
            current = current.getNext();
        }

        //判断当前节点是否可以插入
        if(!flag) {
            //可以插入
            //要插入的节点指向原来节点的下一个节点
            studentNode.setNext(current.getNext());
            //原来的节点的下一个节点指向要插入的节点
            current.setNext(studentNode);
            System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
        }else{
            System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
        }

    }

    //遍历单链表
    public void list(){
        if(isEmpty(head)){
            System.out.println("该单链表为空");
            return;
        }
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
            System.out.println(current);
        }
    }






}

class StudentNode{
    private int no;
    private String name;
    private char gender;
    private StudentNode next;

    public StudentNode() {
    }

    public StudentNode(int no, String name, char gender, StudentNode next) {
        this.no = no;
        this.name = name;
        this.gender = gender;
        this.next = next;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public StudentNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "StudentNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", next=" + next +
                '}';
    }
}

4)单链表的修改【根据数据中的序号值修改当前序号值对应的数据信息】
package com.data.structure.linkedList;

/**
 * 单链表添加数据
 * @author imppm
 * @create 2021-02-28-22:01
 */
public class LinkedListDemo_1 {
    public static void main(String[] args) {
        LinkedListStudentNode list = new LinkedListStudentNode();

        StudentNode student1 = new StudentNode(1, "小猫",'女',null);
        StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
        StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
        StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
        StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);

        list.add(student1);
        list.add(student2);
        list.add(student3);
        list.add(student4);

//        list.list();


        list.addStudentByNo(student1);
        list.addStudentByNo(student0);

        list.updateStudentByNo(new StudentNode(2,"臭皮皮呀",'男',null));
        list.list();
    }
}


class LinkedListStudentNode{
    //单链表的头节点
    private final StudentNode head = new StudentNode();

    //判断单链表是否为空
    private boolean isEmpty(StudentNode studentNode){
        return studentNode.getNext() == null;
    }

    //添加数据到单链表到末尾处
    public void add(StudentNode node){
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
        }
        current.setNext(node);
        System.out.println(node+"链表末尾添加数据成功");
    }

    //按照单链表no值添加数据
    public void addStudentByNo(StudentNode studentNode){
        //获取单链表头节点
        StudentNode current = head;
        //用于判断是否可以插入
        //false:可以插入
        //true:不可以插入,单链表中已存在与该no的数据
        boolean flag = false;
        while(true){
            //判断当前节点的下一个节点是否为空
            if(current.getNext()==null){
                break;
            }

            //判断当前节点的下一个节点的no值是否大于要插入的节点的值
            if(current.getNext().getNo() > studentNode.getNo()){
                //找到需要插入的位置
                break;
                //判断当前节点的下一个节点的no与要插入节点的no大小是否相等
            } else if(current.getNext().getNo() == studentNode.getNo()){
                //标记为设置为true,表示不可插入
                flag = true;
                break;
            }

            //节点的指针后移
            current = current.getNext();
        }

        //判断当前节点是否可以插入
        if(!flag) {
            //可以插入
            //要插入的节点指向原来节点的下一个节点
            studentNode.setNext(current.getNext());
            //原来的节点的下一个节点指向要插入的节点
            current.setNext(studentNode);
            System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
        }else{
            System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
        }

    }

    //遍历单链表
    public void list(){
        if(isEmpty(head)){
            System.out.println("该单链表为空");
            return;
        }
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
            System.out.println(current);
        }
    }




    //根据no修改信息
    public void updateStudentByNo(StudentNode node){
        //头节点
        StudentNode current = head;


        boolean flag = false;
        while(true){
            if(current.getNext()==null){
                break;
            }

            if(current.getNext().getNo() == node.getNo()){
                flag = true;
                break;
            }

            current = current.getNext();
        }

        if(flag){
            if(current.getNext().getNext()!=null){
                node.setNext(current.getNext().getNext());
            }
            current.setNext(node);
            System.out.println("编号为"+node.getNo()+"的数据修改信息成功");
        }else{
            System.out.println("编号为"+node.getNo()+"的数据修改信息失败");
        }
    }




}

class StudentNode{
    private int no;
    private String name;
    private char gender;
    private StudentNode next;

    public StudentNode() {
    }

    public StudentNode(int no, String name, char gender, StudentNode next) {
        this.no = no;
        this.name = name;
        this.gender = gender;
        this.next = next;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public StudentNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "StudentNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", next=" + next +
                '}';
    }
}

5)单链表的删除【根据数据中的序号值删除当前序号值对应的数据信息】
① 注意:
  • 遍历单链表的时候,需要找到待删除节点的前一个节点。
  • 设置前一个节点的Next指向前一个节点的Next节点的Next节点current.setNext(current.getNext().getNext());
  • 被删除的节点将会被垃圾回收器回收
package com.data.structure.linkedList;

/**
 * 单链表添加数据
 * @author imppm
 * @create 2021-02-28-22:01
 */
public class LinkedListDemo_1 {
    public static void main(String[] args) {
        LinkedListStudentNode list = new LinkedListStudentNode();

        StudentNode student1 = new StudentNode(1, "小猫",'女',null);
        StudentNode student2 = new StudentNode(2, "臭皮皮",'男',null);
        StudentNode student3 = new StudentNode(3, "皮皮猫",'女',null);
        StudentNode student4 = new StudentNode(4, "皮皮猫吖",'女',null);
        StudentNode student0 = new StudentNode(0, "小猫宝贝吖",'女',null);

        list.add(student1);
        list.add(student2);
        list.add(student3);
        list.add(student4);

//        list.list();


        list.addStudentByNo(student1);
        list.addStudentByNo(student0);

        list.updateStudentByNo(new StudentNode(2,"臭皮皮呀",'男',null));
        list.deleteStudentNodeByNo(3);
        list.deleteStudentNodeByNo(5);
        list.list();
    }
}


class LinkedListStudentNode{
    //单链表的头节点
    private final StudentNode head = new StudentNode();

    //判断单链表是否为空
    private boolean isEmpty(StudentNode studentNode){
        return studentNode.getNext() == null;
    }

    //添加数据到单链表到末尾处
    public void add(StudentNode node){
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
        }
        current.setNext(node);
        System.out.println(node+"链表末尾添加数据成功");
    }

    //按照单链表no值添加数据
    public void addStudentByNo(StudentNode studentNode){
        //获取单链表头节点
        StudentNode current = head;
        //用于判断是否可以插入
        //false:可以插入
        //true:不可以插入,单链表中已存在与该no的数据
        boolean flag = false;
        while(true){
            //判断当前节点的下一个节点是否为空
            if(current.getNext()==null){
                break;
            }

            //判断当前节点的下一个节点的no值是否大于要插入的节点的值
            if(current.getNext().getNo() > studentNode.getNo()){
                //找到需要插入的位置
                break;
                //判断当前节点的下一个节点的no与要插入节点的no大小是否相等
            } else if(current.getNext().getNo() == studentNode.getNo()){
                //标记为设置为true,表示不可插入
                flag = true;
                break;
            }

            //节点的指针后移
            current = current.getNext();
        }

        //判断当前节点是否可以插入
        if(!flag) {
            //可以插入
            //要插入的节点指向原来节点的下一个节点
            studentNode.setNext(current.getNext());
            //原来的节点的下一个节点指向要插入的节点
            current.setNext(studentNode);
            System.out.println("序号为"+studentNode.getNo()+"的数据添加成功!");
        }else{
            System.out.println("序号为"+studentNode.getNo()+"的数据已存在,添加失败!");
        }

    }

    //遍历单链表
    public void list(){
        if(isEmpty(head)){
            System.out.println("该单链表为空");
            return;
        }
        StudentNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
            System.out.println(current);
        }
    }


    public void updateStudentNodeByNo(StudentNode node){
        if(head.getNext() == null){
            System.out.println("链表为空");
            return;
        }

        StudentNode current = head.getNext();

        boolean flag = false;
        while (true){
            if(current==null){
                break;
            }

            if(current.getNo() == node.getNo()){
                flag = true;
                break;
            }
            current = current.getNext();
        }

        if(flag) {
            current.setName(node.getName());
            current.setGender(node.getGender());
            System.out.printf("序号为%d的节点,修改成功!\n", node.getNo());
        }else {
            System.out.printf("无法找到序号为%d的节点,修改失败!\n", node.getNo());
        }
    }



    //根据no修改信息
    public void updateStudentByNo(StudentNode node){
        //头节点
        StudentNode current = head;


        boolean flag = false;
        while(true){
            if(current.getNext()==null){
                break;
            }

            if(current.getNext().getNo() == node.getNo()){
                flag = true;
                break;
            }

            current = current.getNext();
        }

        if(flag){
            if(current.getNext().getNext()!=null){
                node.setNext(current.getNext().getNext());
            }
            current.setNext(node);
            System.out.println("编号为"+node.getNo()+"的数据修改信息成功");
        }else{
            System.out.println("编号为"+node.getNo()+"的数据修改信息失败");
        }
    }


    //删除节点的时候需要找到待删除节点的前一个节点
    public void deleteStudentNodeByNo(int no){
        if(head.getNext()==null){
            System.out.println("当前链表为空,无法删除数据!");
            return;
        }

        StudentNode current = head;

        boolean flag = false;
        while (true){
            if(current.getNext()==null){
                break;
            }

            //找到待删除节点的前一个节点
            if(current.getNext().getNo() == no){
                flag = true;
                break;
            }

            current = current.getNext();
        }

        if(flag) {
            System.out.printf("编号为%d的节点删除成功!\n",no);
            current.setNext(current.getNext().getNext());
        }else{
            System.out.printf("编号为%d的节点无法找到,删除失败!\n",no);
        }

    }



}

class StudentNode{
    private int no;
    private String name;
    private char gender;
    private StudentNode next;

    public StudentNode() {
    }

    public StudentNode(int no, String name, char gender, StudentNode next) {
        this.no = no;
        this.name = name;
        this.gender = gender;
        this.next = next;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public StudentNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "StudentNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", next=" + next +
                '}';
    }
}

6)单链表的相关面试题
① 获取单链表的长度

第一步:遍历当前单链表

第二步:定义变量,计算单链表中的数据个数,不算头节点

② 获取单链表中倒数索引上的节点

第一步:获取到当前单链表的数据总长度

第二步:根据数据总长度和倒数索引K:length-k

第三步:遍历单链表到length-k长度,即可得到倒数索引节点的数据

③ 反转单链表
  • 方法一:递归方法,反转单链表

    • 第一步:获取到当前的节点【node节点】,获取node节点中next指向的节点【current节点】
    • 第二步:如果current节点等于null
      • 设置head节点的next节点指向node节点,然后return
    • 第三步:如果current节点不等于null,将current节点作为实参,带入到自己这个函数中
    • 第四步:如果node节点为head节点的话:
      • current节点的next节点指向null
    • node节点不为head节点的话:
      • current.setNext(node);
  • 方法二:创建新的单链表:反转单链表

在这里插入图片描述

  • 第一步:创建一个新的单链表头节点new节点、获取旧单链表的头节点old节点,创建一个中间变量temp
  • 第二步:遍历旧的单链表:
    • 中间变量的值等于old节点next指向的节点 ,temp = old.getNext();
    • old节点的next节点指向temp节点的next节点:old.setNext(temp.getNext());
    • temp节点的next指向新链表的next节点:temp.setNext(new.getNext());
    • 新链表的next节点指向temp节点:new.setNext(temp);
④ 反遍历单链表
  • 方式一:递归遍历单链表
  • 方式二:使用栈的特性,反向遍历单链表

在这里插入图片描述

package com.data.leetcode.day03.LinkedList;

import java.util.Stack;

/**
 *
 * 含头节点的:
 *      获取单链表的长度
 *      获取倒数索引上的节点
 *      反转单链表
 *      反遍历单链表
 * @author imppm
 * @create 2021-03-02-16:16
 */
public class LinkedListDemo1 {
    public static void main(String[] args) {
        LinkedListNode list = new LinkedListNode();

        ListNode listNode1 = new ListNode(1);
        ListNode listNode2 = new ListNode(2);
        ListNode listNode3 = new ListNode(3);
        ListNode listNode4 = new ListNode(4);
        ListNode listNode5 = new ListNode(5);
        ListNode listNode6 = new ListNode(6);

        list.addNode(listNode1);
        list.addNode(listNode2);
        list.addNode(listNode3);
        list.addNode(listNode4);
        list.addNode(listNode5);
        list.addNode(listNode6);

        System.out.println(list.LinkedListLength());

//        System.out.println(list.bottomIndex(1));
//        System.out.println(list.bottomIndex(2));
//        System.out.println(list.bottomIndex(3));
//        System.out.println(list.bottomIndex(4));
//        System.out.println(list.bottomIndex(5));
//        System.out.println(list.bottomIndex(6));
        System.out.println(list.lastIndex(1));
        System.out.println(list.lastIndex(2));
        System.out.println(list.lastIndex(3));
        System.out.println(list.lastIndex(4));
        System.out.println(list.lastIndex(5));
        System.out.println(list.lastIndex(6));
        System.out.println("----------------------------------");

//        list.reverseListNode(LinkedListNode.head);

        //单链表反转
        list.reverseList();

        //遍历单链表
        list.list();
        System.out.println("----------------------------------");

        list.reverseListPrint();

        //反向遍历单链表
//        list.reverseList(LinkedListNode.head);
    }
}

class LinkedListNode{
    public static final ListNode head = new ListNode();

    public void addNode(ListNode listNode){
        ListNode current = head;

        while (current.getNext() != null) {
            current = current.getNext();
        }
        current.setNext(listNode);
    }

    public int LinkedListLength(){
        ListNode current = head;
        int count = 0;
        while (current.getNext()!=null){
            count++;
            current = current.getNext();
        }
        return count;
    }


    //链表的倒数第几个数据【简单方法】
    //倒数第K个,遍历到总长度 - K 位置
    public ListNode lastIndex(int k){
        //获取链表的头节点
        ListNode current = head;

        //获取当前链表的长度
        int length = LinkedListLength();

        //单链表为空
        if(current.getNext() == null){
            return null;
        }

        //查找索引不合理
        if(k <= 0 || k > length){
            return null;
        }

        //需要遍历到:length - k
        //查找单链表的倒数第3个:6 - 3;

        // 0,1,2,3
        //倒数第六个,对应的第一个,不进行循环
        current = current.getNext();
        for (int i = 0; i < length - k; i++){
            current = current.getNext();
        }

        //返回查找到的节点
        return current;
    }


    //倒数第几个数据【麻烦方法】
    public ListNode bottomIndex(int k){
        ListNode current = head;
        int length = LinkedListLength();
        boolean flag = false;
        int index = 0;
        while(true){
            if(current.getNext() == null){
                flag = true;
                break;
            }
            index++;
            current = current.getNext();
            if(index == length - k + 1 && length >= k){
                break;
            }

        }
        if(flag){
            System.out.printf("倒数第%d个节点不存在!\n",k);
            return null;
        }
        return current;
    }

    //反转单链表方式一
    public void reverseListNode(ListNode node){
        //1. 获取当前节点的next指向的节点
        ListNode current = node.getNext();

        //2. 如果当前节点next指向的节点为null,表示当前节点为最后一个节点
        //递归到了链表的末尾
        if(current == null){

            //3. 单链表的头节点指向这个末尾节点
            head.setNext(node);
            return;
        }

        //4. 递归当前节点
        reverseListNode(current);

        //5. 如果当前节点不是头节点,当前节点next指向的节点指向当前节点【反转操作】
        if(node != head) {
            current.setNext(node);
        }else {
            //6. 如果当前节点为头节点,当前节点next指向的节点为null【收尾操作】
            current.setNext(null);
        }

    }


    //反转单链表方式二
    //1. 当前单链表一次减少节点
    //2. 另外一个新的单链表,依次增加旧链表减小的节点
    //      新单链表增加节点的位置:一直是在单链表的开头的
    public void reverseList(){
        ListNode current = head;
        ListNode newList = new ListNode();
        ListNode temp = null;
        if(current.getNext()==null || current.getNext().getNext()==null){
            return;
        }

        while (true){
            if(current.getNext() == null){
                break;
            }

            //旧链表删除节点
            //旧链表依次向后删除节点
            //1. 获取当前节点
            temp = current.getNext();
            //2. 头节点指向当前节点的下一个节点
            current.setNext(temp.getNext());

            //新链表增加节点
            //新链表增加节点,一直是最开始位置
            //3. 新增加的节点指向新链表的next指向的节点
            temp.setNext(newList.getNext());
            //4. 新链表的next指向添加的节点
            newList.setNext(temp);
        }

        //旧链表的next指向新链表的next
        head.setNext(newList.getNext());
    }

    //遍历单链表
    public void list(){
        ListNode current = head;
        while (current.getNext()!=null){
            current = current.getNext();
            System.out.println(current);
        }
    }

    //反向遍历单链表:方法一:递归方法
    public void reverseList(ListNode node){
        //当前节点next指向的节点
        ListNode current = node.getNext();

        //当前节点next指向的节点为null,表示当前节点为最后一个节点
        if(current == null){
            return;
        }

        //递归:当前节点
        reverseList(current);

        //输出当前节点next指向节点对应的值
        System.out.println(current.getVal());
    }


    //反向遍历单链表:方法二:栈方法
    public void reverseListPrint(){
        ListNode current = head;

        if(current.getNext()==null){
            return;
        }

        //创建一个栈
        Stack<ListNode> list = new Stack<>();
        while (true){
            if(current.getNext()==null){
                break;
            }
            //获取单链表节点
            current = current.getNext();
            //将单链表的数据放到栈中进行存储
            list.add(current);
        }

        //遍历栈
        while (list.size()>0){
            //输出栈中的数据
            System.out.println(list.pop().getVal());
        }
    }
}

class ListNode{
    private int val;
    private ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }

    public ListNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "ListNode{" +
                "val=" + val +
                ", next=" + next +
                '}';
    }
}

⑤ 合并两个链表
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode current_l1 = l1;
        ListNode current_l2 = l2;
        ListNode head = new ListNode();
        ListNode current = head;
        while(current_l1!=null && current_l2!=null){
            //一个一个走,不用两个一起走
            if(current_l1.val >= current_l2.val){
                current.next = current_l2;
                current_l2 = current_l2.next;
                current = current.next;
            }else{
                current.next = current_l1;
                current_l1 = current_l1.next;
                current = current.next;
            }
        }
        //如果current_l1有剩余:直接补到新的单链表上
        if(current_l1!=null){
            current.next = current_l1;
        }
        //如果current_l1有剩余,直接补到新的单链表上
        else if(current_l2 != null){
            current.next = current_l2;
        }
        return head.next;
    }

2、双向链表

1)双向链表的增删改查

在这里插入图片描述

① 双向链表中增加数据
  • 第一步:遍历双向链表【与单链表遍历相同】
  • 第二步:遍历到双向链表的末尾,添加数据
  • 第三步:需要添加的节点node,current为双向链表的末尾
    • current.next = node;
    • node.pre = current;
② 双向链表中删除数据
  • 第一步:遍历双向链表【与单链表遍历相同】
  • 第二步:遍历到双向链表的该数据位置,current
  • 第三步:删除的操作
    • current.pre.next = current.next;
    • current.next.pre = current.pre;
③ 修改双向链表中的数据【与单链表相同】
④ 查找双向链表中的数据【与单链表相同】
⑤ 通过id值,向双向链表中添加数据
package com.data.structure.twoWayLinkedList;

/**
 * @author imppm
 * @create 2021-03-04-10:47
 */
public class TwoWayLinkedList {
    public static void main(String[] args) {
        LinkedListNode list = new LinkedListNode();

        Node node1 = new Node(1,4,null,null);
        Node node2 = new Node(2,7,null,null);
        Node node3 = new Node(3,11,null,null);
        Node node4 = new Node(4,5,null,null);

        //添加数据
        list.addNode(node1);
        list.addNode(node2);
        list.addNode(node3);
        list.addNode(node4);

        //遍历双向链表
        list.list();

        //删除数据
        list.deleteNodeByNo(node3);

        //遍历双向链表
        list.list();

        Node node5 = new Node(4,1116,null,null);
        //修改双向链表
        list.updateNodeByNo(node5);

        list.list();

        Node node10 = new Node(10,10,null, null);
        Node node8 = new Node(8,8,null, null);

        list.addNodeByNo(node10);
        list.addNodeByNo(node8);
        list.addNodeByNo(node8);

        list.list();
    }
}

class LinkedListNode{
    //创建双向链表的头节点
    private final Node head = new Node();

    //判断当前双向链表是否为空
    private boolean isEmpty(){
        return head.getNext() == null;
    }

    public Node getLinkedListHead(){
        return head;
    }

    //在双向链表中添加数据【在单链表的末尾添加数据】
    public void addNode(Node node){
        Node current = head;

        while (current.getNext() != null) {
            current = current.getNext();
        }

        current.setNext(node);
        node.setPre(current);
        System.out.printf("no为%d的节点数据增加成功!\n",node.getNo());
    }


    public void addNodeByNo(Node node){
        Node current = head;

        boolean flag = false;
        while (true){
            if(current.getNext()==null){
                break;
            }

            current = current.getNext();

            if(current.getNo() == node.getNo()){
                flag = true;
                break;
            }

            //如果双向链表的no值大于node的no值,插入到搞数据的前面
            if(current.getNo() > node.getNo()){
                break;
            }
        }

        if(flag){
            System.out.printf("no为%d的节点数据添加失败!\n",node.getNo());
        }else{
            //将新结点的pre指向当前节点的前一个节点
            node.setPre(current.getPre());
            //当前节点的前一个节点的next指向新的节点
            current.getPre().setNext(node);
            //新的节点的next指向当前节点
            node.setNext(current);
            System.out.printf("no为%d的节点数据增加成功!\n",node.getNo());

        }


    }


    //在双向链表中删除数据
    public void deleteNodeByNo(Node node){
        Node current = head;

        boolean flag = false;

        while (true){
            //末尾数据
            if(current.getNext()==null){
                break;
            }

            current = current.getNext();

            if(current.getNo() == node.getNo()){
                flag = true;
                break;
            }
        }

        if(flag){
            if(current.getNext()!=null){
                current.getPre().setNext(current.getNext());
                current.getNext().setPre(current.getPre());
            }else{
                current.getPre().setNext(null);
            }
            System.out.printf("no为%d的节点数据删除成功!\n",node.getNo());
        }else{
            System.out.printf("no为%d的节点没有找到,数据删除失败!\n",node.getNo());
        }
    }

    public void updateNodeByNo(Node node){
        Node current = head;

        boolean flag = false;

        while(true){
            if(current.getNext()==null){
                break;
            }

            current = current.getNext();

            if(current.getNo() == node.getNo()){
                current.setVal(node.getVal());
                flag = true;
                break;
            }
        }

        if(flag){
            System.out.printf("no为%d的节点数据修改成功!\n",node.getNo());
        }else{
            System.out.printf("no为%d的节点没有找到,数据修改失败!\n",node.getNo());

        }
    }

    public Node queryNodeByNo(int no){
        Node current = head;

        while (current.getNext() != null) {

            current = current.getNext();

            if (current.getNo() == no) {
                System.out.println("no为" + no + "的节点数据为" + current + "!");
                return current;
            }
        }
        return null;
    }

    public void list(){
        Node current = head;
        while (current.getNext()!=null){
            current = current.getNext();
            System.out.println(current);
        }
    }
}

class Node{
    private int no;
    private int val;
    private Node pre;
    private Node next;

    public Node() {
    }

    public Node(int val, Node pre, Node next) {
        this.val = val;
        this.pre = pre;
        this.next = next;
    }

    public Node(int no, int val, Node pre, Node next) {
        this.no = no;
        this.val = val;
        this.pre = pre;
        this.next = next;
    }

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }

    public Node getPre() {
        return pre;
    }

    public void setPre(Node pre) {
        this.pre = pre;
    }

    public Node getNext() {
        return next;
    }

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


    public int getNo() {
        return no;
    }

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

    @Override
    public String toString() {
        return "Node{" +
                "val=" + val +
                '}';
    }
}

3、单向环形链表应用:约瑟夫环问题

1)约瑟夫环:
① 约瑟夫环问题分析

在这里插入图片描述

② 创建约瑟夫环、遍历约瑟夫环分析

在这里插入图片描述

③ 约瑟夫环中:出圈操作分析

在这里插入图片描述

④ 解决方法一【较为复杂,出圈问题中,并非真正对应的玩家进行出圈】:
package com.data.leetcode.day03.LinkedList;

import java.util.Scanner;

/**
 * @author imppm
 * @create 2021-03-05-19:34
 */
public class JosephRing {
    public static void main(String[] args) {
        Node head = new Node(1,null);
        JosephRingNode list = new JosephRingNode();

        int num;
        int no;
        int digit;
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入围圈的人数:");
        num = scanner.nextInt();
        Node node;
        for (int i = 0; i < num-1; i++){
            node = new Node((i+2),null);
            list.addNode(head, node);
        }


        list.list(head);
        System.out.print("请输入约定的编号:");
        no = scanner.nextInt();
        System.out.print("请输入报的数字:");
        digit = scanner.nextInt();

        //制作约瑟夫环
        list.makeJosephRing(head);


        list.count(head, num, no, digit);
    }
}

class JosephRingNode{

    public void count(Node node, int num, int no, int digit){
        Node current = listIndexNode(node, no);

        int index = 0;

        int i = 1;

        while(true){
            //当前链表为空,结束循环
            if(num == 1){
                System.out.println("第"+i+"次输出的数据依次是:"+current.getNo());
                break;
            }

            index++;

            if(index == digit){
                System.out.println("第"+i+"次输出的数据依次是:"+current.getNo());
                current.setNo(current.getNext().getNo());
                current.setNext(current.getNext().getNext());
                index = 0;
                i++;
                num--;
                continue;
            }

            current = current.getNext();
        }
    }

    private Node listIndexNode(Node node, int index){
        Node current = node;

        for (int i = 1; i < index; i++){
            current = current.getNext();
        }
        return current;
    }


    private boolean isEmpty(Node node){
        return node.getNext()==null;
    }

    public void addNode(Node list, Node node){
        Node current = list;

        while(current.getNext()!=null){
            current = current.getNext();
        }
        current.setNext(node);
        //node.setNext(list);
    }

    public void list(Node node){
        Node current = node;

        while(current!=null){
            System.out.println(current);
            current = current.getNext();
        }
    }

    public void makeJosephRing(Node node){
        Node current = node;

        while (current.getNext()!=null){
            current = current.getNext();
        }

        current.setNext(node);
    }
}

class Node{
    private int no;
    private Node next;

    public Node() {
    }

    public Node(int no, Node next) {
        this.no = no;
        this.next = next;
    }

    public int getNo() {
        return no;
    }

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

    public Node getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "Node{" +
                "no=" + no +
                ", next=" + next +
                '}';
    }
}


⑤ 解决方法二:【较为简单,出圈问题中,为真正对应玩家的出圈】
package com.data.leetcode.day03.LinkedList;

import java.io.FileReader;

/**
 * @author imppm
 * @create 2021-03-05-20:40
 */
public class JosephRing2 {
    public static void main(String[] args) {
        LinkedListNode1 list = new LinkedListNode1();

        //创建约瑟夫环
        list.createJosephCircle(5);

        System.out.println("遍历当前约瑟夫环:");
        list.list();

        System.out.println("---------------");
        System.out.println("出圈的顺序:");
//        list.count(10, 3,4);
        list.sayCount(5,1,2);
    }
}

class LinkedListNode1{
    private Node1 head = null;


    //直接创建一个num人数的约瑟夫环【环形单链表】
    public void createJosephCircle(int nums){
        //如果nums小于1,这样的约瑟环不存在
        if(nums < 1){
            System.out.println("创建的约瑟夫环有问题!");
        }

        //作为变量,用来记录当前环形单链表的最后一个节点
        Node1 curBoy = null;

        //创建约瑟夫环
        for (int i = 1; i <= nums; i++){
            Node1 node1 = new Node1(i, null);

            //创建第一个节点的时候,需要单独处理
            if(i == 1){
                head = node1;
                head.setNext(head);
                curBoy = head;
            }
            //其他节点,统一处理
            else{
                node1.setNext(head);
                curBoy.setNext(node1);
                curBoy = node1;
            }
        }
    }


    //每次添加一个数据到环形单链表中
    //添加节点,构建成一个单向环形链表
    public void addNode(Node1 node){
        Node1 current = head;

        //如果当前单链表没有数据,创建单链表的头节点
        if(head == null){
            head = node;
            head.setNext(head);
            return;
        }

        //结束标志
        //判断当前链表的下一个节点是不是head节点
        while(current.getNext()!=head){
            current = current.getNext();
        }

        current.setNext(node);
        node.setNext(head);
    }

    //遍历当前环形链表
    public void list(){
        Node1 current = head;

        if(current == null){
            System.out.println("链表为空");
            return;
        }

        while (current!=null){
            System.out.println(current);
            current = current.getNext();
            if(current==head){
                break;
            }
        }
    }

    //出圈函数:方式1
    //nums:表示总共有多少个人
    //start:表示开始数数的位置
    //digit:表示数数停止的数字
    public void count(int nums, int start, int digit){
        //开始数数的小孩的前一个节点
        Node1 helper = indexPreNode(start);
        //开始数数的小孩节点
        Node1 current = helper.getNext();

        //当前的报数值
        int index = 0;
        //当前圈中剩余的人数
        int num = nums;
        while (true){
            //if(first==helper)
            if(num == 1){
                //圈中还剩下一个人
                System.out.println(current);
                break;
            }

            //报数+1
            index++;

            //当报数的值等于设定的值
            if(index == digit){
                //设置报数值为0
                index = 0;
                //当前报数的人出圈
                System.out.println(current);
                //修改helper的next值
                helper.setNext(current.getNext());
                //报数的人出圈,current指向下一个人
                current = current.getNext();
                //圈中的人数-1
                num--;
                continue;
            }


            current = current.getNext();
            helper = helper.getNext();
        }

    }

    //出圈函数:方式二
    //nums:总人数
    //start:开始报数位置
    //digit:报数截至数
    public void sayCount(int nums, int start, int digit){
        //如果头节点为null,数据为空,开始节点数据不合理,开始位置大于数据个数
        if(head == null || start < 1 || start > nums){
            System.out.println("参数输入有误,请重新输入:");
            return;
        }
        //赋值指针
        Node1 helper = head;
        Node1 current = head;


        while (true){
            //是否到达最后一个孩子
            if(helper.getNext() == head){
                break;
            }
            helper = helper.getNext();
        }

        //报数前,需要让helper和current移动start-1次
        for (int i = 0; i < start - 1; i++){
            current = current.getNext();
            helper = helper.getNext();
        }

        //current指向的节点,就是第一个报数的节点
        //helper指向的节点,第一个报数的节点的前一个节点

        //开始报数,每次需要移动digit-1次,然后出圈
        while (true){
            //圈中只剩下一个节点
            if(current == helper){
                break;
            }

            //current节点和helper节点都向后移动
            for (int i = 0; i < digit -1; i++){
                current = current.getNext();
                helper = helper.getNext();
            }

            //current指向的节点,就是要出圈的节点
            System.out.println(current);
            current = current.getNext();
            helper.setNext(current);
        }
        System.out.println(current);
    }

    //获取最后一个位置节点
    public Node1 lastNode(){
        Node1 current = head;
        while(current!=null){
            current = current.getNext();
            if(current.getNext()==head){
                break;
            }
        }
        return current;
    }

    //获取到目标位置的前一个节点
    public Node1 indexPreNode(int index){
        Node1 current = head;

        if(index == 1){
            return lastNode();
        }

        for(int i = 2; i < index; i++){
            current = current.getNext();
        }

        return current;
    }
}

class Node1{
    private int no;
    private Node1 next;

    public Node1() {
    }

    public Node1(int no, Node1 next) {
        this.no = no;
        this.next = next;
    }

    public int getNo() {
        return no;
    }

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

    public Node1 getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "Node1{" +
                "no=" + no +
                '}';
    }
}


希望本篇文章对大家有所帮助,后续会继续分享java数据结构与算法相关学习知识…

如果文章内容有错误的地方,请在留言处留下你的见解,方便大家共同学习。谢谢!

如有侵权或其他任何问题请联系:QQ1370922071,本文主要用于学习交流,转载请声明!

作者:皮皮猫吖


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值