初识算法与数据结构(1)——手写LinkedList,优化ArrayList

1.定义List接口


package com.huawei.list;

/**
 * 功能描述
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public interface List<E> {
    int ELEMENT_NOT_FOUND = -1;

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

    int size();

    boolean isEmpty();

    void add(E element); // 添加元素到最后面

    E get(int index); // 返回index位置对应的元素

    E set(int index, E element); // 设置index位置的元素

    void add(int index, E element); // 往index位置添加元素

    E remove(int index); // 删除index位置对应的元素

    int remove(E element) ;

    int indexOf(E element); // 查看元素的位置

    void clear(); // 清除所有元素
}

2.定义抽象父类

package com.huawei.list;

/**
 * 功能描述:抽象类,对外界不可见,用来抽取list公共属性和实现代码
 *
 * @author 
 * @since 2022-11-03
 */
public abstract class AbstractList <E> implements List<E> {

    protected int size;

    @Override
    public boolean contains(E element) {
        return indexOf(element) != ELEMENT_NOT_FOUND;
    }

    @Override
    public void add(E element) {
        add(size, element);
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public int remove(E element) {
        int index = indexOf(element);
        remove(index);
        return index;
    }

    protected void checkRangeIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("index error:" + index);
        }
    }

    protected void checkRangeIndexForAdd(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("index error:" + index);
        }
    }
}

3.LinkedList实现



package com.huawei.list;

/**
 * 功能描述
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public class LinkedList<E> extends AbstractList<E> implements List<E> {
    /**
     * 链表设计思路:
     * 1.链表是层层递进,没有初始容量
     * 2。链表需要有一个节点对象(Node)来记录当前元素的值,以及下一个Node
     * 3.链表需要有一个头节点
     */
    // 定义静态内部类
    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> first;

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

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    @Override
    public void add(int index, E element) {
        checkRangeIndexForAdd(index);
        // 在index位置添加元素
        // 思路:
        // 1.在index位置添加元素,有两种思路
        // 1)找到index位置的节点,然后把新节点的next指向index位置的节点
        // 2) 再把index节点上一个节点的next指向当前节点,这个又需要调用找节点的方法,因此不采取
        // 或者
        // 1)找到index-1位置的节点
        // 2)新节点的next指向index-1位置的next
        // 3) index-1位置的next指向新节点

        // 采取第二种方法:
        if (index == 0) {
            // index = 0,代表在头部添加新节点
            // 那么新节点是first,新节点的next是之前first,那么:
            first = new Node<>(element, first);
        } else {
            // 找到index位置的上一个节点
            Node<E> prev = node(index - 1);
            // 新建节点
//            Node<E> newNode = new Node<>(element, prev.next);
            // index - 1 位置的next指向新节点
//            prev.next = newNode;
            prev.next = new Node<>(element, prev.next);
        }
        size ++;
    }

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    @Override
    public E remove(int index) {
        checkRangeIndex(index);
        // 思路:
        // 1)找到当前节点
        // 2)当前节点的前一个的节点的next指向当前节点的next
        // 如果直接找当前节点,那么当找节点方法又要执行两次
        // 因此这里直接找当前要删除节点的前一个节点
        Node<E> removeNode = first;
        if (index == 0) {
            // index = 0,则是删除头节点,那么直接让first指向它的next即可
            first = removeNode.next;
        } else {
            Node<E> node = node(index - 1);
            removeNode = node.next;
            node.next = removeNode.next;
        }
        size -- ;
        return removeNode.element;
    }

    @Override
    public int indexOf(E element) {
        Node<E> node = first;
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node.element == null)
                    return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

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

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

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    // 根据当前index获取Node元素
    private Node<E> node(int index) {
        // index合理性检查
        checkRangeIndex(index);
        // 获取当前Node,必须从头节点开始,一层一层往后面找,要找到index处的node节点,必须找到它的前一个节点
        // 因此从first节点开始,一直node.next.next...直到找到index-1位置的节点,这里就涉及到循环
        // 那么循环多少次?index = 0,则node=first,循环0次;index = 1,node = first.next,循环1次;index = 2,node = first.next.next,循环2次,
        // 依次类推,index = n , 则循环n次,下标从0开始
        // 找index-1次即可找到当前节点node
        Node<E> node = first;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }

}



