Java链表(单链表、双向链表、单向环形链表)

链表

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

单链表创建和遍历代码实现

package dataStructure.LinkedList;

/**
 * @author 
 * @date 2024-04-12 14:52
 */

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        HeroNode hero1 = new HeroNode(1,"宋江1","及时雨1");
        HeroNode hero2 = new HeroNode(1,"宋江2","及时雨2");
        HeroNode hero3 = new HeroNode(1,"宋江3","及时雨3");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        singleLinkedList.list();
    }
}
class SingleLinkedList {
    //初始化头节点,头节点不存放数据
    private HeroNode head = new HeroNode(0,"","");
    //添加节点到单向链表
    //1.找到当前链表的最后一个节点
    //2.将最后这个节点的next指向新的节点
    public void add(HeroNode heroNode) {
        //因为head节点不能动,因此我们需要一个辅助遍历temp
        HeroNode temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //当退出while循环时,temp就指向了链表的最后
        //将最后这个节点的next指向新的节点
        temp.next = heroNode;
    }
    //显示链表
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动,需要辅助变量
        HeroNode temp = head.next;
        while (true) {
            //判断是否到链表最后
            if (temp == null ){
                break;
            }
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }
}
//定义HeroNode,每个HeroNode对象都是一个节点
class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode next;   //指向下一个节点

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

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

单链表代码实现(按编号顺序添加)

