美文网首页
数据结构-单向链表

数据结构-单向链表

作者: 我阿郑 | 来源:发表于2021-12-25 09:00 被阅读0次

一、线性表

线性表可以分为:

  • 顺序表(数组)
  • 链表(单向链表、双向链表、循环链表)

二、链表

链表是一种链式存储的线性表,所有元素的内存地址不一定是连续的;

三、链表(LinkedList)接口设计

由于链表的大部分接口和动态数组一致,抽取出一个共同的List 接口

public interface List<E> {
    static final int ELEMENT_NOT_FOUND = -1;
    /**
     * 清除所有元素
     */
    void clear();

    /**
     * 元素的数量
     * @return
     */
    int size();

    /**
     * 是否为空
     * @return
     */
    boolean isEmpty();

    /**
     * 是否包含某个元素
     * @param element
     * @return
     */
    boolean contains(E element);

    /**
     * 添加元素到尾部
     * @param element
     */
    void add(E element);

    /**
     * 获取index位置的元素
     * @param index
     * @return
     */
    E get(int index);

    /**
     * 设置index位置的元素
     * @param index
     * @param element
     * @return 原来的元素ֵ
     */
    E set(int index, E element);

    /**
     * 在index位置插入一个元素
     * @param index
     * @param element
     */
    void add(int index, E element);

    /**
     * 删除index位置的元素
     * @param index
     * @return
     */
    E remove(int index);

    /**
     * 查看元素的索引
     * @param element
     * @return
     */
    int indexOf(E element);
}

单向链表实现(SingleLinkedList)

image.png

` java

public class SingleLinkedList<E> extends AbstractList<E> {

private Node<E> first;

// 链表中的节点
private static class Node<E> {
    E element; // 节点元素
    Node<E> next; // 节点指向下一个节点

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

/**
 * 根据索引找到节点对象
 */
private Node<E> node(int index) {
    rangeCheck(index);
    Node<E> node = first;
    for (int i = 0; i < index; i++) {
        node = node.next;
    }
    return node;
}

@Override
public void clear() {
          // next 不需要设置为 null,因为 first 指向了 null,后面的 Node 没有被指向,在 Java 中会自动被垃圾回收。
    size = 0;
    first = null;
}

@Override
public E get(int index) {
    return node(index).element;
}

@Override
public E set(int index, E element) {
    E old = node(index).element;
    node(index).element = element;
    return old;
}

@Override
public void add(int index, E element) {
    /*
     * 最好:O(1)
     * 最坏:O(n)
     * 平均:O(n)
     */
    rangeCheckForAdd(index);
    if (index == 0) { // 给空链表添加第一个元素的情况
        first = new Node<>(element, first);
    } else {
        Node<E> prev = node(index - 1);
        prev.next = new Node<>(element, prev.next);
    }
    size++;
}

@Override
public E remove(int index) {
    rangeCheck(index);
    Node<E> node = first;
    if (index == 0) { // 删除第一个元素是特殊情况
        first = first.next;
    } else {
        Node<E> prev = node(index - 1); // 找到前一个元素
        node = prev.next; // 要删除的元素
        prev.next = node.next; // 删除元素
    }
    size--;
    return node.element;
}

@Override
public int indexOf(E element) {
    // 有个注意点, 如果传入元素为null, 则不能调用equals方法, 否则会空指针
    // 因此需要对元素是否为null做分别处理
    if (element == null) {
        Node<E> node = first;
        for (int i = 0; i < size; i++) {
            if (node.element == null) return i;
            node = node.next;
        }
    } else {
        Node<E> node = first;
        for (int i = 0; i < size; i++) {
            if (node.element.equals(element)) return i;
            node = node.next;
        }
    }
    return ELEMENT_NOT_FOUND;
}

@Override
public String toString() {
    StringBuilder string = new StringBuilder();
    string.append("[size=").append(size).append(", ");
    Node<E> node = first;
    for (int i = 0; i < size; i++) {
        if (i != 0) {
            string.append(", ");
        }
        string.append(node.element);
        node = node.next;
    }
    string.append("]");
    return string.toString();
}

}
`

带虚拟头结点的单向链表

为了操作方便,统一所有节点的处理逻辑,可以在最前面增加一个没有实际含义的 虚拟头结点 ,这个虚拟头结点不存储有效数据

image.png

不论是带 虚拟头结点的链表还是不带 虚拟头结点的链表,头指针first都指向链表中的第一个结点

  • 如果该链表有虚拟头结点,则头指针first指向虚拟头结点
  • 如果没有虚拟头结点,则头指针first指向链表的第一个节点

