单链表的模拟实现

简介: 单链表的模拟实现

一:单链表的概念:

单链表是一种在内存上不连续,而逻辑上连续的一种线性表。

单链表是由若干个节点和头节点组成的数据结构;

每个节点又分为数值域和next域:

数值域用来存储数据;
next域用来存储下一个节点的地址;

下图就是一个节点:

链表由若干个节点组成:

二:单链表中的方法:

// 1、无头单向非循环链表实现
public interface IList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    //打印单链表
    public void display();
    //清空单链表
    public void clear();
}

我们将模拟实现这些方法。

1:得到单链表的长度

public int size();

模拟实现:

定义一个变量count,用来计数;

这里我们采取遍历头结点的方法来获取长度:

当头节点不为空的时候,我们就让count++;

 public int size() {
        ListNode cur=head;
        int count=0;
        while(cur!=null){
            count++;
            cur=cur.next;//cur指向下一个节点
        }
        return count;
    }

2:查找是否包含关键字key是否在单链表当中

  public boolean contains(int key);

总体思路还是遍历链表节点中的数据,如果找到了返回true;如果链表遍历完了,仍没有找到,返回false。

public boolean contains(int key) {
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

可能有人会想到:如果单链表为空呢?

上面的代码也是没问题的,因为单链表为空,不会进入循环,会返回false的。

3:打印单链表中的数据:display()

@Override
    public void display() {
        ListNode cur=head;
        while(cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }
        System.out.println();
    }

3: 头插法

将一个节点放在头结点的位置:

头插:在这里我们只需要将node指向他的下一个节点,然后再让头节点更新数据:

看到上图:可能有小伙伴和我有一样的疑问,为什么插入数据的时候,要先绑定后面的数据呢?

这是因为

head=node;
node.next=head;

具体代码实现:

当先更新head的指向时,这时,还没有修改node 的指向,那么原来head指向的节点就没有被指向了,这是它就会被回收!

  public void addFirst(int data) {
        ListNode node =new ListNode(data);//实例化一个单链表
     if(head==null){
         head=node;//如果单链表为空,插入的节点就是头结点
     }else{
         //node节点的next指向head;
         node.next=head;
         head=node;//node变成头节点
     }
    }

测试一下:

public class Test {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.addFirst(1);
        singleLinkedList.addFirst(2);
        singleLinkedList.addFirst(3);
        singleLinkedList.addFirst(4);
        singleLinkedList.display();
    }
}

4:尾插法

在这里我们将最后一个节点的next域指向node即可!

head是指向头结点的,那么我们如何获得最后一个节点,进而修改它的next域呢?

很简单,我们看最后一个节点的特点:它的next域为null;那么我们只需要让cur节点从头结点往后移动,当cur.next==null时,说明cur节点指向最后一个节点

 public void addLast(int data) {
        ListNode node =new ListNode(data);
        if(head==null){
            head=node;
        }else {
            ListNode cur = head;
            while (cur.next != null) {
                cur = cur.next;
            }
            //此时cur指向最后一个节点
            cur.next = node;
        }
    }

测试一下:

public class Test{
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.addLast(1);
        singleLinkedList.addLast(2);
        singleLinkedList.addLast(3);
        singleLinkedList.addLast(4);
        singleLinkedList.display();
    }
}

5:任意位置插入:

在任意位置插入:我们首先要判断插入的位置是否有效;

然后判断单链表是否为空;

在根据插入的位置:

(1)index=0:往第一个节点也就是0下标的位置插入数据,也就是头插法;

(2)index=size():往最后一个位置插入数据,也就是尾插法

(3)其他情况下:

index就是插入中间数据:因为要修改节点的指向,我们需要让index的前一个位置(cur)的next域指向index节点,让index处的节点指向cur的下一个节点

代码实现:

