单链表基本操作图文详解_清晰易懂(超详细,含完整代码)

目录

一.结点的定义

二.单链表的定义

三.单链表的操作

1.头插法

2.尾插法

3.任意位置插入(第一个数据节点为0号下标 )

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

5.得到单链表的长度

6.打印单链表

7.删除第一次出现关键字为key的节点

8.删除所有值为key的节点


双向链表的基本操作详解https://blog.csdn.net/qq_24016309/article/details/121163375

一.结点的定义

在c语言中,我们使用一个结构体来定义一个结点。但是在java中,我们使用一个类(class)来定义结点。

其中data表示当前结点中存放的数据,next表示当前结点的下一个结点。这里还定义了一个构造方法用于初始化data的值。

public class Node {
    public int data;
    public Node next;

    public Node(int data){
        this.data = data;
    }
}

二.单链表的定义

这里我们定义的单链表为不带头结点的非循环单链表,但是要有一个虚拟的头结点,来表示我们这个单链表的第一个结点。

public class MyLinkedList {  //不带头结点

    public Node head ;  //这个不算头结点,算是这个链表的第一个节点

}

三.单链表的操作

下面要介绍的是单链表的相关操作,这些函数放在上面的单链表MyLinkdeList中。经测试,所有功能均能正确实现

1.头插法

就是在最前插入,注意对链表为空的特判

先创建一个node,把数据放进这个node,再把这个node头插

    public void addFirst(int data){
        Node node = new Node(data);

        if(this.head == null){    //注意这里要进行特判
            this.head = node;
        }else {
            node.next = this.head;
            this.head = node;
        }
    }

2.尾插法

就是在最后面插入,注意对链表为空的特判

先创建一个node,把数据放进这个node,再把这个node尾插

注意:和头插法不同的是,要在链表的尾部插入,首先要做的就是找到链表的最后一个结点,链表的最后一个结点的next是null,所以循环退出条件就是cur.next == null,我们用一个while循环和一个当前结点的引用cur遍历到最后一个结点,然后让最后一个结点的next为我们要插入的node结点即可

    public void addLast(int data){
        Node node =new Node(data);
        if(this.head == null){
            this.head = node;
        }else {
            Node cur = this.head;
            while (cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
    }

3.任意位置插入(第一个数据节点为0号下标 )

就主要是对index的特判,如果index不合法(<0或者>length),返回false,若index为0,那就是头插,若Index为length,那就是尾插。

其他位置写一个找到index所对应结点的前一个结点的函数Node pre=searchPrev(index),然后将结点插入到这个pre结点之后就行了。

    public boolean addIndex(int index,int data){
        int len = this.size();
        if (index > len ||index < 0){   //对index合法性特判
            return false;
        }
        if(index == 0){   //当在头部插入时,使用头插法
            this.addFirst(data);
            return true;
        }
        if (index == len){  //当在尾部插入时,使用尾插法
            this.addLast(data);
            return true;
        }else {
            Node p = this.searchPrev(index); //找到index所对应结点的前一个结点
            Node node = new Node(data);
            node.next = p.next;
            p.next = node;
            return true;
        }
    }

 searchPrev(int index)函数:head为第一个结点,它的index值为0

如果我们要找到下标1所对应的结点,即第2个结点,那么从第一个结点head开始,执行1轮循环,即执行一次p = p.next即可。

如果我们要找到下标3所对应的结点,即第4个结点,那么从第一个结点head开始,执行3轮循环,即执行三次p = p.next即可。

............................

如果我们要找到下标Index所对应的结点,即第index+1个结点,那么从第一个结点head开始,执行index轮循环,即执行index次p = p.next即可。

现在我们要查找下标为Index的前一个结点,即下标为index的结点

如果我们要找到下标Index-1所对应的结点,即第index个结点,那么从第一个结点head开始,执行index-1轮循环,即执行index-1次p = p.next即可。

所以执行index-1轮循环即可

    public Node searchPrev(int index){     //返回index所对应结点的前一个结点
        Node p = this.head;
        int count = 0;
        while(count < index-1){
            p = p.next;
            count++;
        }
        return p;
    }

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

这个就很简单了,直接一轮循环结束

    public boolean contains(int key){
        Node p = this.head;
        while(p != null){
            if(p.data == key){
                return true;
            }
            p = p.next;
        }
        return false;
    }

5.得到单链表的长度

这个也是非常简单,一轮循环结束

    public int size(){
        Node p = this.head;
        int len = 0;
        while (p != null){
            p = p.next;
            len++;
        }
        return len;
    }

6.打印单链表

也是一轮循环结束,这里我是每个结点的数据一行这样打印了,也可以打印成一行

    public void display(){
        Node cur = head;
        while (cur != null){
            System.out.println(cur.data);
            cur = cur.next;
        }
    }

7.删除第一次出现关键字为key的节点

主要还是一些特判,1.链表为空;2.key值为头结点上的data;

注意特判里面要return,以为只需要删除第一次出现的结点。

其他的位置,参考下面的删除全部关键字为Key的结点的讲解,找到data=key就break即可。

    public void remove(int key){
        if (this.head == null){
            System.out.println("链表为空");
            return;
        }
        if(this.head.data == key){
            this.head = this.head.next;
            return;
        }
        Node pre = head;
        Node cur = head.next;
        while(cur != null){
            if(cur.data == key){
                pre.next = cur.next;
                break;
            }else {
                pre = cur;
            }
            cur = cur.next;
        }
    }

8.删除所有值为key的节点

这个就比较重要了,要弄明白

  • 首先就是特判,和前面一样,1.链表为空;2.key值为头结点上的data,但是这里要注意一点,对key值是不是为头结点上的data的值这个判定不能放在前面,必须放在最后

因为,比如你要删除2,如果第一个结点和第二个结点都是2,那你处理完第一个结点,第二个结点还是2,此时的头结点还是2,但是头结点的处理只有一次。所以这里要将对头结点的处理放到最后面去,等到我们先把除了头结点以外其他的data=2的结点删除了,再去判断头结点也是2的情况。

如图所示,一开始有三个2,我们先将除了头结点以外的2先删除了,再去对头结点进行删除,此时便不会再有多余的2。

  •  然后要注意的就是如何将后面的2都删除

这里我们定义两个Node类型的引用,pre代表头结点,cur代表头结点的下一个结点,如图所示,我们的目的是要删除所有的2

 然后开始判断,如果cur.data==key,这里key=2,满足条件,那就执行删除操作,让pre.next = cur.next,然后cur = cur.next

 else,也就是如果cur不是我们要删除的结点,我们就让pre = cur,把pre拉过来,然后cur = cur.next

 就这样一直进行循环,直到最后cur=null退出循环。

    public void removeAllKey(int key){
        if (this.head == null){
            System.out.println("链表为空");
        }

        Node pre = head;
        Node cur = head.next;
        while(cur != null){
            if(cur.data == key){
                pre.next = cur.next;
                cur = cur.next;
            }else {
                pre = cur;
                cur = cur.next;
            }
        }

       if(this.head.data == key){   //如果本来的第二个结点还是key呢,这个必须放到后面
            this.head = this.head.next;
        }
    }

Guess you like

Origin blog.csdn.net/qq_24016309/article/details/121004625