双向链表的实现和原理

双向链表

双向链表与链表的区别在于,双向链表有指向前一个节点的指针,可以获取到前一个节点。
获取值的时候,可以判断index下标所在的位置,从而决定从首节点开始查找还是从尾节点开始查找

public class LinkedList<E> {

    private static final int ELEMENT_NOT_FOUNT = -1;

    private int size = 0;
    private Node<E> first;
    private Node<E> last;

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

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

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            if (prev != null) {
                sb.append(prev.element);
            } else {
                sb.append("null");
            }

            sb.append("_").append(element).append("_");

            if (next != null) {
                sb.append(next.element);
            } else {
                sb.append("null");
            }
            return sb.toString();
        }
    }

    /**
     * 清除所有元素
     */
    public void clear() {
        first = null;
        last = null;
        size = 0;
    }

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

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

    /**
     * 是否包含某个元素
     *
     * @param element
     * @return
     */
    public boolean contains(E element) {
        return indexOf(element) != ELEMENT_NOT_FOUNT;
    }

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

    /**
     * 获取index位置的元素
     *
     * @param index
     * @return
     */
    public E get(int index) {
        return node(index).element;
    }

    /**
     * 设置index位置的元素
     * 获取到该位置的节点,直接覆盖原来的值
     *
     * @param index
     * @param element
     * @return 原来的元素ֵ
     */
    public E set(int index, E element) {
        Node<E> node = node(index);
        E oldElement = node.element;
        node.element = element;
        return oldElement;
    }

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

        if (index == size) { // 往最后面添加元素
            Node<E> oldLast = last;
            last = new Node<>(element, oldLast, null);
            if (oldLast == null) { // 链表为空,往链表中添加第一个元素 size == 0  index == 0
                first = last;
            } else {
                oldLast.next = last;
            }
        } else {
            Node<E> next = node(index);
            Node<E> prev = next.prev;
            Node<E> node = new Node<>(element, prev, next);
            next.prev = node;

            if(prev == null) { // index == 0
                first = node;
            } else {
                prev.next = node;
            }
        }
        size++;
    }


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

        Node<E> node = node(index);
        Node<E> prev = node.prev;
        Node<E> next = node.next;

        if(prev == null) { // index == 0 删除首节点
            first = next;
        } else {
            prev.next = next;
        }

        if (next == null) { // index == size - 1 删除尾节点
            last = prev;
        } else {
            next.prev = prev;
        }

        size--;
        return node.element;
    }

    /**
     * 查看元素的索引
     *
     * @param element
     * @return
     */
    public int indexOf(E element) {
        if (element == null) { // 处理传入的参数为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 (element.equals(node.element)) { // 节点可能存储null,所以用传入的element调用equals方法,避免出现空指针异常
                    return i;
                }
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUNT;
    }

    /**
     * 获取对应下标的节点
     * @param index
     * @return
     */
    private Node<E> node(int index) { // 遍历找到对应下标的节点,找到index的节点,循环执行的次数为index - 1次
        checkRange(index);
        if (index < (size >> 1)) {
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        } else {
            Node<E> node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
            return node;
        }
    }

    private void outOfBounds(int index) {
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }

    private void checkRange(int index) {
        if (index < 0 || index >= size) {
            outOfBounds(index);
        }
    }

    private void checkRangeForAdd(int index) {
        if (index < 0 || index > size) {
            outOfBounds(index);
        }
    }

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

    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44); // 11 22 33 44

        list.add(1, 55); // 11 55 22 33 44
        list.remove(0); // 55 22 33 44
        list.set(2, 66); // 55 22 66 44
//        list.clear();

        System.out.println(list.indexOf(22));
        System.out.println(list);
    }
}

双向循环链表

与双向链表的区别在于,首节点的前一个节点指向尾节点,尾节点的下一个节点指向首节点。
在代码上主要的区别体现在添加和删除上

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

    if (index == size) { // 往链表最后面添加节点
        Node<E> oldLast = last;
        last = new Node<>(element, oldLast, first);
        if (oldLast == null) { // 链表为空,添加第一个节点 size == 0  index == 0
            first = last;
            first.next = first;
            first.prev = first;
        } else {
            oldLast.next = last;
            first.prev = last;
        }
    } else {
        Node<E> next = node(index);
        Node<E> prev = next.prev;
        Node<E> node = new Node<>(element, prev, next);
        next.prev = node;
        prev.next = node;

        if (next == first) { // 向链表的开头位置添加节点 index == 0
            first = node;
        }
    }
    size++;
}


/**
 * 删除index位置的元素
 *
 * @param index
 * @return
 */
public E remove(int index) {
    checkRange(index);
    return remove(node(index));
}

/**
 * 删除传入的节点
 *
 * @param node
 * @return
 */
private E remove(Node<E> node) {
    if (size == 1) { // 链表中仅有一个节点
        first = null;
        last = null;
    } else {
        Node<E> prev = node.prev;
        Node<E> next = node.next;
        prev.next = next;
        next.prev = prev;

        if (node == first) { // 删除首节点
            first = next;
        }

        if (node == last) { // 删除尾节点
            last = prev;
        }
    }
    size--;
    return node.element;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值