java 实现单链表、双链表增删改查、排序、约瑟夫等功能实现

总结:

1、链表中每个结点的next引用都相当于一个指针指向另一个结点
2、在单链表中通常使用head引用指向单链表的首结点,由head引用完成对整个链表中所有结点的访问
3、单链表的一个重要特性就是只能通过前驱结点找到后续结点,不能从后续结点找到前驱结点


单链表的增、删、改、查实现:

// 根据条件排序添加
在这里插入图片描述

/**
*  单链表的实现
*/
public class SingleLinkdListDemo {
    public static void main(String[] args) {
        SingleLinkdList singleLinkdList = new SingleLinkdList();
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//        singleLinkdList.add(hero1);
//        singleLinkdList.add(hero2);
//        singleLinkdList.add(hero3);
//        singleLinkdList.add(hero4);
        singleLinkdList.add2(hero1);
        singleLinkdList.add2(hero4);
        singleLinkdList.add2(hero2);
        singleLinkdList.add2(hero3);
        // 修改
        //singleLinkdList.upDataNode(new HeroNode(4, "吴用1111", "智多星1111"));
        // 删除
        //singleLinkdList.deleteNode(5);
        singleLinkdList.showLinkList(singleLinkdList.head);
        singleLinkdList.size();
        singleLinkdList.reversetLog(singleLinkdList.head);
        // 经典面试
        //1、查询链表有效个数
       // singleLinkdList.getLength();
        //2、查询链表的倒数第几个
        //singleLinkdList.getEndIndex(2);
        //3、反转链表
        // singleLinkdList.invertsLink(singleLinkdList.head);
        //singleLinkdList.showLinkList(singleLinkdList.head);
    }


}

/**
* 单链表
*/
class SingleLinkdList{
    // 头节点
    public HeroNode head = new HeroNode(1,"","");
    /**
     * 添加数据 1 无序添加(直接加在链表的尾部)
     * 1. 找到当前链表的最后节点
     * 2. 将最后这个节点的 next 指向 新的节点
     */
    public void add(HeroNode node){
        if(node == null) {
            System.out.println("传入数据为空~~~");
            return;
        }
        // 头节点不能变,所以备份变量
        HeroNode data = head;
        while (true) {
            // 找到最后一个元素,next为null,赋值即可
            if(data.next == null) {
                data.next = node;
                break;
            }
            data = data.next;
        }
    }

    /**
     * 添加数据 2 (有序添加,根据no变量值插入)
     * 1、找到前节点,然后指向新节点,然后新节点指向后节点
     * 2、如果排行相等 就不加入
     */
    public void add2(HeroNode node){
        if(node == null) {
            System.out.println("传入数据为空~~~");
            return;
        }
        // 因为单链表,因为我们找的temp 是位于 添加位置的前一个节点,否则插入不了
        HeroNode data = head;
        // flag标志添加的编号是否存在,默认为false
        boolean isFlag = false;
        while (true){
            if(data.next == null) {
                break;
            }
            // 判断下一个节点的排名大于当前的排名
            if(data.next.no > node.no) {
                break;
            }else if(data.next.no == node.no) {
                isFlag = true;
                break;
            }
            data = data.next;
        }
        if(isFlag) {
            System.out.printf("准备插入的英雄的编号 %d 已经存在了, 不能加入\n", node.no);
        }else {
            // 新节点指向后一节点
            node.next = data.next;
            // 上一节点指向新节点
            data.next = node;
        }
    }

    /**
     * 修改节点的信息, 根据no编号来修改,即no编号不能改.
     * 修改某一个节点
     */
    public void upDataNode(HeroNode node){
        if(head.next == null) {
            System.out.println("链表为空~~~");
            return;
        }
        
        HeroNode data = head;
        while (true){
            if(data.next == null && data.no != node.no) {
                System.out.println("没有找到您要修改的数据");
                break;
            }
            if(data.next.no == node.no) {
                // 找到修改的数据
                data.next.setNickName(node.getNickName());
                data.next.setName(node.getName());
                break;
            }
            data = data.next;
        }
    }

