学习数据结构笔记=====>链表

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习数据结构笔记=====>链表相关的知识,希望对你有一定的参考价值。

学习来源–>传送门–>尚硅谷Java数据结构与java算法(Java数据结构与算法)


链表存储结构 不一定就是连续的;比如下面这个单链表;
链表由一个个地节点组成; data 存放实际数据,next类似于一个指针;指向下一个节点;
最后一个节点的指针next指向 null;
还有,链表不一定都需要头节点;

单向链表实现

案例,完成一个存放英雄的链表,可以增删改查的那种单链表实现;
创建的结点存放时,会保存英雄的编号,姓名,以及指向下一个英雄的指针;

先完成创建链表,添加节点;然后遍历链表;

public class LinkTest01 {

    //用节点存放英雄;
    class HeroNode {
        //编号;
        private int serial;
        //姓名,
        private String name;
        //指针;
        private HeroNode next;

        //初始化;
        public HeroNode(int serial, String name) {
            this.serial = serial;
            this.name = name;
        }

        //输出节点;
        @Override
        public String toString() {
            return "HeroNode{" + "serial=" + serial + ", name='" + name + '}';
        }
    }

    //链表;
    class OneDirectionLink {
        //链表头结点; 空数据;
        private HeroNode headNode = new HeroNode(0, "");

        //判断链表为空;
        public boolean isEmpty() {
            return headNode == null;
        }
        //获取头结点;
        public HeroNode getHeadNode() {
            return headNode;
        }

        //添加新节点时,直接尾插法; 在后面添加即可; 让指向null的节点 指向这个添加的节点,添加的节点指向 null;
        public void addNode(HeroNode heroNode) {
            //把头结点存入临时节点;
            HeroNode temp = headNode;
            //找最后一个;
            while (temp.next != null) {
                //节点后移;
                temp = temp.next;
            }
            //找到了,连接即可;
            temp.next = heroNode;
            heroNode.next = null;
        }

        //遍历链表;
        public void getAllLink() {
            if (isEmpty()) {
                System.out.println("链表为空,不遍历");
                return;
            }
            //遍历时;
            //头结点为空数据,不用输出;
            HeroNode tempNode = headNode.next;
            //遍历;结束条件;
            while (tempNode != null) {
                System.out.println(tempNode);
                tempNode = tempNode.next;
            }
        }
    }
    
 }   

测试:

//测试链表;
    public static void main(String[] args) {
        LinkTest01 test = new LinkTest01();
        //创建节点;
        HeroNode heroNode0 = test.new HeroNode(10, "杰克");
        HeroNode heroNode1 = test.new HeroNode(8, "刘备");
        HeroNode heroNode2 = test.new HeroNode(45, "马克");
        HeroNode heroNode3 = test.new HeroNode(489, "张飞");
        HeroNode heroNode4 = test.new HeroNode(32, "关羽");
        //创建链表;
        OneDirectionLink oneDirectionLink = test.new OneDirectionLink();
        //添加;
        oneDirectionLink.addNode(heroNode0);
        oneDirectionLink.addNode(heroNode1);
        oneDirectionLink.addNode(heroNode2);
        oneDirectionLink.addNode(heroNode3);
        oneDirectionLink.addNode(heroNode4);
        //遍历;
        oneDirectionLink.getAllLink();
    }