4.LinkedList虚拟头结点



package com.huawei.list;

/**
 * 功能描述:引入虚拟头节点
 *
 * @author
 * @since 2022-11-03
 */
public class LinkedList2<E> extends AbstractList<E> implements List<E> {
    /**
     * 链表设计思路:
     * 1.链表是层层递进,没有初始容量
     * 2。链表需要有一个节点对象(Node)来记录当前元素的值,以及下一个Node
     * 3.链表需要有一个头节点
     */
    // 定义静态内部类
    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> first;

    public LinkedList2() {
        // 添加虚拟头结点
        this.first = new Node<>(null,null);
    }

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

    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

    @Override
    public void add(int index, E element) {
        checkRangeIndexForAdd(index);
            // 找到index位置的上一个节点
        Node<E> prev = index == 0 ? first : node(index - 1);
        prev.next = new Node<>(element, prev.next);
        size ++;
    }

    @Override
    public E remove(int index) {
        checkRangeIndex(index);
        // 思路:
        // 1)找到当前节点
        // 2)当前节点的前一个的节点的next指向当前节点的next
        // 如果直接找当前节点,那么当找节点方法又要执行两次
        // 因此这里直接找当前要删除节点的前一个节点
        Node<E> prev = index == 0 ? first : node(index - 1);
        Node<E> removeNode = prev.next;
        prev.next = removeNode.next;
        size --;
        return removeNode.element;
    }

    @Override
    public int indexOf(E element) {
        Node<E> node = first.next;
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node.element == null)
                    return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

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

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

    // 根据当前index获取Node元素
    private Node<E> node(int index) {
        // index合理性检查
        checkRangeIndex(index);
        Node<E> node = first.next;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }

}


5.ArrayList实现




package com.huawei.list;

/**
 * 功能描述
 *
 * @author 
 * @since 2022-11-03
 */
public class ArrayList<E> extends AbstractList<E> implements List<E> {
    /**
     * 设计思路:
     * 1、动态数组首先得有数组
     * 2、了解数组的特性,是一块连续的内存空间,当在内存中开辟空间以后,那一块空间本身就不能变动了
     * 3、数组的容量并不代表当前数据量
     * 4、注意对象类型数据的内存管理与比较方式
     */
    /**
     * 完成功能:
     * 1)动态数组大小 size
     * 2)数组是否为空 isEmpty
     * 3)是否包含某个元素
     * 4)在末尾添加元素
     * 5)获取某个索引的元素
     * 6)设置某个索引的元素
     * 7)在某个索引位添加元素
     * 8)根据元素本身移除元素
     * 9)根据元素索引移除元素
     * 10)找到某个元素的索引位置
     * 11)清空动态数组
     * 这些方法中,最重要的方法就是:基础添加与基础删除方法
     * 需要考虑问题:
     * 1)添加扩容问题,扩容1.5倍
     * 2)toString,采用StringBuilder拼接
     * 3)对象clear时候,注意内存管理,对象引用置为空,但是数组要保留
     */

    private E[] elements;

    private static final int DEFAULT_CAPACITY = 10;

    public ArrayList(int capacity) {
        capacity = capacity > DEFAULT_CAPACITY ? capacity : DEFAULT_CAPACITY;
        elements = (E[]) new Object[capacity];
    }

    public ArrayList() {
        this(DEFAULT_CAPACITY);
    }

    @Override
    public E get(int index) {
        // 校验索引合理性
        checkRangeIndex(index);
        return elements[index]; // O(1)
    }