在这里插入图片描述

    public void addByOrder(HeroNode heroNode) {
        //因为单链表,temp是位于添加位置的前一个节点
        HeroNode temp = head;
        boolean flag = false;   //flag标志添加的编号是否存在,默认为false
        while (true) {
            if (temp.next == null ){
                //已经到链表的最后
                break;
            }
            if (temp.next.no > heroNode.no) {
                //在temp的d当前位置添加
                break;
            } else if (temp.next.no == heroNode.no) {
                //编号已存在
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            System.out.printf("准备插入的英雄编号 %d 已经存在",heroNode.no);
        } else {
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

//测试在psvm中 
HeroNode hero4 = new HeroNode(4,"宋江4","及时雨4");
HeroNode hero5 = new HeroNode(5,"宋江5","及时雨5");
HeroNode hero6 = new HeroNode(6,"宋江6","及时雨6");
SingleLinkedList singleLinkedList1 = new SingleLinkedList();
singleLinkedList1.addByOrder(hero4);
singleLinkedList1.addByOrder(hero6);
singleLinkedList1.addByOrder(hero5);
singleLinkedList1.list();

单链表节点修改的代码实现

//修改节点的信息,根据no编号来修改,no编号不能修改
    public void update(HeroNode newHeroNode) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        while(true) {
            if (temp == null) {
                System.out.printf("没有找到编号%d的节点,不能修改\n",newHeroNode.no);
                break;
            }
            if (newHeroNode.no == temp.no) {
                temp.name = newHeroNode.name;
                temp.nickname = newHeroNode.nickname;
                System.out.println("修改成功");
                break;
            }
            temp = temp.next;
        }
    }

单链表删除节点

在这里插入图片描述

public void delete(int no ) {
        HeroNode temp = head;
        while(true) {
            if (temp == null) {
                System.out.printf("没有找到编号%d的节点,不能删除\n",no);
                break;
            }
//            System.out.println("temp.next.no is " + temp.next.no);
            if (temp.next.no == no) {
                temp.next = temp.next.next;
                System.out.println("删除成功");
                break;
            }
            temp = temp.next;
        }
    }

题目

  1. 求单链表中的有效节点个数

    /* 
        * 
        * @author  
        * @date 2024/4/15 16:37
         * @param head 链表的头节点
         * @return int 有效节点个数
        */
        public static int getLength(HeroNode head) {
            if (head.next == null ) {
                return 0;
            }
            int length = 0;
            HeroNode cur = head.next;
            while (cur != null) {
                length ++;
                cur = cur.next;
            }
            return length;
        }
    
  2. 查找单链表中的倒数第k个节点

    public static HeroNode findLatIndexNode(HeroNode head, int index){
            if (head.next == null) {
                return null;
            }
            //获取链表长度
            int size = getLength(head);
            if (index <= 0 || index > size) {
                return null;
            }
            HeroNode cur = head.next;
            for (int i = 0; i < (size - index); i++) {
                cur = cur.next;
            }
            return cur;
        }
    
  3. 单链表的反转

    在这里插入图片描述

    public static void reverseList(HeroNode head){
            if (head.next == null || head.next.next == null){
                return ;
            }
            HeroNode cur = head.next;
            HeroNode next = null;   //指向当前节点[cur]的下一个节点
            HeroNode reverseHead = new HeroNode(0,"","");
            //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
            while (cur != null) {
                next = cur.next;    //先暂时保存当前节点的下一个节点
                cur.next = reverseHead.next;    //cur中next用于存放reverseHead中next值
                reverseHead.next = cur;    //将cur连到reverseHead后的第一个位置
                cur = next; //让cur后移
            }
            //将head.next指向reverseHead.next,实现单链表的反转
            head.next = reverseHead.next;
        }
    
  4. 从尾到头打印单链表

    在这里插入图片描述

        //用栈
        public static void reversePrint(HeroNode head) {
            if (head.next == null) {
                return ;
            }
            //创建一个栈,将各个节点压入栈中
            Stack<HeroNode> stack = new Stack<>();
            HeroNode cur = head.next;
            while (cur != null) {
                stack.push(cur);
                cur = cur.next;
            }
            while (stack.size()>0) {
                System.out.println(stack.pop());
            }
        }
    
  5. 合并两个有序的单链表,合并之后的链表依然有序

    public static HeroNode mergeDoubleList(HeroNode head1, HeroNode head2) {
            //TODO 如果其中一个链表为空,则直接返回另一个链表
            if (head1 == null) {
                return head2;
            }
            if (head2 == null) {
                return head1;
            }
            HeroNode mergeHead = new HeroNode(0,"","");
            HeroNode cur1 = head1.next;
            HeroNode cur2 = head2.next;
            HeroNode cur = mergeHead;
            while (cur1 != null || cur2 != null) {
                if (cur1 != null && cur2 != null) {
                    if (cur1.no <= cur2.no) {
                        cur.next = cur1;
                        cur1 = cur1.next;
                    } else {
                        cur.next = cur2;
                        cur2 = cur2.next;
                    }
                } else if (cur1 == null && cur2 != null) {
                    cur.next = cur2;
                    cur2 = cur2.next;
                } else if (cur2 == null && cur1 != null) {
                    cur.next = cur1;
                    cur1 = cur1.next;
                }
    //            cur.next.next = null;
                cur = cur.next;
            }
            return mergeHead;
        }
    
    
    //测试
            HeroNode mergedHead = mergeDoubleList(
                    list1.getHead(),list2.getHead()
            );
            SingleLinkedList list3 = new SingleLinkedList();
            list3.setHead(
                    mergedHead
            );
            list3.list();
    

双向链表

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

在这里插入图片描述

package dataStructure.LinkedList;

/**
 * @author
 * @date 2024-04-16 20:51
 */

public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        System.out.println("This is test");
        HeroNode2 hero1 = new HeroNode2(1,"宋江","及时雨");
        HeroNode2 hero2= new HeroNode2(2,"卢俊义","玉麒麟");
        HeroNode2 hero3 = new HeroNode2(3,"吴用","智多星");
        HeroNode2 hero4 = new HeroNode2(4,"林冲","豹子头");
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);
        doubleLinkedList.list();

        System.out.println("This is update");
        HeroNode2 hero5 = new HeroNode2(4,"公孙胜","入云龙");
        doubleLinkedList.update(hero5);
        doubleLinkedList.list();

        System.out.println("This is delete");
        doubleLinkedList.delete(3);
        doubleLinkedList.list();

        System.out.println("This is addByOrder");
        doubleLinkedList.addByOrder(hero3);
        doubleLinkedList.list();
    }
}
class DoubleLinkedList {
    private HeroNode2 head = new HeroNode2(0,"","");

    public HeroNode2 getHead() {
        return head;
    }

