Data structure: Detailed illustrations of various operations of singly linked lists (head insertion, tail insertion, insertion at any position, deletion of nodes, query of nodes, finding the length of the linked list, clearing the linked list)


Table of contents

 1. What is a linked list?

2. Implementation of linked list

Insertion of nodes

Head insertion method

tail insertion method

Insert at specified position

Node deletion

Delete the first occurrence of the keyword node

Delete all keyword nodes

Node search

Clearing the linked list

length of linked list


Foreword:In the previous article, we learned about the sequence table in the linear data structure, and this article introduces another structure in the linear data structure— —Linked list

If you want to know more about sequence table related operations, you can check this article:
Detailed explanation with pictures and text Various operations of sequence tables

 1. What is a linked list?

A linked list is a data structure that consists of a series of nodes, each node containing data and a pointer to the next node (next). The nodes in the linked list can be anywhere in the memory, and they are linked together through pointers to form a linked structure. The advantage of a linked list over an array is that it can dynamically add and delete nodes without moving a large amount of data. The disadvantage of the linked list is that when accessing elements, the entire linked list needs to be traversed, which is less efficient.

2. Implementation of linked list

The linked list is composed of different nodes strung together. Each node is composed of two parts. One is the data field, which is used to store the specific numerical content. The other is the pointer field, which is used to store the address information of the next node. Between each other It's like being strung together with a chain, as shown in the picture below, so it's called a linked list.

For a structure like the one shown above, we can define a linked list as follows, treating the linked list as a class, and in this class there is an internal class specifically to store the data structure of each node, and a head node is defined separately to record the start of the linked list. starting position

public class MyLinkList{
    //节点的数据结构
    static class ListNode{
        public int val;
        public ListNode next;
    }
    //链表的属性
    public ListNode head;//记录链表的第一个元素:头节点
}

For a linked list, it should complete the following functions. We abstract these functions into an interface, and then use this interface to implement a real linked list.

public interface Ilist {
    //头插
    void addFirst(int data);
    //尾插
    void addLast(int data);
    //指定插入
    void addIndex(int index,int data);
    //查询是否存在
    boolean contains(int key);
    //删除节点
    void remove(int key);
    //删除所有与目标相同的节点
    void removeAllKey(int key);
    //得到链表的长度
    int size();
    //清空链表
    void clear();
    //输出链表的所有信息
    void display();
}

Insertion of nodes

We divide node insertion into three types:

  • Header insertion:Insert the node to the front of the linked list
  • Tail insertion:Insert the node to the end of the linked list
  • Insert at the specified position:Insert the node into the middle of the linked list

Head insertion method

As shown in the figure, we are ready to insert the linked list just now

We proceed here in two steps:

  1. Point new node to head node
  2. Update the position of the head node

We change the pointer field of the node to be added so that it points to the new node 

After the pointing is completed, our newly added node is already the first node of the linked list, so we need to update the information of the head node and record that the new node is the first node

In this way, we have completed the head insertion node. The specific code is implemented as follows. First generate a node, and then operate according to the idea shown in the above figure.

    //头插法
    public void addFirst(int data) {
        ListNode newNode = new ListNode(data);
        newNode.next = this.head;
        this.head = newNode;
    }

tail insertion method

The tail insertion method inserts the node to the end of the linked list, but we do not record the position of the end node, so if you want to use the tail insertion method, you need to find the tail node first. Then we only need to traverse to find the last node based on the characteristics of the last node, andThe biggest characteristic of the last node is that it only has information in the data field, and the pointer field is empty< /span>. If the linked list is empty, then we can add it directly after the head node. The overall process is as follows:

The overall code is implemented as follows. First determine whether the linked list is empty. If it is empty, add it directly after the head node. If it is not empty, traverse to find the last node. Then change the content of the pointer field and add a new node.

    //尾插法
    public void addLast(int data) {
        //当链表为空的时候,直接将节点插入到头节点的位置
        ListNode newNode = new ListNode(data);
        if (head == null) {
            head = newNode;
        }else{
            ListNode cur = head;
            while (cur.next != null){
                cur = cur.next;
            }
            //找到最后一个节点
            cur.next = newNode;
            //newNode.next = null;//默认新节点的指针域为空,所以这里可以不写这一行代码
        }
    }

Insert at specified position