    @Override
    public E set(int index, E element) {
        // 校验索引合理性
        checkRangeIndex(index);
        E oldElement = elements[index];
        elements[index] = element; // O(1)
        return oldElement;
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,往最后添加元素 O(1)
    // 最坏情况复杂度,往0添加元素,O(size)--> O(n),数组的挪动
    // 平均情况复杂度,(1+2+3+...+n)/n  -->O(n)
    @Override
    public void add(int index, E element) {
        // 校验索引合理性
        checkRangeIndexForAdd(index);
        // 考虑扩容问题,因为是添加元素,所以容量必须得是size+1才行
        ensureCapacity(size + 1);
        // 添加思路:
        // 在index位置添加元素的本质是:index位置的元素往后移,即index位置的元素放到index+1的位置,然后将当前元素放在index的位置
        // 但是为了避免元素覆盖,必须先移最后面的元素,也就是说,必须先把size-1位置的元素移到size上面
        // 所以循环应该先从size-1开始到index结束
        for (int i = size - 1; i >= index; i--) {
            elements[i + 1] = elements[i];
        }
        // 或者
//        for (int i = size; i > index; i--) {
//            elements[i] = elements[i - 1];
//        }
        elements[index] = element;
        size++;
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,往最后添加元素 O(1)
    // 最坏情况复杂度,往0添加元素,O(size)--> O(n)
    // 平均情况复杂度, 均摊复杂度--> O(1),均摊等于最好
    // 什么时候使用均摊复杂度?经过连续的多次复杂度比较低的情况以后,出现个别复杂度比较高的情况
    private void ensureCapacity(int capacity) {
        int oldCapacity = elements.length;
        // 如果之前的容量大于等于或者size+1,代表原来容量够用,直接return
        if (oldCapacity >= capacity) {
            return;
        }
        // 如果不够用,则扩容,java中官方扩容大小是1.5倍,因此这里也扩容1.5倍,避免频繁扩容,损耗CPU
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 由于数组是一块连续的空间,因此扩容只能new一个新的数组
        E[] newElements = (E[]) new Object[newCapacity];
        // 数据拷贝
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;

        System.out.println("数组扩容:" + oldCapacity +"-->" + newCapacity);
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,删除最后添加元素 O(1)
    // 最坏情况复杂度,删除0位置元素,O(size)--> O(n)
    // 平均情况复杂度,(1+2+3+...+n)/n  -->O(n)
    @Override
    public E remove(int index) {
        // 校验索引合理性
        checkRangeIndex(index);
        // 删除思路:
        // 移除某个index位置的元素,本质就是数组中,index后面的元素往前移,覆盖前面的元素
        // 由于是后面的覆盖前面的,那必定是从index后面一个元素开始,逐个覆盖前面的元素
        // 因此,实现思路:1.循环覆盖 2.如果要删除index位置的元素,那么应该是index + 1覆盖index,一直到size-1 覆盖size-2
        // 由于内存管理的原因,size-1位的元素需要置为null
        E oldElement = elements[index];
        for (int i = index + 1; i < size; i++) {
            elements[i - 1] = elements[i];
        }
        // size -- ;
        // elements[size - 1] = null; 代码合并
        elements[--size] = null;
        // 缩容和扩容原理一样,申请新的内存空间
        trimCapacity();
        return oldElement;
    }

    // 缩容
    private void trimCapacity() {
        int oldCapacity = elements.length;
        int newCapacity = oldCapacity >> 1;
        if(size >= newCapacity || newCapacity < DEFAULT_CAPACITY) return;
        // 由于数组是一块连续的空间,因此缩容只能new一个新的数组
        E[] newElements = (E[]) new Object[newCapacity];
        // 数据拷贝
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;

        System.out.println("数组缩容:" + oldCapacity +"-->" + newCapacity);
    }

    @Override
    public int indexOf(E element) {
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (elements[i] == null)
                    return i;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(elements[i]))
                    return i;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

    @Override
    public void clear() {
        // 由于直接将size = 0,对象还滞留在内存中,因此这里还需要清理无用的对象
        for (int i = 0; i < size; i++) {
            elements[i] = null;
        }
        // 缩容,申请新的内存空间
        if(elements.length > DEFAULT_CAPACITY){
            elements = (E[]) new Object[DEFAULT_CAPACITY];
        }
        size = 0;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("size:" + size + ",[");
        for (int i = 0; i < size; i++) {
            if (i != 0)
                builder.append(",");
            builder.append(elements[i]);
        }
        builder.append("]");
        return builder.toString();
    }
}


6.ArrayList采用双端队列思路优化

package com.wangzi.list;

/**
 * 功能描述
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public class ArrayList2<E> extends AbstractList<E> implements List<E> {
    /**
     * 设计思路:
     * 1、动态数组首先得有数组
     * 2、了解数组的特性,是一块连续的内存空间,当在内存中开辟空间以后,那一块空间本身就不能变动了
     * 3、数组的容量并不代表当前数据量
     * 4、注意对象类型数据的内存管理与比较方式
     */
    /**
     * 完成功能:
     * 1)动态数组大小 size
     * 2)数组是否为空 isEmpty
     * 3)是否包含某个元素
     * 4)在末尾添加元素
     * 5)获取某个索引的元素
     * 6)设置某个索引的元素
     * 7)在某个索引位添加元素
     * 8)根据元素本身移除元素
     * 9)根据元素索引移除元素
     * 10)找到某个元素的索引位置
     * 11)清空动态数组
     * 这些方法中,最重要的方法就是:基础添加与基础删除方法
     * 需要考虑问题:
     * 1)添加扩容问题,扩容1.5倍
     * 2)toString,采用StringBuilder拼接
     * 3)对象clear时候,注意内存管理,对象引用置为空,但是数组要保留
     */

    private E[] elements;

    private int front; // 头节点索引,在头部添加元素,以及删除头部的元素的时候需要注意

    private static final int DEFAULT_CAPACITY = 10;

    public ArrayList2(int capacity) {
        capacity = capacity > DEFAULT_CAPACITY ? capacity : DEFAULT_CAPACITY;
        elements = (E[]) new Object[capacity];
    }

    public ArrayList2() {
        this(DEFAULT_CAPACITY);
    }

    @Override
    public E get(int index) {
        // 校验索引合理性
        checkRangeIndex(index);
        return elements[index(index)]; // O(1)
    }

    @Override
    public E set(int index, E element) {
        // 校验索引合理性
        checkRangeIndex(index);
        E oldElement = elements[index(index)];
        elements[index(index)] = element; // O(1)
        return oldElement;
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,往最后添加元素 O(1)
    // 最坏情况复杂度,往0添加元素,O(size)--> O(n),数组的挪动
    // 平均情况复杂度,(1+2+3+...+n)/n  --> O(n)
    @Override
    public void add(int index, E element) {
        // 校验索引合理性
        checkRangeIndexForAdd(index);
        // 考虑扩容问题,因为是添加元素,所以容量必须得是size+1才行
        ensureCapacity(size + 1);
        // 添加思路:
        // 添加的时候,根据index的位置,考虑数组往左移或者右移
        boolean left = true;
        if (index >= size >> 2) {
            left = false;
        }
        // 往左移
        if (left) {
            for (int i = 0; i < index; i++) {
                elements[index(i - 1)] = elements[index(i)];
            }
            // front向后移动
            front = index(-1);
            elements[index(index)] = element;
        } else {
            for (int i = size - 1; i >= index; i++) {
                elements[index(i + 1)] = elements[index(i)];
            }
            elements[index(index)] = element;
        }
        size++;
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,往最后添加元素 O(1)
    // 最坏情况复杂度,往0添加元素,O(size)--> O(n)
    // 平均情况复杂度, 均摊复杂度--> O(1),均摊等于最好
    // 什么时候使用均摊复杂度?经过连续的多次复杂度比较低的情况以后,出现个别复杂度比较高的情况
    private void ensureCapacity(int capacity) {
        int oldCapacity = elements.length;
        // 如果之前的容量大于等于或者size+1,代表原来容量够用,直接return
        if (oldCapacity >= capacity) {
            return;
        }
        // 如果不够用,则扩容,java中官方扩容大小是1.5倍,因此这里也扩容1.5倍,避免频繁扩容,损耗CPU
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 由于数组是一块连续的空间,因此扩容只能new一个新的数组
        E[] newElements = (E[]) new Object[newCapacity];
        // 数据拷贝
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[index(i)];
        }
        elements = newElements;

        System.out.println("数组扩容:" + oldCapacity + "-->" + newCapacity);
    }

    // O(n) n是数据规模,动态数组的size是数据规模
    // 最好情况复杂度,删除最后添加元素 O(1)
    // 最坏情况复杂度,删除0位置元素,O(size)--> O(n)
    // 平均情况复杂度,(1+2+3+...+n)/n  -->O(n)
    @Override
    public E remove(int index) {
        // 校验索引合理性
        checkRangeIndex(index);
        // 删除思路:
        // 移除某个index位置的元素,本质就是数组中,index后面的元素往前移,覆盖前面的元素
        // 由于是后面的覆盖前面的,那必定是从index后面一个元素开始,逐个覆盖前面的元素
        // 因此,实现思路:1.循环覆盖 2.如果要删除index位置的元素,那么应该是index + 1覆盖index,一直到size-1 覆盖size-2
        // 由于内存管理的原因,size-1位的元素需要置为null
        E oldElement = elements[index(index)];
        boolean left = true;
        if (index >= size >> 2) {
            left = false;
        }
        if (left) { // 如果删左边,则左边部分右移
            for (int i = index; i > 0; i--) {
                elements[index(i)] = elements[index(i - 1)];
            }
            elements[index(0)] = null; //  头部置为null
            front = index(1); // 头向右移动1位
        } else { // 如果删右边,则右边部分左移
            for (int i = index; i < size; i++) {
                elements[index(i)] = elements[index(i + 1)];
            }
            elements[index(size - 1)] = null; // 尾部置为null
        }
        size--;
        // 缩容和扩容原理一样,申请新的内存空间
        trimCapacity();
        return oldElement;
    }

    // 缩容
    private void trimCapacity() {
        int oldCapacity = elements.length;
        int newCapacity = oldCapacity >> 1;
        if (size >= newCapacity || newCapacity < DEFAULT_CAPACITY) return;
        // 由于数组是一块连续的空间,因此缩容只能new一个新的数组
        E[] newElements = (E[]) new Object[newCapacity];
        // 数据拷贝
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[index(i)];
        }
        elements = newElements;

        System.out.println("数组缩容:" + oldCapacity + "-->" + newCapacity);
    }

    @Override
    public int indexOf(E element) {
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                int realIndex = index(i);
                if (elements[realIndex] == null)
                    return realIndex;
            }
        } else {
            for (int i = 0; i < size; i++) {
                int realIndex = index(i);
                if (element.equals(elements[realIndex]))
                    return realIndex;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

    @Override
    public void clear() {
        // 由于直接将size = 0,对象还滞留在内存中,因此这里还需要清理无用的对象
        for (int i = 0; i < size; i++) {
            elements[index(i)] = null;
        }
        // 缩容,申请新的内存空间
        if (elements.length > DEFAULT_CAPACITY) {
            elements = (E[]) new Object[DEFAULT_CAPACITY];
        }
        size = 0;
    }

    private int index(int index) {
        index += front;
        if (index < 0) {
            return index + elements.length;
        }
        return index >= elements.length ? index - elements.length : index;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("front:" + front);
        builder.append(",size:" + size + ",[");
        for (int i = 0; i < elements.length; i++) {
            if (i != 0)
                builder.append(",");
            builder.append(elements[i]);
        }
        builder.append("]");
        return builder.toString();
    }
}

7.双向链表


package com.huawei.list;

/**
 * 功能描述:双向链表
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public class DoubleLinkedList<E> extends AbstractList<E> implements List<E> {
    /**
     * 链表设计思路:
     * 1.链表是层层递进,没有初始容量
     * 2。链表需要有一个节点对象(Node)来记录当前元素的值,以及下一个Node
     * 3.链表需要有一个头节点
     */
    // 定义静态内部类
    private static class Node<E> {
        E element;

        Node<E> next;

        Node<E> pre;

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (pre != null) {
                builder.append(pre.element);
            } else {
                builder.append("null");
            }
            builder.append("_" + element + "_");

            if (next != null) {
                builder.append(next.element);
            } else {
                builder.append("null");
            }

            return builder.toString();
        }

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

        }
    }