    /**
     * 链表大小,没有头节点
     */
    public void size(){
        if(head.next == null) {
            System.out.println("链表为空~~~");
            return;
        }
        int num = 0;
        HeroNode data = head;
        while (true){
            if(data.next == null) {
                break;
            }
            num++;
            data = data.next;
        }
        System.out.println("链表size~~~" + num);
    }

    /**
     * 查询链表数据
     */
     public void showLinkList(HeroNode head){
         if(head.next == null) {
             System.out.println("链表为空~~~");
             return;
         }
         HeroNode data = head;
         while (true){
             if(data.next == null) {
                 break;
             }
             data = data.next;
             System.out.println(data);
         }
     }

    /**  
     * 删除链表数据
     * 1、找到前一个节点,然后指向删除节点的下一个节点
     * 2、通过no排名条件,删除节点
     */
    public void deleteNode(int no){
        if(head.next == null) {
            System.out.println("链表为空~~~");
            return;
        }
        HeroNode data = head;
        while (true){
            if(data.next == null && data.no != no) {
                System.out.println("没有找到删除的数据~~~");
                break;
            }
            // 找到对应排名的数据
            if(data.next.no == no) {
                System.out.println("删除成功~~~"  + data.next);
                // 前节点指向 删除节点的后一节点
                data.next = data.next.next;
                break;
            }
            data = data.next;
        }
    }

 
}
/**
* 链表节点
*/
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;
    }
    public HeroNode getNext() {
        return next;
    }
    public void setNext(HeroNode next) {
        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 String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

几个单链表相关的面试题:

1、求单链表中有效节点的个数
 /**
     * 求单链表中有效节点的个数
     */
    public int getLength(){
        if(head.next == null) {
            System.out.println("有效数量为0.....");
            return 0;
        }
        int length = 0;
        HeroNode data = head;
        while (data.next != null) {
            length++;
            data = data.next;
        }
        System.out.println("有效数量为....." + length);
        return length;
    }
2、查找链表的倒数第几个
   /**
     * 查找链表的倒数第几个
     */
    public void getEndIndex(int index){
        if(head.next == null) {
            System.out.println("没有找到");
            return;
        }
        if(index <= 0 || index > getLength()) {
            System.out.println("查找的位置异常。。。");
            return;
        }
        HeroNode data = head;
        // 有效个数 - 倒数第几个。  比如有效长度 4  倒数第2     0 1 2 3
        for(int i = 0; i <= getLength() - index; i++) {
            data = data.next;
        }
        System.out.println("查找的倒数第" + index + ":  " + data);
    }
3、反转链表
/**
 * 反转链表
 */
public void invertsLink(HeroNode head){
    if(head == null || head.next == null || head.next.next == null) {
        System.out.println("您反转的链表为空~~~"  + "head.next.next == null单链表1个不需要反转");
        return;
    }
    /**
     * 思路:
     * 1、创建新反转的头节点
     * 2、然后在旧的头取出数据放在新头节点后面,
     * 3、同时是永远都要放在新头节点的后面,之前节点的前面(新链表的最前端)
     * */
    //定义一个辅助的指针(变量),帮助我们遍历原来的链表
    HeroNode cur = head.next;
    // 指向当前节点[cur]的下一个新节点(备份下一个新节点)
    HeroNode next = null;
    // 创建新的头结点
    HeroNode newHead = new HeroNode(0, "", "");
    while (cur != null){
        // 新链表下个要操作的节点
        next = cur.next;
        // 取出来的节点指向新的反转头结点最前端
        cur.next = newHead.next;
        // 新的反转头节点指向取出来的节点
        newHead.next = cur;
        // 新链表往后移动
        cur = next;
    }
    // 之前的头结点指向 反转头节点的后面
    head.next = newHead.next;
}
4、逆向打印链表 (还可以通过链表反转在打印等等)
   /**
     * 逆向打印链表
     * 方式: 通过栈
     */
    public void reversetLog(HeroNode head){
        if(head == null || head.next == null) {
            System.out.println("链表为空");
            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());
        }
    }

