Java实现单向链表


链表基本的单元是节点Node。
单向链表中每个节点都由两部分组成:存储的数据和下一个节点的内存地址Node。
双向链表则在单向链表的基础上,多了一个Node,用来存储上一个节点的内存地址。
对比下面的两张图,很容易就能理解“单向”和“双向”。
单向链表(图源B站老杜)
双向链表(图源B站老杜)
图看懂了,下面我们来看看Java中如何实现单项链表。

Node类

上面说过了,每一个节点Node中都由两部分组成:下一个节点的内存地址(它也是一个节点Node)以及存储的数据。
所有一个Node类应该有两个实例变量Node next和Object data,也可以使用泛型,声明为Node<E> next和E data。

SingleLink类

一个单向链表类应该包含一个节点Node,单向链表的长度int size。在程序中我把这个节点Node命名为header(头节点)。此外,SingleLink类中还要有两个指针Node<E> last和Node<E> next,分别指向当前节点的上一个节点和下一个节点,方便程序的编写。

Test类

用于测试SingleLink中的方法,不多赘述了。

类中的方法

Node类

重写toString()、hashCode()、equals()三个方法,提供有参和无参两个构造方法,没什么好说的。

Single类

最基本的“增删改查”四个方法要有,其他的写不写都无所谓。
boolean add(E data):增加元素;
boolean remove(E data):删除元素;
void set(int index,E newData):根据下标修改元素;
E get(int index):根据下标获取元素;
boolean contains(E data):判断链表中是否包含某个元素;
boolean isEmpty():判断链表是否为空;
boolean checkIndex():由set()和get()两个方法调用,判断提供的下标是否正确。
Node<E> findLast():由add()方法调用,查找尾节点;
Node<E> node(int index):根据下标查找节点,配合checkIndex()方法使用;
void unlink(Node<E> removedNode): 让被删除的元素所在的节点removedNode的上一个节点不再指向它。

代码

代码如下,我写了好久,写到自己都晕了,如有不妥之处欢迎指出。

Node类:

import java.util.Objects;

public class Node<E> {
    E data;

    Node<E> next;

    /*---------------------------- 构造方法 ----------------------------*/
    public Node() {
    }

    public Node(E data, Node<E> next) {
        this.data = data;
        this.next = next;
    }

    /*---------------------------- 重写的方法 ----------------------------*/
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Node)) return false;
        Node<?> node = (Node<?>) o;
        return Objects.equals(data, node.data) &&
                Objects.equals(next, node.next);
    }

    @Override
    public int hashCode() {
        return Objects.hash(data, next);
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", next=" + next +
                '}';
    }
}

SingleLink类:

//每个单链表都有一个头节点和长度
public class SingleLink<E> {
    Node<E> header;

    int size;

    // 指针指向上一个节点
    Node<E> last;

    // 指针指向下一个节点
    Node<E> next;

    /**
     * 将元素添加到链表中
     * @param e 要添加的元素
     * @return 添加成功返回true,否则返回false
     */
    public boolean add(E e){
        if (null == header){ //如果头节点为空
            header = new Node<E>(e,null);
            size++;
            System.out.println("添加成功!");
            return true;
        }
        else {
            Node<E> node = findLast(header);
            node.data = e;
            node.next = null;
            last.next = node;
            size++;
            System.out.println("添加成功!");
            return true;
        }
    }

    /**
     * 查找尾节点
     * @param node 从哪个节点开始找
     * @return 返回尾节点
     */
    private Node<E> findLast(Node<E> node) {
        if (null == node){
            return new Node<E>();
        }
        last = node; // 指向尾节点的上一个节点
        return findLast(node.next);
    }

    /**
     * 从链表中删除元素
     * @param e 要删除的元素
     * @return 删除成功返回true,否则返回false
     */
    public boolean remove(E e){
        // 如果要删除的元素为空
        if(null == e) {
            for (Node<E> currentNode = header; currentNode != null;) {
                if (null == currentNode.next.data) {
                    last = currentNode;
                    next = null;
                    unlink(currentNode.next);
                    System.out.println("删除成功");
                    return true;
                }
            }
        }
        else{
            // 重置指针
            last = null;
            next = null;
            for (Node<E> currentNode = header; currentNode != null;){
                if(e.equals(currentNode.data)){
                    unlink(currentNode);
                    System.out.println("删除成功");
                    return true;
                }
                last = currentNode; // 指针指向当前节点的上一个节点
                currentNode = currentNode.next;
                if (currentNode == null){
                    next = null;
                }
                else {
                    next = currentNode.next; // 指针指向当前节点的下一个节点
                }
            }
        }
        System.out.println("删除失败,元素不存在");
        return false;
    }

    /**
     * 让被删除的元素所在的节点removedNode的上一个节点不再指向它
     * @param removedNode 被删除的元素所在的节点
     */
    private void unlink(Node<E> removedNode){
        // removedNode一定不为空
        if (last == null && removedNode.next != null){ //如果被删除的节点是头节点
            header = removedNode.next; // 头节点被删除了,它的下一个节点成为了头节点
        }
        else if (null == removedNode.next){ // 如果被删除的元素所在的节点是尾节点
            last = null;
        }
        else {
            // 如果不是尾节点,就让当前节点的”上一个节点的next“指向当前节点的下一个节点。
            last.next = removedNode.next;
        }
        removedNode.data = null;
        size--;
    }

    /**
     * 查找链表中是否包含指定的元素
     * @param data 要查找的元素
     * @return 如果包含要查找的元素则返回true,否则返回false
     */
    public boolean contains(E data){
        if (null == data){
            return true;
        }
        else {
            for (Node<E> currentNode = header; currentNode != null; currentNode = currentNode.next) {
                if (data.equals(currentNode.data)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 修改节点中的元素
     * @param index 被修改的元素的下标
     * @param newData 修改为哪个元素
     */
    public void set(int index,E newData){
        if (checkIndex(index)) { // 下标一定正确
            node(index).data = newData;
        }
    }

    /**
     * 根据下标取出元素
     * @param index 要取出的元素所在的下标
     * @return 返回要取出的元素
     * @throw 如果下标不正确就抛出下标越界异常
     */
    public E get(int index){
        if (checkIndex(index)){
            return node(index).data;
        }
        else {
            throw new IndexOutOfBoundsException("下标【"+index+"】超过了链表的长度【"+size+"】");
        }
    }

    /**
     * 根据下标查找节点
     * @param index 提供的下标
     * @return 返回下标对应的节点
     */
    private Node<E> node(int index){
        // 下标一定正确
        Node<E> gettedNode = null;
        if (0 == index){
            gettedNode = header;
        }
        else if (index == size){
            gettedNode = last;
        }
        else{
            Node<E> current = null;
            gettedNode = header;
            for (int count = 0; count < index; count++) {
                current = gettedNode;
                if (null == gettedNode){
                    gettedNode = current;
                    break;
                }
                gettedNode = gettedNode.next;
            }
        }
        return gettedNode;
    }

    /**
     * 检查下标是否正确
     * @param index 要检查的下标
     * @return 下标正确返回true,否则返回false
     */
    private boolean checkIndex(int index){
        if (index<0 || index >=size){
            return false;
        }
        return true;
    }

    /**
     * 检查链表是否为空
     * @return 为空则返回true,否则返回flase
     */
    public boolean isEmpty(){
        return size == 0;
    }
}

测试类就不放出来了。
感觉挺乱的。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值