    private Node<E> first;

    private Node<E> last;

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

    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

    @Override
    public void add(int index, E element) {
        checkRangeIndexForAdd(index);
        // 先通用情况,再特殊情况
        // 1.先考虑中间情况
        // 在当前索引添加元素,则先找到index原来位置的元素
        // 然后,新节点的next是原来节点,原来节点pre是新节点
        // 原来节点上一个节点的next指向新节点
        // 2.再考虑在尾部或者首部添加
        if (index == size) { // 在尾部添加
            // last肯定指向newNode,然后之前的last的next指向newNode
            Node<E> oldLast = last;
            last = new Node<>(oldLast, element, null);
            if (oldLast == null) { // 没有节点的时候,first = last = newNode
                first = last;
            } else {
                oldLast.next = last; // 这里当没有节点的时候,oldLast可能为null
            }
        } else {
            Node<E> origin = node(index);
            Node<E> newNode = new Node<>(origin.pre, element, origin);
            if (origin.pre == null) { // 在首部添加节点
                first = newNode;
            } else {
                origin.pre.next = newNode;
            }
            origin.pre = newNode;
        }
        size++;
    }

    @Override
    public E remove(int index) {
        checkRangeIndex(index);
        // 删除的时候
        // 1.找到当前节点
        Node<E> removeNode = node(index);
        // 2.removeNode的prev的next指向removeNode.next
        // removeNode的next的pre指向removeNode.prev
        Node<E> prev = removeNode.pre;
        Node<E> next = removeNode.next;
        if (prev == null) { // index = 0 的时候,删除头节点
            first = next;
        } else {
            prev.next = next;
        }

        if (next == null) { // 删除尾节点
            last = prev;
        } else {
            next.pre = prev;
        }

        size--;
        return removeNode.element;
    }

