数据结构与算法--004-队列链表实现--Java

链表

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

  1. 链表是以节点的方式来储存.链式储存
  2. 每个节点包含data 域, next 域: 指向下一个节点
  3. 如图: 发现链表的各个节点不一定是连续存储.
  4. 链表分带头节点的链表和没有头节点的链表, 根据实际的需求来确定

单链表(带头结点) 逻辑结构示意图如下

在这里插入图片描述

在这里插入图片描述

单链表的应用实例

第一种方法在添加英雄时, 直接添加到链表的尾部

不考虑顺序编号

  1. 找的当前链表的最后节点
  2. 将最后这个节点的next指向新的节点
  3. 将最后这个节点的next指向新的节点 temp.next = heroNode;
  public void add(HeroNode heroNode) {
        //头节点不能动,需要一个辅助遍历temp
        HeroNode temp = head;
        //遍历链表到最后
        while (true) {
            if (temp.next == null) {
                break;
            }
            //如果没有找的,将temp后移
            temp = temp.next;
        }
        //当退出while循环,temp就指向链表的最后
        //将最后这个节点的next指向新的节点
        temp.next = heroNode;
    }

在这里插入图片描述

第二种方式在添加英雄时,根据排名将英雄插入到指定位置

  1. 因为头节点不能动,因此我们仍然通过一个辅助指针(变量)
  2. 因为单链表,我们找的temp是位于添加位置的前一个节点,否则插入失败
  3. temp = temp.next;
  4. 插入到链表中, temp的后面
  5. heroNode.next = temp.next;
  6. temp.next = heroNode;
       HeroNode temp = head;
        boolean flag = false; // flag标志添加的编号是否存在,默认为false
        while (true) {
            //说明已经在链表最后了
            if (temp.next == null) {
                break;
            }
            位置找到,就在temp的后面插入
            if (temp.next.no > heroNode.no) {
                break;
                //说明希望添加的heroNode的编号已然存在
            } else if (temp.next.no == heroNode.no) {
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }
        if (false) {
            System.out.println(heroNode.no + "该编号存在");
        } else {
            //插入到链表中, temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

在这里插入图片描述

修改节点的信息

  • 根据 newHeroNode 的 no 来修改即可
  • 找到需要修改的节点, 根据no编号定义一个辅助变量
  • 找到该节点
  • 根据flag 判断是否找到要修改的节点
  • temp.name = newHeroNode.name;
//修改节点
    public void update(HeroNode heroNode) {
        if (head.next == null) {
            System.out.println("此节点为空");
            return;
        }
        //找到要修改的节点,根据no修改
        //定义一个辅助变量
        HeroNode temp = head;
        boolean flag = false;//表示是否找到该节点
        while (true) {
            if (temp == null) {
                break;//遍历完毕
            }
            if (temp.no == heroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否是要修改的节点
        if (flag) {
            temp.name = heroNode.name;
        } else {
            System.out.println("没有找到要修改的编号" + heroNode.no);
        }
    }

在这里插入图片描述

删除节点

  • head 不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
  • 说明我们在比较时,是temp.next.no 和 需要删除的节点的no比较
  • 遍历链表,找到对应的链表
  • temp.next = temp.next.next;
 1.head不能动,需要一个辅助变量temp 辅助节点找到要删除的节点
    1.我们在比较的时候只需要找到temp.next.no 和要需要的no作比较
     */
    public void del(int no) {
        //找到要修改的节点,根据no修改
        //定义一个辅助变量
        HeroNode temp = head;
        boolean flag = false;// 标志是否找到待删除节点的
        while (true) {
            if (temp.next == null) { //已经到链表的最后
                break;//遍历完毕
            }
            if (temp.next.no == no) {
                //找到的待删除节点的前一个节点temp
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.println("要删除的节点不在" + no);
        }
    }

在这里插入图片描述

获取到单链表的节点个数(不统计头节点)

  • 定义一个辅助变量
  • cur = cur.next;//遍历
    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;
    }

查找单链表中的倒数第k个结点

  1. 编写一个方法接受head节点,同时接收一个index
  2. index 表示说倒数index个系欸但
  3. 得到链表的总的长度 getLength
  4. 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
  5. 如果找到了,则返回该节点,否则返回nulll
 /*
   查找单链表中的倒数第k个结点
   思路:
   1.编写一个方法接受head节点,同时接收一个index
   2.index 表示说倒数index个系欸但
   3.得到链表的总的长度 getLength
   4. 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
   5.如果找到了,则返回该节点,否则返回nulll
    */
    public static HeroNode findLastIndexNode(HeroNode head, int index) {
        //判断是否为空 返回null
        if (head.next == null) {
            return null;
        }
        //遍历得到其长度
        int size = getLength(head);
        if (index <= 0 || index > size) {
            return null;
        }
        //定义辅助遍历 cur, 通过 size-index 确定其位置
        HeroNode cur = head.next;
        for (int i = 0; i < size - index; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /*
    获取到单链表的节点个数(不统计头节点)
     */
    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;
    }

}

在这里插入图片描述

反转链表

  1. //设置一个辅助遍历 cur 用来遍历 HeroNode cur = head.next;

  2. //用来存放cur的下一个结点,辅助遍历 HeroNode next = null; 特别注意

  3. 设置一个新链表每取出一个链表的头结点,每次放到新链表reverseHead的头部

  4. 暂时保存cur下一个节点

    //必须保存 保存的是被反转的下一个的节点
    /** 这种必然失败
    cur.next=reverseHead.next;
    reverseHead.next=cur;
    cur= cur.next;
    */
    
  5. 暂时保存cur下一个节点next=cur.next;//

  6. 将cur的下一个节点指向新的链表的最前端cur.next=reverseHead.next;//

  7. 将cur 连接到新的链表上reverseHead.next=cur;//

  8. 将暂时保存cur下一个的节点归还给curcur= next; //

  9. 将head.next 指向 reverseHead.next , 实现单链表的反转 head.next = reverseHead.next;

   //反转链表
    public static void reverList(HeroNode head) {
        //当前链表为空
        if (head.next == null || head.next.next == null) {
            return;
        }
        //设置一个辅助遍历 cur 用来遍历
        HeroNode cur = head.next;
        //用来存放cur的下一个结点,辅助遍历
        HeroNode next = null;
        //设置一个新链表
        HeroNode reverseHead = new HeroNode(0, "");
        //每取出一个链表的头结点,每次放到新链表reverseHead的头部
        while (cur != null){
            //必须保存 保存的是被反转的下一个的节点
            /** 这种必然失败
             cur.next=reverseHead.next;
             reverseHead.next=cur;
             cur= cur.next;
             */
            next=cur.next;//暂时保存cur下一个节点
            cur.next=reverseHead.next;//将cur的下一个节点指向新的链表的最前端
            reverseHead.next=cur;//将cur 连接到新的链表上
            cur= next; //将暂时保存cur下一个的节点归还给cur
        }
        //将head.next 指向 reverseHead.next , 实现单链表的反转
        head.next = reverseHead.next;
    }

在这里插入图片描述

逆序打印

  • 思路一: 将其反转,然后打印,这样会破环链表的本身结构,不利后续的操作
  • 思路二: 利用栈的这个数据结构的特殊性,先进后出,将各个节点的数据压入到栈中,然后将其输出就可以
 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;//cur后移,这样就可以压入下一个节点
        }
        //将栈中的节点进行打印,pop 出栈
        while ((stack.size()>0)){
            System.out.println(stack.pop());//stack的特点是先进后出
        }
    }

在这里插入图片描述

完整代码

package com.nie.linkedlist;

import java.util.Stack;

public class SingleLinkListDemo {
    public static void main(String[] args) {
        HeroNode hero1 = new HeroNode(1, "黎明");
        HeroNode hero2 = new HeroNode(2, "郭富城");
        HeroNode hero3 = new HeroNode(5, "刘德华");
        HeroNode hero4 = new HeroNode(6, "张学友");

        //创建链表
        SingleLinkList singleLinkList = new SingleLinkList();
        //增加结点
        //根据插入的顺序进行插入
        singleLinkList.add(hero1);
        singleLinkList.add(hero2);
        singleLinkList.add(hero3);
        singleLinkList.add(hero4);

        //增加结点
        //根据节点的编号进行插入
//        singleLinkList.addByOrder(hero1);
//        singleLinkList.addByOrder(hero2);
//        singleLinkList.addByOrder(hero4);
//        singleLinkList.addByOrder(hero3);
        singleLinkList.list();
        System.out.println("+++++++++++");
        System.out.println("-------逆序----------");
        System.out.println("逆序打印单链表, 没有改变链表的结构~~");
        reversePrint(singleLinkList.getHead());

        System.out.println("-------反转----------");
        reverList(singleLinkList.getHead());
        singleLinkList.list();
        System.out.println("-------修改-----------");
        HeroNode newHeroNode = new HeroNode(2, "舞王郭富城");
        singleLinkList.update(newHeroNode);
        singleLinkList.list();
        System.out.println("-------删除-----------");
        singleLinkList.del(1);
        singleLinkList.list();

        System.out.println("有效结点的个数" + getLength(singleLinkList.getHead()));
        //测试第倒数几的结点的
        int indexLast = 1;
        HeroNode res = findLastIndexNode(singleLinkList.getHead(), indexLast);
        System.out.println("倒数第" + indexLast + "的结点为" + res);
    }


    //逆序打印
    /*
    思路一: 将其反转,然后打印,这样会破环链表的本身结构,不利后续的操作
    思路二: 利用栈的这个数据结构,将各个节点的数据压入到栈中,然后将其输出就可以
     */
    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;//cur后移,这样就可以压入下一个节点
        }
        //将栈中的节点进行打印,pop 出栈
        while ((stack.size() > 0)) {
            System.out.println(stack.pop());//stack的特点是先进后出
        }
    }

    //反转链表
    public static void reverList(HeroNode head) {
        //当前链表为空
        if (head.next == null || head.next.next == null) {
            return;
        }
        //设置一个辅助遍历 cur 用来遍历
        HeroNode cur = head.next;
        //用来存放cur的下一个结点,辅助遍历
        HeroNode next = null;
        //设置一个新链表
        HeroNode reverseHead = new HeroNode(0, "");
        //每取出一个链表的头结点,每次放到新链表reverseHead的头部
        while (cur != null) {
            //必须保存 保存的是被反转的下一个的节点
            /** 这种必然失败
             cur.next=reverseHead.next;
             reverseHead.next=cur;
             cur= cur.next;
             */
            next = cur.next;//暂时保存cur下一个节点
            cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端
            reverseHead.next = cur;//将cur 连接到新的链表上
            cur = next; //将暂时保存cur下一个的节点归还给cur
        }
        //将head.next 指向 reverseHead.next , 实现单链表的反转
        head.next = reverseHead.next;
    }


    /*
   查找单链表中的倒数第k个结点
   思路:
   1.编写一个方法接受head节点,同时接收一个index
   2.index 表示说倒数index个系欸但
   3.得到链表的总的长度 getLength
   4. 得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
   5.如果找到了,则返回该节点,否则返回nulll
    */
    public static HeroNode findLastIndexNode(HeroNode head, int index) {
        //判断是否为空 返回null
        if (head.next == null) {
            return null;
        }
        //遍历得到其长度
        int size = getLength(head);
        if (index <= 0 || index > size) {
            return null;
        }
        //定义辅助遍历 cur, 通过 size-index 确定其位置
        HeroNode cur = head.next;
        for (int i = 0; i < size - index; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /*
    获取到单链表的节点个数(不统计头节点)
     */
    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;
    }

}


class SingleLinkList {

    //初始化一个头节点
    private HeroNode head = new HeroNode(0, "");


    //返回头节点
    public HeroNode getHead() {
        return head;
    }


    添加节点到单向链表
    /*
    不考虑顺序编号
    1.找的当前链表的最后节点
    2.将最后这个节点的next指向新的节点
     */
    public void add(HeroNode heroNode) {
        //头节点不能动,需要一个辅助遍历temp
        HeroNode temp = head;
        //遍历链表到最后
        while (true) {
            if (temp.next == null) {
                break;
            }
            //如果没有找的,将temp后移
            temp = temp.next;
        }
        //当退出while循环,temp就指向链表的最后
        //将最后这个节点的next指向新的节点
        temp.next = heroNode;
    }


    /*
  第二种方式在添加英雄时,根据排名将英雄插入到指定位置
(如果有这个排名,则添加失败,并给出提示)
     */
    public void addByOrder(HeroNode heroNode) {
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)
        //因为单链表,我们找的temp是位于添加位置的前一个节点,否则插入失败
        HeroNode temp = head;
        boolean flag = false; // flag标志添加的编号是否存在,默认为false
        while (true) {
            //说明已经在链表最后了
            if (temp.next == null) {
                break;
            }
            位置找到,就在temp的后面插入
            if (temp.next.no > heroNode.no) {
                break;
                //说明希望添加的heroNode的编号已然存在
            } else if (temp.next.no == heroNode.no) {
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }
        if (false) {
            System.out.println(heroNode.no + "该编号存在");
        } else {
            //插入到链表中, temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    //修改节点
    public void update(HeroNode heroNode) {
        if (head.next == null) {
            System.out.println("此节点为空");
            return;
        }
        //找到要修改的节点,根据no修改
        //定义一个辅助变量
        HeroNode temp = head;
        boolean flag = false;//表示是否找到该节点
        while (true) {
            if (temp == null) {
                break;//遍历完毕
            }
            if (temp.no == heroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否是要修改的节点
        if (flag) {
            temp.name = heroNode.name;
        } else {
            System.out.println("没有找到要修改的编号" + heroNode.no);
        }
    }

    //修改节点
    /*
    思路
    1.head不能动,需要一个辅助变量temp 辅助节点找到要删除的节点
    1.我们在比较的时候只需要找到temp.next.no 和要需要的no作比较
     */
    public void del(int no) {
        //找到要修改的节点,根据no修改
        //定义一个辅助变量
        HeroNode temp = head;
        boolean flag = false;// 标志是否找到待删除节点的
        while (true) {
            if (temp.next == null) { //已经到链表的最后
                break;//遍历完毕
            }
            if (temp.next.no == no) {
                //找到的待删除节点的前一个节点temp
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.println("要删除的节点不在" + no);
        }
    }

    //显示链表
    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);
            temp = temp.next;
        }
    }
}


class HeroNode {
    public int no;
    public String name;
    public HeroNode next;//指向下一个结点

    //构造器
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值