public void addIndex(int index, int data) {
        if(index<0||index>size()){//判断index是否有效
            throw new IndexException("index越界"+index);
        }
        ListNode node =new ListNode(data);
        if(head == null) {//判断链表是否为空
            head = node;
            return;
        }
        if(index==0){
            //头插
            addFirst(data);
            return;
        }
        if(index==size()){
            //尾插
            addLast(data);
            return;
        }
        //中间插入
        ListNode cur=searchPrevIndex(index);//cur指向index下标的前一个节点
        ListNode curNext=cur.next;
        cur.next=node;
        node.next=curNext;
    }
    private ListNode searchPrevIndex(int index) {
        ListNode cur=head;
        int count=0;
        while(count!=index-1){
            count++;
            cur=cur.next;
        }
        return cur;
    }
    @Override
    public boolean contains(int key) {
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

测试一下:

public class Test{
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.addLast(1);
        singleLinkedList.addLast(2);
        singleLinkedList.addLast(3);
        singleLinkedList.addLast(4);
        singleLinkedList.display();
        System.out.println("===========================");
        singleLinkedList.addIndex(0,10);
        singleLinkedList.display();
        System.out.println("=================");
        singleLinkedList.addIndex(4,22);
        singleLinkedList.display();
        System.out.println("=================");
        singleLinkedList.addIndex(1,100);
        singleLinkedList.display();
    }
}

6:删除第一次出现关键字为key的节点

在这里定义一个cur指针用来表示要删除节点的头节点,

 public void remove(int key) {
        if(head==null){
           return;
        }
        if(head.val==key){
            head=head.next;
            return;
        }
        ListNode cur=searchPrevRemove(key);
        if(cur==null){
             return;
        }
          ListNode del=cur.next;
          cur.next=del.next;
    }
    private ListNode searchPrevRemove(int key){
        ListNode cur=head;
        while(cur.next!=null){
            if(cur.next.val==key){
                return cur;
            }
            cur=cur.next;
        }
        return null;
    }
    @Override
    public void removeAllKey(int key) {
    }
    @Override
    public int size() {
        ListNode cur=head;
        int count=0;
        while(cur!=null){
            count++;
            cur=cur.next;//cur指向下一个节点
        }
        return count;
    }
    @Override
    public void display() {
        ListNode cur=head;
        while(cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }
        System.out.println();
    }
    @Override
    public void clear() {
    }
}

测试一下:

public class Test{
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.addLast(1);
        singleLinkedList.addLast(2);
        singleLinkedList.addLast(3);
        singleLinkedList.addLast(4);
        singleLinkedList.addLast(5);
        singleLinkedList.display();
        singleLinkedList.remove(1);
        singleLinkedList.display();
        System.out.println("=============");
        singleLinkedList.remove(5);
        singleLinkedList.display();
        System.out.println("===============");
        singleLinkedList.remove(3);
        singleLinkedList.display();
    }
}

6:删除所有值为key的节点

定义两个指针:

prev表示可能要删除节点的前一个节点

cur表示要删除的节点。

我们在这里从第二个节点开始遍历,如果cur是要删除的节点,让prev.next=cur.next,也就是让prev指向下一个节点,prev是可能要删除的节点的前一个节点,当cur被删除后,prev仍然是cur下一个节点的前一个节点,所以cur被删除后,prev不能移动。

如果cur节点没有被删除,说明cur不是要被删除的节点,prev要移动到这个cur节点。

为什么此时prev移动呢?

cur节点没有被删除,说明可能删除节点的前一个节点就是cur节点了;判断cur移动到的下一个节点是否要被删除,prev表示可能要删除节点的前一个节点,所以当cur不被删除时,prev要移动

    public void removeAllKey(int key) {
        if(head==null){
            return;//单链表为空,直接返回
        }
        ListNode cur=head.next;//从第二个节点判断是否等于key
        ListNode prev=head;
        while(cur!=null){
            if(cur.val==key){
               prev.next=cur.next;
               cur=cur.next;
            }else{
                prev=cur;
                cur=cur.next;
            }
        }
        if(head.val==key){//判断头节点是否==key
            head=head.next;
        }
    }

