轻松拿捏链表(java)

简介: 轻松拿捏链表(java)

链表

文章目录

顺序表的缺陷

上篇文章我们介绍了顺序表,顺序表的底层是数组,当我们在进行数组的添加和删除时,需要将后序元素依次移动位置,使得时间复杂度为O(N),效率比较低。因此:java引进了LinkedList,即链表结构。

概念

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。简单来说,链表不会按照顺序表的存储结构去存储数据,而是在每一个节点里存放下个节点的地址 。

结构

764e0a626553171bb6f47c139d76c884.png

这是一个简单的单向链表的结点,val表示的是当前结点要存的值,next表示引用,用来存放下一个结点的地址。

由于Java中不存在指针,所以每一个结点通常为一个类,而next是一个结点类实例的引用

23047397322b604e2c1f65210fa23eae.png

创建链表

我们介绍了链表,可是该如何创建一个链表呢?

在Java中我们用类来表示链表的结构

public static class Node {
    int value;//值
    Node next;//结点引用,引用当前节点的下一节点
    public Node(int value) {
        this.value = value;
    }
}

结点的插入

在链表中插入一个节点时,一般有3种方法:头插、尾插、指定位置插入

头插

在链表的起始位置插入结点。

public void addFirst(int data) {
    Node node = new Node(data);
    node.next = head;
    head = node;
}

代码很简单,思路也很简单

a69eeaebea39ba0e6431a49426af58b6.png

尾插

与头插相似,只是在最后一个结点的位置插入新结点。

cae90120e12d1a46acc7c947ffda8e2b.png

//尾插法
public void addLast(int data) {
    //创建新链表
    Node node = new Node(data);
    //判断链表是否为空
    if (head == null) {
        addFirst(data);
        return;
    }
    //后面节点
    Node current = head;
    while (current.next != null) {
        current = current.next;
    }
    current.next = node;
}

指定位置插入

9c2df8a7a37436943b6e171d3b0acd3e.png

private void checkRange(int index) {
    if (index < 0 || index > size()) {
        throw new IndexOutOfBoundsException("下标非法 i=" + index);
    }
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index, int data) {
    //检查index合法性
    checkRange(index);
    //首位
    if (index == 0) {
        addFirst(data);
        return;
    }
    //尾部
    if (index == size()) {
        addLast(data);
        return;
    }
    //中间位置
    //首先找到所找节点的上一节点
    Node preNode = findPreNode(index);
    //创建新节点
    Node node = new Node(data);
    //用node.next引用preNode.next
    node.next = preNode.next;
    //preNode.next引用新节点
    preNode.next = node;
}
private Node findPreNode(int index) {
    //创建临时节点
    Node current = head;
    for (int i = 0; i < (index - 1); i++) {
        current = current.next;
    }
    return current;
}

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

遍历数组即可