    public void setHead(HeroNode2 head) {
        this.head = head;
    }
    //添加到双向链表末尾
    public void add(HeroNode2 heroNode) {
        //因为head节点不能动,因此我们需要一个辅助遍历temp
        HeroNode2 temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //当退出while循环时,temp就指向了链表的最后
        //将最后这个节点的next指向新的节点
        temp.next = heroNode;
        heroNode.prev = temp;
    }
    public void addByOrder(HeroNode2 heroNode) {
        //因为单链表,temp是位于添加位置的前一个节点
        HeroNode2 temp = head;
        boolean flag = false;   //flag标志添加的编号是否存在,默认为false
        while (true) {
            if (temp.next == null ){
                //已经到链表的最后
                break;
            }
            if (temp.next.no > heroNode.no) {
                //在temp的d当前位置添加
                break;
            } else if (temp.next.no == heroNode.no) {
                //编号已存在
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            System.out.printf("准备插入的英雄编号 %d 已经存在",heroNode.no);
        } else {
            temp.next = heroNode;
            heroNode.prev = temp;
        }
    }
    //修改节点的信息,根据no编号来修改,no编号不能修改
    public void update(HeroNode2 newHeroNode) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode2 temp = head.next;
        while(true) {
            if (temp == null) {
                System.out.printf("没有找到编号%d的节点,不能修改\n",newHeroNode.no);
                break;
            }
            if (newHeroNode.no == temp.no) {
                temp.name = newHeroNode.name;
                temp.nickname = newHeroNode.nickname;
                System.out.println("修改成功");
                break;
            }
            temp = temp.next;
        }
    }
    public void delete(int no ) {
        HeroNode2 temp = head.next;
        while(true) {
            if (temp == null) {
                System.out.printf("没有找到编号%d的节点,不能删除\n",no);
                break;
            }
            if (temp.no == no) {
                temp.prev.next = temp.next;
                //如果是最后一个节点,就不需要执行下面
                if (temp.next!=null) {
                    temp.next.prev = temp.prev;
                }
                System.out.println("删除成功");
                break;
            }
            temp = temp.next;
        }
    }
    //遍历双向链表
    //toString方法不能prev和next
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动,需要辅助变量
        HeroNode2 temp = head.next;
        while (true) {
            //判断是否到链表最后
            if (temp == null ){
                break;
            }
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }
}
class HeroNode2 {
    public int no;
    public String name;
    public String nickname;
    public HeroNode2 prev;
    public HeroNode2 next;

    public HeroNode2(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

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

单向环形链表

Josephu问题

在这里插入图片描述

在这里插入图片描述

(helper最开始指向5,first指向1)

package dataStructure.LinkedList;

public class Josephu {
    public static void main(String[] args) {
        CircleSingleLinkedList list = new CircleSingleLinkedList();
        list.addBoy(5);
        list.showBoy();

        System.out.println("This is countBoy");
        list.countBoy(1,2,5);   //2->4->1->5->3
    }
}
//创建一个环形的单向链表
class CircleSingleLinkedList {
    //创建一个first节点,当前没有编号
    private Boy first = new Boy(-1);
    //添加小孩节点
    public void addBoy(int nums) {
        // nums数据校验
        if (nums < 1) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null;  //辅助指针
        //使用for循环创建环形链表
        for (int i = 1; i <= nums ; i++) {
            //根据编号创建节点
            Boy boy = new Boy(i); //将要添加的boy
            //如果是第一个小孩
            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();  //后移
        }
    }
    //根据用户的输入,计算出圈顺序
    /*
    *
    * @author 
    * @date 2024/4/18 22:25
     * @param startNo   第几个开始(先移动startNo-1次)
     * @param countNum  数几下
     * @param nums  表示最初有多少人
    */
    public void  countBoy(int startNo, int countNum, int nums){
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("参数输入有误,请重新输入");
            return;
        }
        Boy helper = first;
        //创建辅助指针helper,指向环形链表的最后一个节点
        while (true) {
            if (helper.getNext() == first) {
                //当前helper已经指向最后一个小孩了
                break;
            }
            helper = helper.getNext();
        }
        //小孩报数前,先让first和helper移动k-1次
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数时,让first和helper指针同时的移动 m - 1次,然后出圈
        //循环直到圈中只有一个节点
        while (true) {
            if (helper == first) {
                break;
            }
            //让first和helper指针同时的移动 m - 1次,然后出圈
            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());
    }
}
//表示一个节点
class Boy {
    private int no; //编号
    private Boy next;   //指向下一个节点,默认null

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值