单链表的模拟实现

简介: 单链表的模拟实现

一:单链表的概念:

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

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

每个节点又分为数值域和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();
    }
}

目录
相关文章
|
7月前
|
C++
【C++】模拟实现二叉搜索树的增删查改功能
【C++】模拟实现二叉搜索树的增删查改功能
|
7月前
|
存储 机器学习/深度学习 算法
模拟实现单链表、双链表、栈、队列——数组模拟
我们在数据结构中都学到过单链表、双链表、栈和队列,当我们实现的时候时使用结构体指针实现的。定义一个结构体,结构体中存储指针变量和存放数值的变量。当然,C++的STL库中已经有实现好的栈和队列,我们可以直接用。但是在做算法题时,有时候我们会发现超出时间限制。原因是我们用STL库中的栈和队列容器时,效率相对来说较慢。我们这时就引出用数组模拟实现栈和队列。用数组模拟实现的使用起来效率更高、更方便。当然,我们也会讲到用数组模拟实现单链表和双链表。
28 0
|
5月前
|
存储
模拟实现顺序表
模拟实现顺序表
30 0
|
11月前
|
算法
【基础算法】单链表的OJ练习(6) # 复制带随机指针的链表 #
【基础算法】单链表的OJ练习(6) # 复制带随机指针的链表 #
|
12月前
|
C++
C++ --模拟实现搜索二叉树
#搜索二叉树 1. 搜索二叉树特点 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 每个结点的左右子树也分别为二叉搜索树
22 0
|
12月前
|
机器学习/深度学习 C++
|
存储
邻接表(单链表)详细分析,言简意赅
邻接表(单链表)详细分析,言简意赅
87 0
|
算法
大厂面试经典单链表例题(创建有序单链表,逆置单链表,判断链表是否有环,取链表中间节点)(含核心代码与解析)
大厂面试经典单链表例题(创建有序单链表,逆置单链表,判断链表是否有环,取链表中间节点)(含核心代码与解析)
|
Java C语言 C++
模拟单链表
模拟单链表
35 0
模拟单链表