链表的概念+MySingleList的实现

文章目录


链表

顺序存储:顺序表/ArrayList

  • 优点:给定下标的时候,查找速度快 o(1)
  • 缺点:插入和删除时要移动元素o(n) 、每次扩容会浪费资源

由于ArrayList的缺点,我们引入链式存储:链表

一、 链表的概念

1.概念

  • 链表在物理层面是非连续的存储结构,在逻辑上是连续的,通过引用链接次序来实现它的逻辑顺序
  • 链表由一个个结点组成,结点从堆上申请
  • 一个结点起码包含两个域,val域存储数组 、next域存储下一个结点的地址

2. 结构

链表由多种结构:由头、无头、单向、双向、循环、非循环

排列组合后有八种,重点了解无头单向非循环链表和无头双向链表

二、MySingleList的实现

无头单向非循环链表的实现

1 .定义内部类

java 复制代码
public class MySingleList {
    //链表是由一个一个的结点所组成的,可以把Node定义成一个内部类
    static class Node {
        public int val;//存储的数据
        public Node next;//存储下一个结点的地址
        public Node(int val) {//先不设置next,创建一个新的结点时,还不知道下一个结点是什么
            this.val = val;
        }
    }
}

链表是由一个个的结点所组成的,可以把Node定义成一个内部类

  • 定义一个val变量存储数据
  • 定义一个Node类型的next变量存储下一个结点的地址
java 复制代码
  public Node head;//代表当前链表的头结点的引用

head 代表当前链表的头结点的引用

  • 因为现在写的是不带头结点的单链表,通过一个变量head,来引用当前链表的头结点
  • head引用哪个结点,哪个就是当前链表的头结点

2 .创建链表

java 复制代码
public void creatList(){//创建一个链表
        Node node1 = new Node(12);
        Node node2 = new Node(86);
        Node node3 = new Node(33);
        Node node4 = new Node(45);
        node1.next =node2;
        node2.next =node3;
        node3.next =node4;
        head=node1;//创建头结点
    }

创建各个结点,给定val值,每个结点的next依次引用下一个结点的地址

确定头结点,head引用node1所引用的地址

3. 遍历链表并打印