测试一下:

public class Test{
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.addLast(1);
        singleLinkedList.addLast(2);
        singleLinkedList.addLast(6);
        singleLinkedList.addLast(3);
        singleLinkedList.addLast(4);
        singleLinkedList.addLast(5);
        singleLinkedList.addLast(6);
        singleLinkedList.display();
        System.out.println("==========================");
        singleLinkedList.removeAllKey(6);
        singleLinkedList.display();
    }
}

目录
相关文章
Altium Designer如何设定/修改PCB板边框外形
Altium Designer如何设定/修改PCB板边框外形
2585 0
|
弹性计算 虚拟化 异构计算
2023阿里云GPU服务器租用费用说明:包年包月、小时收费、学生GPU服务器租用费用
阿里云GPU服务器租用价格表包括包年包月价格、一个小时收费以及学生GPU服务器租用费用,阿里云GPU计算卡包括NVIDIA V100计算卡、T4计算卡、A10计算卡和A100计算卡,GPU云服务器gn6i可享受3折优惠,分享阿里云GPU服务器租用价格表、GPU一个小时多少钱以及学生GPU服务器收费价格表:
3739 0
|
7月前
|
运维 安全 开发工具
GitHub 热门开源运维工具 Websoft9:如何实现服务器管理效率翻倍?
Websoft9 提供 200+ 开源应用一键部署,支持容器化隔离、GitOps 自动化和企业级安全防护,助力服务器管理效率提升 80%。
209 1
|
Linux 数据安全/隐私保护 网络安全
Centos7环境下搭建SVN服务器
SVN是subversion的缩写,是一个开放源代码的版本控制系统,通过采用分支管理系统的高效管理,简而言之就是用于多个人共同开发同一个项目,实现共享资源,实现最终集中式的管理。
529 0
|
10月前
|
DataWorks 搜索推荐 大数据
聊聊DataWorks——这个一站式智能大数据开发治理平台
聊聊DataWorks——这个一站式智能大数据开发治理平台
649 2
|
Ubuntu Apache
Ubuntu20.04下一键安装ROS1 Noetic
本文提供了一个简化在Ubuntu 20.04系统上安装ROS1 Noetic过程的一键安装脚本工具,该脚本通过优化配置和使用清华大学镜像源,加速了国内用户的下载速度,并自动完成环境设置和依赖安装,同时提供了详细的使用说明和源码。
811 0
Ubuntu20.04下一键安装ROS1 Noetic
|
12月前
|
存储 安全 API
项目管理系统介绍,核心概念与操作技巧
项目管理系统通过任务分解、工时管理和项目规划等功能提升效率,适用于多种场景,具有高度可定制性。它能满足从小团队到跨国公司的需求,注重数据安全并与第三方软件集成。Zoho Projects因功能全面、价格亲民及易用性受到中小企业欢迎。
137 0
|
编译器 C语言 C++
xv6(21) 内联汇编
内联汇编
188 1
Shortest Path with Obstacle( CodeForces - 1547A )(模拟)
Shortest Path with Obstacle( CodeForces - 1547A )(模拟)
162 0
|
算法 大数据 数据挖掘
Pandas数据分析基础操作
最近出了几期pandas的函数用法和一些数据框操作的讲解文章,感觉效果还行——一些有幸能被推荐,让更多人看到,帮助到一些人的同时,我也收获了许多宝贵的改进建议——欢迎大家继续批评指正,我们一起互相进步。 最近有读者反映我的内容中一些常用的基础函数并没有经过讲解,导致“基础教程”并不“基础”。缺乏基础的读者并不能一次性在我的文章里快速学到技巧,因为去找别的教程来看懂我的教程的确浪费精力。 因此本期我们补一下pandas基础的操作,包括但不限于: 迭代(也有称作遍历循环的,具体请看案例) 统计函数 排序 删除列
Pandas数据分析基础操作