【链表知识】


一、什么是链表?

链表是⼀种物理存储单元上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表由⼀系列结点(链表中每⼀个元素称为结点)组成,结点可以在运⾏时动态⽣成。链表的存储⽅式是,每个位置上存储的元素分为两个部分,⼀部分是当前存储的元素的值,另外⼀部分是存储⼀下⼀个元素的内存地址。
在这里插入图片描述

二、链表的存储

1,存储方式
通过存储下个元素的地址,这样避免了数组需要连续存储空间的问题,虽然在内存中链表存储的元素是不连续的,但是也是由于这个问题,链表存储元素才被拆分为了两部分。这样才能通过内部维护的引⽤,知道整个链表的存储的位置关系。
在这里插入图片描述

2,链表的分类
在这里插入图片描述

三、用代码实现链表

代码如下(示例):


/**
 *  单链表实现
 * @param <E>
 */
public class LinkedList<E> {

    private final Node<E> DUMMY_FRONT = new Node<>(null,null); // 当前链表的首元素 虚拟头结点
    private int size; // 存储链表中元素的个数

    public LinkedList(){

    }


    /**
     * 添加元素
     * 默认情况下添加元素到链表头位置
     * @param e
     */
    public void addFirst(E e){
        add(0,e);
    }

    /**
     * 添加元素
     */
    public void add(E e){
        addLast(e);
    }

    /**
     * 添加元素到末尾
     * @param e
     */
    public void addLast(E e){
        add(size,e);
    }

    /**
     * 在指定的索引位置上添加元素
     * @param index
     * @param e
     */
    public void add(int index,E e){
        // 判定索引是否越界
        checkIndex(index);
        // 获取虚拟头结点
        Node pre = DUMMY_FRONT;
        // 获取待添加元素的前一个元素
        for (int i = 0; i < index; i++)
            pre = pre.next;
        pre.next = new Node<>(e, pre.next);
        //维护元素的个数
        size++;

    }

    /**
     * 根据指定索引删除元素
     * @param index
     * @return
     */
    public E remove(int index){
        checkBounds(index);
        // 声明待删除节点的前一个元素
        Node<E> pre = DUMMY_FRONT;
        for (int i = 0;i<index;i++)
            pre = pre.next;
        // 获取当前待删除节点
        Node<E> cur = pre.next;

        pre.next = cur.next;
        // 将待删除节点的下一个节点置为null
        cur.next = null;
        // 维护size
        size--;
        return cur.item;
    }

    /**
     * 删除首元素
     * @return
     */
    public E removeFirst(){
        return remove(0);
    }
    /**
     * 删除尾元素
     * @return
     */
    public E removeLast(){
        return remove(size-1);
    }

    /**
     * 删除指定的元素
     * @param e
     */
    public void remove(E e){
        int index = indexOf(e);
        remove(index);
    }

    /**
     * 修改指定位置上的元素为对应的指定元素
     * 返回修改之前的元素
     * @param e
     * @param index
     */
    public E set(E e,int index){
        // 判定索引是否有效
        checkBounds(index);
        // 查找需要修改的元素
        Node<E> cur = DUMMY_FRONT.next;
        for (int i = 0;i<index;i++)
            cur = cur.next;
        E retItem = cur.item;
        cur.item = e;
        return retItem;
    }

    /**
     * 确定索引是否有效
     * @param index
     */
    private void checkBounds(int index) {
        if (index<0||index>=size)
            throw new IllegalArgumentException("the linkedlist size:"+size+",index:"+index);
    }


    /**
     * 确定索引是否有效
     * @param index
     */
    private void checkIndex(int index) {
        if (index<0||index>size)
            throw new IllegalArgumentException("the linkedlist size:"+size+",index:"+index);
    }


    // LinkedList存储的元素就是一个个的Node节点对象
    private static class Node<E>{
        E item; // 存储的元素
        Node next; // 下一个节点对象
        public Node(E item,Node next){
            this.item = item;
            this.next = next;
        }
    }

    /**
     * 获取集合中的元素个数
     * @return
     */
    public int getSize(){
        return size;
    }

    /**
     * 获取集合是否为空
     * @return
     */
    public boolean isEmpty(){
        return size==0;
    }


    /**
     * 查询指定索引位置上的元素
     * @param index
     * @return
     */
    public E get(int index){
        checkBounds(index);
        Node<E> cur = DUMMY_FRONT.next;
        for (int i = 0;i<index;i++)
            cur = cur.next;
        return cur.item;
    }

    /**
     * 查询链表中的首元素
     * @return
     */
    public E getFirst(){
        return get(0);
    }

    /**
     * 查询链表中的尾元素
     * @return
     */
    public E getLast(){
        return get(size-1);
    }

    /**
     * 查询集合总是否包含某个元素
     * @param e
     * @return
     */
    public boolean contains(E e){
        return indexOf(e) != -1;
    }

    /**
     * 查询元素对应的索引位置
     * @param e
     * @return
     */
    public int indexOf(E e) {
        Node<E> node = DUMMY_FRONT.next;
        if (e == null){
            for (int i = 0;i<size;i++){
                if (e==node.item)
                    return i;
                node = node.next;
            }

        }else{
            for (int i = 0;i<size;i++){
                if (e.equals(node.item))
                    return i;
                node = node.next;
            }
        }
        return -1;
    }

    public String toString(){
        StringBuilder retStr = new StringBuilder();
        retStr.append("size:"+size+"{");
        Node<E> node = DUMMY_FRONT.next;
        for (int i = 0;i<size;i++){
            retStr.append(node.item);
            if (i != size-1)
                retStr.append("->");
            if (node.next != null)
                node = node.next;
        }
        retStr.append("}");
        return retStr.toString();
    }


}


总结

不同的应用场景,应该使用不同的数据结构,链表在需要无限递增的数据的时候,应该比较好用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值