java 复制代码
public void disPlay() {//不要改变head
        Node cur = head;//定义一个cur,让cur移动,head不动
        //链表遍历完,head==null
        //遍历到尾部,不打印最后一个,head.next==null
        while (cur!=null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

1.通过cur来代替head,保证head不会发生改变

2.循环的判断依据是cur!=null,这样能遍历完整

cur.next!=null会少打印最后一个

4.查找单链表中是否包含关键字key

java 复制代码
public boolean contains(int key) {
        Node cur = head;
        while (cur != null) {//当cur为空时,遍历完
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;//cur向后移动
        }
        return false;
    }

5.得到链表的长度

java 复制代码
 public int size(){
        int count = 0;
        Node cur = head;
        while (cur!=null) {//遍历一遍链表 o(n)
            count++;
            cur=cur.next;
        }
        return count ;
    }

遍历数组,直到cur为null跳出循环,说明已经走完整个链表,返回记录的次数

6.头插法

java 复制代码
 public void addFirst(int data){
        Node node = new Node(data);//新建一个结点
        node.next=head;
        head = node;
    }

1.创建新的结点

2.将新结点的next域存放原本头结点的地址值

3.让新结点成为新的头结点

这样就完成了头插法的实现

7. 尾插法

java 复制代码
 public void addList(int data) {
        Node node = new Node(data);
        if (head == null) {//判断头结点为空的情况
            head = node;//让新建的结点成为头节点
            return;//
        }

        Node cur = head;//让cur代替head
        while (cur.next != null) {
            cur = cur.next;
        }//当cur的next为null时,找到了最后一个结点
        //找到最后一个结点后,插入node结点
        cur.next = node;
    }

1.创建一个新结点

2.判断头结点是否为空

3.遍历链表找到当前最后一个结点

4.将新结点插到末尾
链表的插入只是修改指向

8.任意位置插入

java 复制代码
public void addIndex(int index, int data) throws IndexOutOfException {
        checkIndex(index);//先检查index的值是否合法
        if (index == 0) {//如果index=0,调用头插法
            addFirst(data);
            return;
        }
        if (index == size()) {//如果index的值为链表长度,调用尾插法
            addList(data);
            return;
        }
        Node cur = findIndexSubOne(index);//找到index的前一个元素
        Node node = new Node(data);
        node.next = cur.next;
        cur.next = node;
    }

    private void checkIndex(int index) {
        if (index < 0 || index > size()) {
            throw new IndexOutOfException("index位置不合法");
        }
    }

    //走index-1步,返回当前索引的地址
    private Node findIndexSubOne(int index) {
        Node cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

1.调用checkIndex方法先检查index的值是否合法,不合法抛出异常

2.判断索引,如果是0,调用头插法,如果等于链表长,调用尾插法

3.调用findIndexSubOne方法,找到index的前一个节点,返回地址值

4.创建一个新结点,使新结点的next域的值等于前一个结点next域的值

5.再让前一个结点的next域,引用新结点的地址值。

8.删除结点

删除第一次出现关键字为key的结点

java 复制代码
 public void remove(int key) {
        if (head == null) {//判断是否是空节点
            return; //一个结点都没有
        }
        if (head.val == key) {//如果当前头结点的元素等于要删除的元素
            head = head.next;//头结点向后移动
            return;
        }
        Node cur = searchPrev(key);//找到key的前驱结点
        if (cur == null) {//没有要删除的key
            return;
        }
        Node del = cur.next;//要删除的结点
        cur.next = del.next;
        //or cur.next = cur.next.next;
    }

    private Node searchPrev(int key) {//找到key的前一个结点

        Node cur = head;
        while (cur.next != null) { //cur.next==null,说明cur已经走到最后一个结点
            if (cur.next.val == key) {//如果cur下一个结点的值等于key
                return cur;//找到key的前一个结点
            }
            cur = cur.next;
        }
        return null;//没有你要删除的结点
    }

1.先定义searchPrev方法,遍历链表,如果cur的下一个结点的值=key,cur就为key的前驱结点

2.判断:如果头结点为空,链表中没有元素,直接返回

3.如果头结点的值,就是要删除的元素,头结点后移,直接返回

4.都不是,进入searchPrev方法,返回前驱结点cur;

5.如果返回的cur==null,说明遍历完链表,没有要删除的元素

6.要删除的结点del就是cur的下一个结点

7.让cur结点的next域引用del的下一个结点的地址;

完成删除

删除所有值为key的节点

java 复制代码
 public void removeAllKey(int key) {
        if (head == null) {//如果头结点为空,直接返回
            return ;
        }
        Node prev = head;
        Node cur = head.next;
        while (cur != null) {
            if (cur.val == key) {
                prev.next = cur.next;
                //cur = cur.next;
            } else {
                prev = cur;
                //cur = cur.next;
            }
            cur = cur.next;
        }
        if (head.val == key) {//最后处理头结点
            //如果头结点的值等于key,头结点后移
            head = head.next;
        }
    }

1.判断头结点是否为空

2.设置prev为头结点,cur为下一个结点(头结点key的情况最后考虑)

3.遍历链表,直到cur==null为止

4.如果cur的值等于key,prev的next域引用cur下一个结点的地址,cur后移一位

5.否则,prev移动到cur,cur后移一位

6.最后处理头结点,如果头结点刚好等于key,将头结点后移一位。

清空
java 复制代码
   public void clear(){//清空,让链表中所有的结点都不被引用
        head =null;
    }

清空,让链表中所有的结点都不被引用,head置为空

点击移步博客主页,欢迎光临~

相关推荐
Chrikk7 分钟前
Go-性能调优实战案例
开发语言·后端·golang
亦枫Leonlew8 分钟前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
幼儿园老大*9 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue10 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
考试宝11 分钟前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物
杜杜的man12 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟13 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天1 小时前
java的threadlocal为何内存泄漏
java
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express