HeroNode{serial=10, name='杰克}
HeroNode{serial=8, name='刘备}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}
HeroNode{serial=32, name='关羽}

现在需要完成一个新的需求;添加节点时,按照英雄的编号大小去添加,而不是之前那样直接添加到尾部;那么在添加之前就得遍历链表来确定编号;决定好把新节点放到哪个位置;

直接在链表OneDirectionLink类中添加方法

//按照顺序添加节点;
public void addNodeOrder(HeroNode heroNode){
    //先把头结点存入临时节点;
    HeroNode temp = headNode;
    //放置一个标志位;
    boolean flag = false;
    //遍历; 找位置
    while (true){
        //结束条件;
        if(temp.next==null){
            break;
        }
        //比较大小; 得出要添加的节点位于 temp 与 temp.next 之间;
        if(temp.next.serial >heroNode.serial){
           break;
        }
        //编号相同;不许添加;
        else if(temp.next.serial == heroNode.serial) {
            flag = true;
            break;
        }
        //移动临时节点;
        temp = temp.next;
    }

    //对标志位进行判断;
    if(flag){
        //不可添加节点;
        System.out.println("该节点英雄已存在->"+heroNode.serial);
    }else {
        //添加;
        heroNode.next = temp.next;
        temp.next = heroNode;
    }
}

测试效果

//测试链表;
public static void main(String[] args) {
        LinkTest01 test = new LinkTest01();
        //创建节点;
        HeroNode heroNode0 = test.new HeroNode(10, "杰克");
        HeroNode heroNode1 = test.new HeroNode(8, "刘备");
        HeroNode heroNode2 = test.new HeroNode(45, "马克");
        HeroNode heroNode3 = test.new HeroNode(489, "张飞");
        HeroNode heroNode4 = test.new HeroNode(32, "关羽");
        //创建链表;
        OneDirectionLink oneDirectionLink = test.new OneDirectionLink();
        //添加;
        oneDirectionLink.addNodeOrder(heroNode0);
        oneDirectionLink.addNodeOrder(heroNode1);
        oneDirectionLink.addNodeOrder(heroNode2);
        oneDirectionLink.addNodeOrder(heroNode3);
        oneDirectionLink.addNodeOrder(heroNode4);
        //遍历;
        oneDirectionLink.getAllLink();
}        

添加的时候是有序的;

HeroNode{serial=8, name='刘备}
HeroNode{serial=10, name='杰克}
HeroNode{serial=32, name='关羽}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}

现在对这个单链表进行修改;由编号得到要修改的节点;仅限于对英雄名称的修改;

在链表OneDirectionLink类中添加修改方法;

//对单链表进行修改;
public void updateLink(HeroNode newNode){
    //先判空;
    if(isEmpty()){
        System.out.println("抱歉,链表为空");
        return;
    }
    //修改即可;
    HeroNode temp = headNode.next;
    //是否可修改的标志位;
    boolean flag =false;
    while (true){
        //遍历结束条件;
        if(temp==null){
            break;
        }
        //找编号;
        else if(temp.serial == newNode.serial){
            flag = true;
            break;
        }
        //移动节点;
        temp = temp.next;
    }

    //根据标志位决定是否修改;
    if(flag){
        //修改;
        temp.name = newNode.name;
    }else {
        System.out.println("抱歉,您要找的节点不存在==>"+newNode.serial);
    }
}

测试修改

//测试链表;
    public static void main(String[] args) {
        LinkTest01 test = new LinkTest01();
        //创建节点;
        HeroNode heroNode0 = test.new HeroNode(10, "杰克");
        HeroNode heroNode1 = test.new HeroNode(8, "刘备");
        HeroNode heroNode2 = test.new HeroNode(45, "马克");
        HeroNode heroNode3 = test.new HeroNode(489, "张飞");
        HeroNode heroNode4 = test.new HeroNode(32, "关羽");
        //创建链表;
        OneDirectionLink oneDirectionLink = test.new OneDirectionLink();
        //添加;
        oneDirectionLink.addNodeOrder(heroNode0);
        oneDirectionLink.addNodeOrder(heroNode1);
        oneDirectionLink.addNodeOrder(heroNode2);
        oneDirectionLink.addNodeOrder(heroNode3);
        oneDirectionLink.addNodeOrder(heroNode4);
        //遍历;
        oneDirectionLink.getAllLink();
        System.out.println("--------修改节点测试------------");
        //修改不存在的;
        oneDirectionLink.updateLink(test.new HeroNode(15,"阿杰"));

        //修改存在的;
        oneDirectionLink.updateLink(test.new HeroNode(10,"阿杰"));
        //遍历查看
        oneDirectionLink.getAllLink();
}        

修改不存在的节点,会提示信息;

HeroNode{serial=8, name='刘备}
HeroNode{serial=10, name='杰克}
HeroNode{serial=32, name='关羽}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}
--------修改节点测试------------
抱歉,您要找的节点不存在==>15
HeroNode{serial=8, name='刘备}
HeroNode{serial=10, name='阿杰}
HeroNode{serial=32, name='关羽}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}

完成对节点的删除,注意删除时把指针的next的指向调整好;
先找待删除结点的前一个结点; 然后把这个结点的指针next指向到待删除结点的下一个节点;

在链表OneDirectionLink类中添加删除方法;

//删除结点;
public void removeNode(int serial){
    //头结点存入临时节点;
    HeroNode temp = headNode;
    //放置标志位,判断是否找到待删除结点;
    boolean flag =false;
    //遍历找结点;
    while (true){
        //遍历结束条件;
        if(temp.next == null){
            break;
        }
        //找编号是否相同;
        if(temp.next.serial == serial){
            flag = true;
            break;
        }
        //移动节点;
        temp = temp.next;
    }
    //根据标志位决定是否要删除;
    if(flag){
        temp.next = temp.next.next;
    }else {
        System.out.println("要删除的节点不存在->"+serial);
    }
}

测试删除结点的方法

//测试链表;
public static void main(String[] args) {
    LinkTest01 test = new LinkTest01();
    //创建节点;
    HeroNode heroNode0 = test.new HeroNode(10, "杰克");
    HeroNode heroNode1 = test.new HeroNode(8, "刘备");
    HeroNode heroNode2 = test.new HeroNode(45, "马克");
    HeroNode heroNode3 = test.new HeroNode(489, "张飞");
    HeroNode heroNode4 = test.new HeroNode(32, "关羽");
    //创建链表;
    OneDirectionLink oneDirectionLink = test.new OneDirectionLink();
    //添加;
    oneDirectionLink.addNodeOrder(heroNode0);
    oneDirectionLink.addNodeOrder(heroNode1);
    oneDirectionLink.addNodeOrder(heroNode2);
    oneDirectionLink.addNodeOrder(heroNode3);
    oneDirectionLink.addNodeOrder(heroNode4);
    //遍历;
    oneDirectionLink.getAllLink();
    System.out.println("-----------删除结点测试---------------");
    oneDirectionLink.removeNode(10);
    //遍历
    oneDirectionLink.getAllLink();
} 

测试结果

HeroNode{serial=8, name='刘备}
HeroNode{serial=10, name='杰克}
HeroNode{serial=32, name='关羽}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}
-----------删除结点测试---------------
HeroNode{serial=8, name='刘备}
HeroNode{serial=32, name='关羽}
HeroNode{serial=45, name='马克}
HeroNode{serial=489, name='张飞}

单向链表相关题

1.求出单链表的实际存放节点个数

实际上,本可以在刚才的案例中定义一个数 size,记录节点的个数;添加时size+1 ;删除时size-1;
那就不加了;直接遍历也行;

在刚才的结点和链表基础上,在测试类中定义静态方法;

//计算节点的个数;
public static int getNodeSize(HeroNode headNode){
    if(headNode.next == null){
        System.out.println("这是空链表");
        return 0;
    }
    int size = 0;
    //头节点 指向 临时节点;
    HeroNode temp =headNode.next;
    while (temp!=null){
        size++;
        //移动操作的节点;
        temp = temp.next;
    }
    return size;
}

2. 查询单链表的倒数第k个节点

首先分析一下;
这个方法,会有两个参数; 一个是链表的头结点;一个是倒数第k个 数;

先把链表遍历一下;得出链表的长度;size;
然后再次遍历 (size - k) 即可得到想要查询的节点;

在测试类中定义静态方法;

//查询链表的倒数第K个节点;
    public static HeroNode getTheKNode(HeroNode head,int k){
        //先对链表判空,以及指定的位置判断;
        if(head.next == null || k<=0){
            return null;
        }
        //计算数量;
        int size = getNodeSize(head);
        //若指定的位置已经大于链表的长度;则直接返回null;
        if(size<k){
            return null;
        }
        //还是临时节点去活动;
        HeroNode temp =head.next;<

以上是关于学习数据结构笔记=====>链表的主要内容,如果未能解决你的问题,请参考以下文章

数据结构学习笔记(数组链表OJ题)整理与总结

数据结构学习笔记(数组链表OJ题)整理与总结

学习数据结构笔记=====>链表

《数据结构算法与应用 —— C++语言描述》学习笔记 — 字典 — 链表实现

尚硅谷算法与数据结构学习笔记03 -- 双向链表

尚硅谷算法与数据结构学习笔记02 -- 单链表