public boolean contains(int key) {
    //遍历链表
    Node current = head;
    while (current != null) {
        if (current.value == key) {
            return true;
        }
        current = current.next;
    }
    return false;

删除元素

这个会出现2种情况:删除第一次出现的关键字key和删除出现的所有关键字key

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

public void remove(int key) {
    //若为头节点
    if (head.value == key) {
        head = head.next;
        return;
    }
    //不是头节点
    Node current = head;
    while (current != null) {
        if (current.next.value == key) {
            current.next = current.next.next;
            return;
        }
        current = current.next;
    }
}

删除出现的所有关键字key

public void removeAllKey(int key) {
    //判断链表是否为空
    if (head == null) {
        return;
    }
    //出现在首位
    if (head.value == key) {
        head = head.next;
    }
    //不为空
    //两个临时变量
    Node current = head.next;
    Node prev = head;
    while (current != null) {
        if (current.value == key) {
            prev.next = current.next;
        } else {
            prev = current;
        }
        current = current.next;
    }
}

获取链表长度

public int size() {
    //遍历每个节点,统计节点个数
    Node current = head;
    int count = 0;
    while (current != null) {
        count++;
        current = current.next;
    }
    return count;
}

清空链表

public void clear() {
    //手动将每个节点置空
    Node current = head;
    while (current != null) {
        //得到下一个节点
        Node nextNode = current.next;
        current.next = null;
        current = nextNode;
    }
    //最简便方法
    head = null;
}

链表打印

public void display() {
    //创建一个临时变量引用head
    Node current = head;
    StringBuilder sb = new StringBuilder();
    sb.append("[");
    while (current != null) {
        sb.append(current.value);
        //最后一个节点不加空格
        if (current.next != null) {
            sb.append(" ");
        }
        current = current.next;
    }
    sb.append("]");
    System.out.println(sb.toString());
}

以上就是链表的基本操作了,下面是全部源码

import java.util.Stack;
public class SingleLinkedList {
    public static class Node {
        int value;
        Node next;
        public Node(int value) {
            this.value = value;
        }
    }
    public Node head;
    //头插法
    public void addFirst(int data) {
        Node node = new Node(data);
        node.next = head;
        head = node;
    }
    //尾插法
    public void addLast(int data) {
        //创建新链表
        Node node = new Node(data);
        //判断链表是否为空
        if (head == null) {
            addFirst(data);
            return;
        }
        //后面节点
        Node current = head;
        while (current.next != null) {
            current = current.next;
        }
        current.next = node;
    }
    private void checkRange(int index) {
        if (index < 0 || index > size()) {
            throw new IndexOutOfBoundsException("下标非法 i=" + index);
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        //检查index合法性
        checkRange(index);
        //首位
        if (index == 0) {
            addFirst(data);
            return;
        }
        //尾部
        if (index == size()) {
            addLast(data);
            return;
        }
        //中间位置
        //首先找到所找节点的上一节点
        Node preNode = findPreNode(index);
        //创建新节点
        Node node = new Node(data);
        //用node.next引用preNode.next
        node.next = preNode.next;
        //preNode.next引用新节点
        preNode.next = node;
    }
    private Node findPreNode(int index) {
        //创建临时节点
        Node current = head;
        for (int i = 0; i < (index - 1); i++) {
            current = current.next;
        }
        return current;
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        //遍历链表
        Node current = head;
        while (current != null) {
            if (current.value == key) {
                return true;
            }
            current = current.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        //若为头节点
        if (head.value == key) {
            head = head.next;
            return;
        }
        //不是头节点
        Node current = head;
        while (current != null) {
            if (current.next.value == key) {
                current.next = current.next.next;
                return;
            }
            current = current.next;
        }
    }
    //删除所有值为key的节点
    public void removeAllKey(int key) {
        //判断链表是否为空
        if (head == null) {
            return;
        }
        //出现在首位
        if (head.value == key) {
            head = head.next;
        }
        //不为空
        //两个临时变量
        Node current = head.next;
        Node prev = head;
        while (current != null) {
            if (current.value == key) {
                prev.next = current.next;
            } else {
                prev = current;
            }
            current = current.next;
        }
    }
    //得到单链表的长度
    public int size() {
        //遍历每个节点,统计节点个数
        Node current = head;
        int count = 0;
        while (current != null) {
            count++;
            current = current.next;
        }
        return count;
    }
    public void clear() {
        //手动将每个节点置空
        Node current = head;
        while (current != null) {
            //得到下一个节点
            Node nextNode = current.next;
            current.next = null;
            current = nextNode;
        }
        //最简便方法
        head = null;
    }
    public void display() {
        //创建一个临时变量引用head
        Node current = head;
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        while (current != null) {
            sb.append(current.value);
            //最后一个节点不加空格
            if (current.next != null) {
                sb.append(" ");
            }
            current = current.next;
        }
        sb.append("]");
        System.out.println(sb.toString());
        /*while(current!=null){
            System.out.print(current.value+" ");
            current=current.next;
        }
        System.out.println();*/
    }
    public void printList() {
        if (head == null) {
            System.out.println("");
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node current = head;
        while (current != null){
            stack.push(current);
            current=current.next;
        }
        while(!stack.empty()){
            System.out.print(stack.pop().value+" ");
        }
        System.out.println();
    }
}


目录
相关文章
|
1月前
|
Java
java数据结构,双向链表的实现
文章介绍了双向链表的实现,包括数据结构定义、插入和删除操作的代码实现,以及双向链表的其他操作方法,并提供了完整的Java代码实现。
java数据结构,双向链表的实现
|
13天前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
18 3
|
4月前
|
Java
环形数组链表(java)
环形数组链表(java)
|
2月前
|
存储 Java
|
2月前
|
存储 Java
java实现单链表的创建、增、删、改、查
这篇文章详细介绍了Java中如何实现单链表的创建以及对单链表进行增加、删除、修改、查询等操作的方法,并提供了相应的代码示例。
java实现单链表的创建、增、删、改、查
|
2月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
81 0
|
2月前
|
存储 Java
【Java集合类面试十】、HashMap中的循环链表是如何产生的?
在多线程环境下,HashMap在扩容时如果发生条件竞争,元素的插入顺序可能形成循环链表,导致死循环。
|
2月前
|
存储 Java
java实现双向链表的增删改查
这篇文章展示了如何在Java中实现双向链表的增加、删除、修改和查询操作,并通过代码示例演示了在双向链表中存储和操作学生信息的过程。
|
2月前
|
算法 Java
LeetCode初级算法题:环形链表+排列硬币+合并两个有序数组java解法
LeetCode初级算法题:环形链表+排列硬币+合并两个有序数组java解法
50 0
|
2月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
40 0