双链表的实现:

在这里插入图片描述
分析 双向链表的遍历,添加,修改,删除的操作思路===》代码实现

  1. 遍历 方和 单链表一样,只是可以向前,也可以向后查找
  2. 添加 (默认添加到双向链表的最后)
    (1) 先找到双向链表的最后这个节点
    (2) temp.next = newHeroNode
    (3) newHeroNode.pre = temp;
  3. 修改 思路和 原来的单向链表一样.
  4. 删除
    (1) 因为是双向链表,因此,我们可以实现自我删除某个节点
    (2) 直接找到要删除的这个节点,比如 temp
    (3) temp.pre.next = temp.next
    (4) temp.next.pre = temp.pre;
public static void main(String[] args) {
        TwoLinkdList twoLinkdList = new TwoLinkdList();
        HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
        HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
        HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
        HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
        twoLinkdList.add(hero1);
        twoLinkdList.add(hero2);
        twoLinkdList.add(hero3);
        twoLinkdList.add(hero4);
        twoLinkdList.showTowLinkData();
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~");
        HeroNode2 hero5 = new HeroNode2(6, "林冲111", "豹子头111");
        twoLinkdList.upData(hero5);
        twoLinkdList.showTowLinkData();
//        twoLinkdList.deleteLink(3);
//        twoLinkdList.showTowLinkData();
    }

class TwoLinkdList{
    // 指向上面的指针
    public HeroNode2 pre;
    // 指向下面的指针
    public HeroNode2 next;
    // 头节点
    public HeroNode2 head = new HeroNode2(0,"","");


    public void add(HeroNode2 node){
        if(node == null) {
            System.out.println("传入的数据异为null");
            return;
        }


        HeroNode2 data = head;
        while (true){
            // 找到最后一个节点
            if (data.next == null){
                break;
            }
            data = data.next;
        }
        data.next = node;
        node.pre = data;
    }


    /**
     * 修改节点
     */
    public void upData(HeroNode2 node){
        if(head == null || head.next == null) {
            System.out.println("链表为空~~~");
            return;
        }


        HeroNode2 data = head.next;
        // 是否匹配到修改的标识
        boolean isSelecte = false;
        while (true){
            if(data.next == null && data.no != node.no) {
                System.out.println("没有找到匹配对象~~~");
                break;
            }
        
            if(data.no == node.no) {
                isSelecte = true;
                break;
            }
            data = data.next;
        }
        
        if(isSelecte) {
            data.setName(node.getName());
            data.setNickName(node.getNickName());
        }
    }


    /**
     * 查询双链表数据
     */
    public void showTowLinkData(){


        if(head == null || head.next == null) {
            System.out.println("链表为空~~~");
            return;
        }


        HeroNode2 data = head.next;
        while (data != null){
            System.out.println(data);
            data = data.next;
        }
    }


    /**
     * 双向链表删除某一个
     */
    public void deleteLink(int no){
        System.out.println("~~~~~~~~~~~~~~~~~~~~~");
        if(head == null || head.next == null) {
            System.out.println("双链表为空哦~~~");
            return;
        }


        HeroNode2 data = head.next;
        while (data != null){
            if(data.no == no) {
                // 说明删除的这个节点肯定是最后一个
                if(data.next == null) {
                    data.pre.next = null;
                }else {
                    // 正常删除
                    data.pre.next = data.next;
                    data.next.pre = data.pre;
                }
                break;
            }
            data = data.next;
        }
    }




}


    /**
     * 链表节点
     */
class HeroNode2{
        /** 排名*/
        public int no;
        /** 英雄人物*/
        public String name;
        /** 英雄代号*/
        public String nickName;


        public com.example.suanfa.lianbiao.HeroNode2 next;
        public com.example.suanfa.lianbiao.HeroNode2 pre;


        public HeroNode2 getPre() {
            return pre;
        }


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


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


        public com.example.suanfa.lianbiao.HeroNode2 getNext() {
            return next;
        }