    @Override
    public int indexOf(E element) {
        Node<E> node = first;
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node.element == null)
                    return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

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

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

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    // 根据当前index获取Node元素
    private Node<E> node(int index) {
        // index合理性检查
        checkRangeIndex(index);
        // 双向链表从两头开始查找元素,比单向链表节省一半时间
        Node<E> node;
        if (index > (size >> 2)) { // 从尾部找
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.pre;
            }
        } else { // 从头找
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        }
        return node;
    }

}

8.单向循环链表


package com.huawei.list;

/**
 * 功能描述:单向循环链表
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public class CircleLinkedList<E> extends AbstractList<E> implements List<E> {
    /**
     * 链表设计思路:
     * 1.链表是层层递进,没有初始容量
     * 2。链表需要有一个节点对象(Node)来记录当前元素的值,以及下一个Node
     * 3.链表需要有一个头节点
     */
    // 定义静态内部类
    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 String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append( element + "_").append(next.element);
            return builder.toString();
        }
    }

    private Node<E> first;

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

    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

    @Override
    public void add(int index, E element) {
        checkRangeIndexForAdd(index);
        // 在index位置添加元素
        // 思路:
        // 1.在index位置添加元素,有两种思路
        // 1)找到index位置的节点,然后把新节点的next指向index位置的节点
        // 2) 再把index节点上一个节点的next指向当前节点,这个又需要调用找节点的方法,因此不采取
        // 或者
        // 1)找到index-1位置的节点
        // 2)新节点的next指向index-1位置的next
        // 3) index-1位置的next指向新节点

        // 采取第二种方法:
        if (index == 0) {
            // index = 0,代表在头部添加新节点
            // 那么新节点是first,新节点的next是之前first,那么:
            if(size == 0){
                first = new Node<>(element,null);
                first.next = first;
            }else{
                // 单向循环链表,添加头部节点的时候,需要找到尾部节点,然后尾部节点的头节点指向新添加的节点
                // 先找尾部节点
                Node<E> last = node(size - 1);
                // 新节点成为头节点
                first = new Node<>(element, first);
                // 尾节点的next变成新的first
                last.next = first;
            }
        } else {
            // 找到index位置的上一个节点
            Node<E> prev = node(index - 1);
            // 新建节点
//            Node<E> newNode = new Node<>(element, prev.next);
            // index - 1 位置的next指向新节点
//            prev.next = newNode;
            prev.next = new Node<>(element, prev.next);
        }
        size ++;
    }

    @Override
    public E remove(int index) {
        checkRangeIndex(index);
        Node<E> removeNode = first;
        if (index == 0) {
            if(size == 1){
                first = null;
            }else{
                // 找到最后一个节点,最后一个节点的next指向要指向新的first,
                // 当size = 1的时候,下列方法实际上没有把对象删掉,对象还保留在内存中,浪费了内存空间,但是把size减成了0,不影响使用
                // 上面对于size = 1这种情况还是特殊处理一下
                Node<E> last = node(size - 1);
                first = removeNode.next;
                last.next = first;
            }
        } else {
            Node<E> node = node(index - 1);
            removeNode = node.next;
            node.next = removeNode.next;
        }
        size -- ;
        return removeNode.element;
    }

    @Override
    public int indexOf(E element) {
        Node<E> node = first;
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node.element == null)
                    return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

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

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

    private Node<E> node(int index) {
        // index合理性检查
        checkRangeIndex(index);
        Node<E> node = first;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }

}