虚拟头结点 的单向链表与普通单向链表代码类似:但是 add、reomove 略有不同

` java
/// 增加一个虚拟头结点
public class SingleLinkedList2<E> extends AbstractList<E> {

private Node<E> first;

//**********************************
public SingleLinkedList2() { // 初始化一个虚拟头结点
    first = new Node<>(null, null);
};
//**********************************

private static class Node<E> {
    E element;
    Node<E> next;

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

@Override
public void clear() {
    size = 0;
    first = null;
}

@Override
public E get(int index) {
    return node(index).element;
}

@Override
public E set(int index, E element) {
    E old = node(index).element;
    node(index).element = element;
    return old;
}

@Override
public void add(int index, E element) {
    rangeCheckForAdd(index);
    Node<E> prev = (index == 0) ? first : node(index - 1);
    prev.next = new Node<>(element, prev.next);
    size++;
}

@Override
public E remove(int index) {
    rangeCheck(index);

    Node<E> prev = (index == 0) ? first : node(index - 1);
    Node<E> node = prev.next;
    prev.next = node.next;

    size--;
    return prev.element;
}

@Override
public int indexOf(E element) {
    // 有个注意点, 如果传入元素为null, 则不能调用equals方法, 否则会空指针
    // 因此需要对元素是否为null做分别处理
    if (element == null) {
        Node<E> node = first;
        for (int i = 0; i < size; i++) {
            if (node.element == null) return i;
            node = node.next;
        }
    } else {
        Node<E> node = first;
        for (int i = 0; i < size; i++) {
            if (node.element.equals(element)) return i;
            node = node.next;
        }
    }
    return ELEMENT_NOT_FOUND;
}

/**
 * 根据索引找到节点
 * 
 * @param index
 * @return
 */
private Node<E> node(int index) {
    rangeCheck(index);
    Node<E> node = first.next;
    for (int i = 0; i < index; i++) {
        node = node.next;
    }
    return node;
}

@Override
public String toString() {
    StringBuilder string = new StringBuilder();
    string.append("[size=").append(size).append(", ");
    Node<E> node = first.next;
    for (int i = 0; i < size; i++) {
        if (i != 0) {
            string.append(", ");
        }
        string.append(node.element);
        node = node.next;
    }
    string.append("]");
    return string.toString();
}

}
`

Leetcode算法题

1、删除链表中的节点

2、反转链表

3、环形链表

4、移除链表元素

5、删除排序链表中的重复元素

6、链表的中间结点

相关文章

  • 算法与数据结构知识汇总(二、链表)

    1、概念 2、链表的数据结构 单向链表的数据结构如下图: 上图数据结构为单向链表,简称单链表,该数据结构由若干个节...

  • 2019-12-04 Java-LinkedList源码解读

    @TOC 1、链表数据结构 链表分为单向链表和双向链表,他们的区别在于,单向链表只能单向寻址,而双向链表可以双向寻...

  • 用Java写单向链表

    数据结构—单向链表 为了巩固自己的基础知识,这次就用 Java 来写一个单向链表。问:什么是单向链表?首先链表是数...

  • 数据结构 | 其二 链表

    冰河winner - 数据结构之链表 2.1 单向链表 数据结构(一) 单链表的实现-JAVA 2.2 双端链表 ...

  • 线性表-单向循环链表

    单向循环链表 单向循环链表示意图如下: 数据结构定义(同普通链表) 单向循环链表初始化与赋值 在上面循环遍历查找尾...

  • 数据结构笔记

    数据结构课程概览 ================== 1.顺序表 2.链表:单链表,单向循环链表,双链表...

  • 链表反转

    概述 链表反转是非常经典的面试题,要实现此功能,需先实现链表的数据结构。 链表类 获得单向链表方法 输出单向链表方...

  • 总结

    Android篇 数据结构与算法顺序表 - ArrayList源码链表 - 单向链表、双向链表 - LinkedL...

  • 2018-03-26

    数据结构:单向链表 #include#include//实现一个简单的单向链表 (不带头节点的链表,只有一个头指针...

  • 链表

    一种线性数据结构。包含单向链表和双向链表。 单向链表的结构,操作(插入、删除和遍历)及其时间复杂度: 通常使...

网友评论

      本文标题:数据结构-单向链表

      本文链接:https://www.haomeiwen.com/subject/wnyvxrtx.html