Inserting in the middle position is the most troublesome. The reason is that we cannot immediately obtain the information about the insertion position. We need to judge first. If the input position is at the front, then you can use the head plug. If it is at the end, use it. Tail plug. After knowing that the input position is in the middle of the linked list, we need to first find the information about the nodes before and after this position, as shown below. If the position we want to insert is the third position, then we need to know the second position and the third position. When we find the location information, we can operate it in two parts (the order cannot be changed):

  1. Let the node point to the following node first
  2. Then let the previous node point to the inserted node

The first step is to let the inserted node point to the following node

The second step is to point the previous node to the inserted node

We can implement this process through code, first judging the legality, and then performing targeted insertions.

    //指定位置添加
    public void addIndex(int index, int data) {
        if(index < 0 || index > size()) {
            //这里不一定非要抛出自定义异常,大家可以更具喜好自行设置
            throw new IndexException("index不合法: "+index);
        }
        //如果输入的位置在最前面就进行头插
        if (index == 0)
            addFirst(data);
        //如果输入的位置在链表最后就进行尾插
        if (index == size())
            addLast(data);
        //输入位置在中间,就先找到这个节点的位置
        ListNode cur = searchPrevIndex(index);
        ListNode newNode = new ListNode(data);
        //这俩步是顺序非常重要
        newNode.next = cur.next;
        cur.next = newNode;
    }
    //找到位置对应的节点
    private ListNode searchPrevIndex(int index) {
        ListNode cur = head;
        int count = 0;
        while (count != index-2) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

Node deletion

For nodes, deletion is much simpler than insertion. We still divide it into two ways to delete. One is to delete only the node that appears for the first time, and the other is to delete all the nodes that you want to delete.

Delete the first occurrence of the keyword node

We still use the initial linked list as an example. If we want to delete the third node

The first step is to directly change the pointer field of the node before the node to be deleted so that it points to the node after the node to be deleted.

In the second step, we set the pointer field of the node to be deleted to empty.

The order of these two steps cannot be wrong either, because once we first set the pointer field of the node to be deleted to empty, we will no longer be able to find the following nodes, and the linked list is equivalent to being disconnected.

We encapsulate a function to find the previous node of the node to be deleted, and then delete the target node through it

    //删除第一个关键字
    public void remove(int key) {
        if (head == null)
            return;
        if (head.val == key){
            head = head.next;
            return;
        }
        //查找val等于key的节点
        ListNode cur = findKey(key);
        if (cur == null){
            System.out.println("查无此元素,无法删除");
            return;
        }
        ListNode delNode = cur.next;
        cur.next = delNode.next;
        //cur.next =cur.next.next;
    }
    //找到要删除的元素的前一个节点
    private ListNode findKey(int key){
        ListNode cur = head;
        while (cur.next != null){
            if (cur.next.val != key) {
                cur = cur.next;
            }else{
                return cur;
            }
        }
        return null;
    }

Delete all keyword nodes

The difference from just now is that to delete a node, we first find the predecessor node and then operate through this predecessor node. To delete all keyword nodes, we need to traverse the entire linked list. Once it is a keyword, we will overwrite it. Delete, no more drawing here. After all, the whole process is equivalent to performing a single delete operation several times.

    //删除所有的关键字
    public void removeAllKey(int key) {
        if(head == null) {
            return;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        
        while (cur != null) {
            if(cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
        //除了头节点都删除完成了
        if(head.val == key) {
            head = head.next;
        }
    }

Node search

The search for node is to traverse the entire linked list. If it is found, it will returntrue, if it is not found, it will return false

    //查找是否存在
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null){
            if (cur.val == key)
                return true;
            cur = cur.next;
        }
        return false;
    }

Clearing the linked list

When the head node of the linked list is empty, we consider the linked list to be cleared.

    //清空链表
    public void clear() {
        head = null;
    }

length of linked list

Traverse the entire linked list, use a calculator to accumulate and record the number of nodes, and then return

    //求链表的长度
    public int size() {
        int count =0;
        ListNode cur = head;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }



  That’s it for this sharing. I hope my sharing can be helpful to you. I also welcome everyone’s support. Your likes are the biggest motivation for bloggers to update! If you have different opinions, you are welcome to actively discuss and exchange in the comment area, let us learn and make progress together! If you have relevant questions, you can also send a private message to the blogger. The comment area and private messages will be carefully checked. See you next time

Guess you like

Origin blog.csdn.net/m0_69519887/article/details/134733887