        public void setNext(com.example.suanfa.lianbiao.HeroNode2 next) {
            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 String getNickName() {
            return nickName;
        }


        public void setNickName(String nickName) {
            this.nickName = nickName;
        }


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

约瑟夫问题:

/**
* Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,
* 数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,
* 依次类推,直到所有人出列为止,由此 产生一个出队编号的序列。
*
*
* 用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,
* 然后由 k 结点起从 1 开 始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个
*/

public static void main(String[] args) {
  
        SingLink singLink = new SingLink();
        // 约瑟夫问题
        BoyNode head = singLink.creatRingLink(5);
        singLink.outQueue(1,2,head,5);
        singLink.showRingLink(head);
    }

class SingLink{
    /**
     * 环形链表  约瑟夫问题
     * 创建环形链表  num: 环形链表的数量
     * 没有头节点
     */
    public BoyNode creatRingLink(int num){
        if(num <= 0) {
            System.out.println("创建的环形队列长度异常~~");
            return null;
        }


        // 定义一个当前执行的节点,比较好操作
        BoyNode curNode = null;
        BoyNode head = null;
        for(int i = 0; i < num; i++) {
            BoyNode boy = new BoyNode(i + 1);
            if(i == 0) {
                // 当前的节点
                curNode = boy;
                // 标记第一个
                head = boy;
                // 如果是一个环形指向自己
                curNode.setNext(boy);
            }else {
                // 当前的节点指向心节点
                curNode.setNext(boy);
                // 新节点指向第一个
                boy.next = head;
                // 更新当前节点
                curNode = boy;
            }
        }
        return head;
    }


    /**
     * 遍历环形链表
     */
    public void showRingLink(BoyNode node){
        if(node == null || node.next == null) {
            System.out.println("您输出的为空环形链表~~");
            return;
        }
        BoyNode head = node;
        while (head.next != null){
            System.out.println(head);
            if(head.next == node) {
                // 最后一个链表的下一个指向的是头,就退出
                break;
            }
            head = head.next;
        }
    }


    /**
     * 出队列
     * num:  出队列的数字
     * start: 第几个人开始数
     * sum: 圈里坐的人数
     * 思路:
     * 1、先创建一个辅助指针,helper指针,指向最后一个节点
     * 2、然后将first和helper指针移动到start - 1第几个人开始数
     * 3、让first和helper移动 num - 1 次
     * 4、让first 指向 下一个节点 ,  helper再次指向first
     *     first = first.next   helper.next = frist
     */
    public void outQueue(int start,int num, BoyNode first, int sum){
        if(num <= 0 || start < 1 || start > sum) {
            System.out.println("输入出队的数字不对");
            return;
        }


        if(first == null || first.next == null) {
            System.out.println("链表为空~~~");
            return;
        }


        // 1、创建辅助指针,并指向最后一个节点
        BoyNode helperNode = null;
        BoyNode cur = first;
        while(cur.next != null) {
            if(cur.next == first) {
                break;
            }
            cur = cur.next;
        }
        helperNode = cur;


        // 2、移动到要数的节点位置
        for(int i = 0; i < start - 1; i++) {
          first = first.next;
          helperNode = helperNode.next;
        }


        // 3、移动num - 1步,然后变换指针
        while (true){
            if(first == helperNode) {
                System.out.println("只剩下一个...");
                break;
            }


            for(int i = 0; i < num - 1; i++) {
                first = first.next;
                helperNode = helperNode.next;
            }
            System.out.printf("小孩%d 出圈\n", first.getNo());


            // 变换指针
            first = first.next;
            helperNode.next = first;
        }
        System.out.printf("最后留在圈中的小孩编号%d \n", first.getNo());
    }
}


class BoyNode{
    public int no;
    public BoyNode next;


    public int getNo() {
        return no;
    }


    public BoyNode(int no) {
        this.no = no;
    }


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




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


    @Override
    public String toString() {
        return "BoyNode{" +
                "no=" + no +
                '}';
    }
}
发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/102936563