9.双向循环列表


package com.huawei.list;

/**
 * 功能描述:双向循环链表
 * 注:不论是单向循环链表还是双向循环链表,在删除单一元素的时候,需要特殊处理
 * 如果不处理,功能正确,但是会残留对象在内存
 *
 * @author wWX1050875
 * @since 2022-11-03
 */
public class CircleDoubleLinkedList<E> extends AbstractList<E> implements List<E> {
    /**
     * 链表设计思路:
     * 1.链表是层层递进,没有初始容量
     * 2。链表需要有一个节点对象(Node)来记录当前元素的值,以及下一个Node
     * 3.链表需要有一个头节点
     */
    // 定义静态内部类
    private static class Node<E> {
        E element;

        Node<E> next;

        Node<E> pre;

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(pre.element);
            builder.append("_" + element + "_");
            builder.append(next.element);
            return builder.toString();
        }

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

        }
    }

    private Node<E> first;

    private Node<E> last;

    // 双向循环链表:约瑟夫环
    private Node<E> current; // 环中链表的当前位置

    // 重置当前节点
    public void reset() {
        current = first;
    }

    // next:当前节点移动到下一个节点
    public void next() {
        if (current == null)
            return;
        current = current.next;
    }

    // current 获取当前节点元素,删除当前节点,并且指针向后移动
    public E current() {
        if (current == null)
            return null;
        Node<E> temp = current;
        remove(current);
        if (size != 1) { // 避免size为1的时候,自己指向自己
            current = temp.next;
        }
        return temp.element;
    }

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

    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

    @Override
    public void add(int index, E element) {
        checkRangeIndexForAdd(index);
        // 双向循环列表添加,在中间添加不影响,考虑尾部和头部

        if (index == size) { // 在尾部添加
            // 双向链表在尾部添加
            // 1.新节点取代之前的尾部节点成为last,之前的last的next指向newLast
            // 2.新节点next指向oldLast的next,新节点pre指向oldLast
            Node<E> oldLast = last;
            // 3.之前的first的prev指向新的next
            if (oldLast == null) { // 此时数组还没有元素
                first = last = new Node<>(null, element, null);
                first.pre = first;
                first.next = first;
            } else {
                last = new Node<>(oldLast, element, oldLast.next);
                oldLast.next = last;
                first.pre = last;
            }
        } else {
            Node<E> origin = node(index);
            Node<E> newNode = new Node<>(origin.pre, element, origin);
            origin.pre.next = newNode;
            origin.pre = newNode;
            if (index == 0) { // 在首部添加的时候,要改变first的指向
                first = newNode;
            }
        }
        size++;
    }

    @Override
    public E remove(int index) {
        checkRangeIndex(index);
        Node<E> removeNode = node(index);
        remove(removeNode);
        return removeNode.element;
    }

    private void remove(Node<E> removeNode) {
        // 删除的时候
        if (size == 1) {
            first = last = null;
        } else {
            // 1.找到当前节点
            // 2.removeNode的prev的next指向removeNode.next
            // removeNode的next的pre指向removeNode.prev
            Node<E> prev = removeNode.pre;
            Node<E> next = removeNode.next;
            prev.next = next;
            next.pre = prev;
            // 删除头节点的时,first指向变化
            if (removeNode == first) {
                first = next;
            }
            if (removeNode == last) { // 删除尾部节点的时候,last指向pre
                last = prev;
            }
        }
        size--;
    }

    @Override
    public int indexOf(E element) {
        Node<E> node = first;
        // element空校验
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node.element == null)
                    return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

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

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

    // 复杂度分析:
    // 最好情况复杂度,找第一个元素,O(1)
    // 最坏情况复杂度,找最后一个元素,O(n)
    // 平均情况复杂度,O(n)
    // 根据当前index获取Node元素
    private Node<E> node(int index) {
        // index合理性检查
        checkRangeIndex(index);
        // 双向链表从两头开始查找元素,比单向链表节省一半时间
        Node<E> node;
        if (index > (size >> 2)) { // 从尾部找
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.pre;
            }
        } else { // 从头找
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        }
        return node;
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

crazy程序猿丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值