【恋上数据结构与算法 第一季】链表LinkedList


持续学习&持续更新中…

学习态度:脚踏实地


单向链表

在这里插入图片描述

双向链表

在这里插入图片描述

图示单向链表

单向链表

只有一个元素的情况:

在这里插入图片描述

图示单向循环链表

单向循环链表

只有一个元素的情况:

在这里插入图片描述

图示双向链表

双向链表

只有一个元素的情况:

在这里插入图片描述

图示双向循环链表

双向循环链表

只有一个元素的情况:

在这里插入图片描述

接口设计

List接口
public interface List<E> {

    int ELEMENT_NOT_FOUND = -1;

    int size();

    boolean add(E element);

    void add(int index, E element);

    boolean remove(E element);

    E remove(int index);

    E get(int index);

    E set(int index, E element);

    int indexOf(E element);

    boolean isEmpty();

    boolean contains(E element);

    void clear();

}

AbstractList 抽象类
public abstract class AbstractList<E> implements List<E> {

    protected int size;

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

    @Override
    public boolean add(E element) {
        add(size, element);
        return true;
    }

    @Override
    public boolean remove(E element) {
        remove(indexOf(element));
        return true;
    }

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

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

    protected void rangeCheck(int index) {
        if (index < 0 || index >= size) {
            arrayIndexOutOfBoundsException(index);
        }
    }

    protected void rangeCheckForAdd(int index) {
        if (index < 0 || index > size) {
            arrayIndexOutOfBoundsException(index);
        }
    }

    protected void arrayIndexOutOfBoundsException(int index) {
        throw new ArrayIndexOutOfBoundsException("index : " + index + " size : " + size);
    }

}

代码实现

单向链表
public class SingleLinkedList<E> extends AbstractList<E> {

    private Node<E> first;

    @Override
    public void add(int index, E element) {
        if (null == element) {
            System.out.println("不能添加null元素");
            return;
        }
        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 = prev.next.next;
        }
        size--;
        return node.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 e = node.element;
        node.element = element;
        return e;
    }

    @Override
    public int indexOf(E element) {
        int i;
        Node<E> node = first;
        for (i = 0; i < size; i++) {
            if (node.element.equals(element)) break;
            node = node.next;
        }
        if (i == size) return ELEMENT_NOT_FOUND;
        return i;
    }

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

    private Node<E> node(int index) {
        rangeCheck(index);
        Node<E> node = first;
        while (index != 0) {
            node = node.next;
            index--;
        }
        return node;
    }

    @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.element);
            node = node.next;
        }
        sb.append("]");
        return sb.toString();
    }

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

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

}

单向循环链表
public class SingleCircleLinkedList<E> extends AbstractList<E> {

    private Node<E> first;

    @Override
    public void add(int index, E element) {
        if (null == element) {
            System.out.println("不能添加null元素");
            return;
        }
        if (index == 0) {

            if (size == 0) {
                first = new Node<>(element, null);
                first.next = first;
            } else {
                Node<E> last = node(size - 1);
                first = new Node<>(element, first);
                last.next = 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) {

            if (size == 1) {
                first = null;
            } else {
                Node<E> last = node(size - 1);
                first = first.next;
                last.next = first;
            }

        } else {
            Node<E> prev = node(index - 1);
            node = prev.next;
            prev.next = prev.next.next;
        }
        size--;
        return node.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 e = node.element;
        node.element = element;
        return e;
    }

    @Override
    public int indexOf(E element) {
        int i;
        Node<E> node = first;
        for (i = 0; i < size; i++) {
            if (node.element.equals(element)) break;
            node = node.next;
        }
        if (i == size) return ELEMENT_NOT_FOUND;
        return i;
    }

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

    private Node<E> node(int index) {
        rangeCheck(index);
        Node<E> node = first;
        while (index != 0) {
            node = node.next;
            index--;
        }
        return node;
    }

    @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.element);
            node = node.next;
        }
        sb.append("]");
        return sb.toString();
    }

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

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

}

双向链表
public class LinkedList<E> extends AbstractList<E> {

    private Node<E> first;
    private Node<E> last;

    @Override
    public void add(int index, E element) {
        if (null == element) {
            System.out.println("不能添加null元素");
            return;
        }
        rangeCheckForAdd(index);

        if (size == index) { // 往后加
            Node<E> node = new Node<>(last, element, null);
            if (last != null) {
                last.next = node;
            } else {
                first = node;
            }
            last = node;
        } else {
            Node<E> next = node(index);
            Node<E> prev = next.prev;
            Node<E> node = new Node<>(prev, element, next);
            if (null == prev) { // 在头部插入
                first = node;
            } else {
                prev.next = node;
            }
            next.prev = node;
        }

        size++;
    }

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

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

        if (prev == null) { // 删除第0个元素
            first = next;
        } else {
            prev.next = next;
        }

        if (next == null) { // 删除最后一个元素
            last = prev;
        } else {
            next.prev = prev;
        }

        size--;

        return node.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 e = node.element;
        node.element = element;
        return e;
    }

    @Override
    public int indexOf(E element) {
        int i;
        Node<E> node = first;
        for (i = 0; i < size; i++) {
            if (node.element.equals(element)) break;
            node = node.next;
        }
        if (i == size) return ELEMENT_NOT_FOUND;
        return i;
    }

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

    private Node<E> node(int index) {
        rangeCheck(index);

        Node<E> node;

        if (index < (size >> 1)) {
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        } else {
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
        }

        return node;
    }

    @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();
    }

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

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

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();

            if (null == prev) {
                sb.append("null");
            } else {
                sb.append(prev.element);
            }

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

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

            return sb.toString();
        }

    }

}

双向循环链表
public class CircleLinkedList<E> extends AbstractList<E> {

    private Node<E> first;
    private Node<E> last;

    @Override
    public void add(int index, E element) {
        if (null == element) {
            System.out.println("不能添加null元素");
            return;
        }
        rangeCheckForAdd(index);

        if (size == index) { // 往后加

            Node<E> node = new Node<>(last, element, first);
            if (last != null) {
                last.next = node;
                first.prev = node;
            } else {
                first = node;
                first.next = first;
                first.prev = first;
            }

            last = node;
        } else {
            Node<E> next = node(index);
            Node<E> prev = next.prev;
            Node<E> node = new Node<>(prev, element, next);
            prev.next = node;
            next.prev = node;
            if (index == 0) { // 在头部插入
                first = node;
            }
        }

        size++;
    }

    @Override
    public E remove(int index) {
        rangeCheck(index);
        Node<E> node = first;
        if (size == 1) {
            first = last = null;
        } else {
            node = node(index);
            Node<E> prev = node.prev;
            Node<E> next = node.next;
            prev.next = next;
            next.prev = prev;

            if (index == 0) { // 删除第0个元素
                first = next;
            }
            if (index == size - 1) { // 删除最后一个元素
                last = prev;
            }
        }

        size--;

        return node.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 e = node.element;
        node.element = element;
        return e;
    }

    @Override
    public int indexOf(E element) {
        int i;
        Node<E> node = first;
        for (i = 0; i < size; i++) {
            if (node.element.equals(element)) break;
            node = node.next;
        }
        if (i == size) return ELEMENT_NOT_FOUND;
        return i;
    }

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

    private Node<E> node(int index) {
        rangeCheck(index);

        Node<E> node;

        if (index < (size >> 1)) {
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        } else {
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
        }

        return node;
    }

    @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();
    }

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

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

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();

            if (null == prev) {
                sb.append("null");
            } else {
                sb.append(prev.element);
            }

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

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

            return sb.toString();
        }

    }

}

动态数组、链表复杂度分析

在这里插入图片描述

注意:动态数组的“addLast”方法的**平均(均摊)**复杂度是O(1);一般来说,我们使用ArrayList时使用的都是“addLast”(直接调用add方法)。

复杂度分析—双向链表

双向链表 vs 单向链表

在这里插入图片描述

双向链表 vs 动态数组

在这里插入图片描述

约瑟夫问题

在这里插入图片描述

在这里插入图片描述

/*
    为了约瑟夫问题
    并且发挥出双向循环链表的最大威力
*/
public class CircleLinkedListPlus<E> extends AbstractList<E> {

    private Node<E> first;
    private Node<E> last;

    private Node<E> current;

    public void reset() {
        current = first;
    }

    public E next() {
        if (current == null) return null;
        current = current.next;
        return current.element;
    }

    /**
     * 删除current,并且让current指向current.next
     * @return 被删除节点的element
     */
    public E remove() {
        if (current == null) return null;
        Node<E> next = current.next;
        E ele = remove(current);
        if (size == 0) {
            current = null;
        } else {
            current = next;
        }
        return ele;
    }

    private E remove(Node<E> node) {
        if (size == 1) {
            first = last = null;
        } else {
            Node<E> prev = node.prev;
            Node<E> next = node.next;
            prev.next = next;
            next.prev = prev;

            if (first == node) { // 删除第0个元素
                first = next;
            }

            if (last == node) { // 删除最后一个元素
                last = prev;
            }
        }

        size--;

        return node.element;
    }

    @Override
    public void add(int index, E element) {
        if (null == element) {
            System.out.println("不能添加null元素");
            return;
        }
        rangeCheckForAdd(index);

        if (size == index) { // 往后加

            Node<E> node = new Node<>(last, element, first);
            if (last != null) {
                last.next = node;
                first.prev = node;
            } else {
                first = node;
                first.next = first;
                first.prev = first;
            }

            last = node;
        } else {
            Node<E> next = node(index);
            Node<E> prev = next.prev;
            Node<E> node = new Node<>(prev, element, next);
            prev.next = node;
            next.prev = node;
            if (index == 0) { // 在头部插入
                first = node;
            }
        }

        size++;
    }

    @Override
    public E remove(int index) {
        rangeCheck(index);
        return remove(node(index));
//        Node<E> node = first;
//        if (size == 1) {
//            first = last = null;
//        } else {
//            node = node(index);
//            Node<E> prev = node.prev;
//            Node<E> next = node.next;
//            prev.next = next;
//            next.prev = prev;
//
//            if (index == 0) { // 删除第0个元素
//                first = next;
//            }
//            if (index == size - 1) { // 删除最后一个元素
//                last = prev;
//            }
//        }
//
//        size--;
//
//        return node.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 e = node.element;
        node.element = element;
        return e;
    }

    @Override
    public int indexOf(E element) {
        int i;
        Node<E> node = first;
        for (i = 0; i < size; i++) {
            if (node.element.equals(element)) break;
            node = node.next;
        }
        if (i == size) return ELEMENT_NOT_FOUND;
        return i;
    }

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

    private Node<E> node(int index) {
        rangeCheck(index);

        Node<E> node;

        if (index < (size >> 1)) {
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        } else {
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
        }

        return node;
    }

    @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();
    }

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

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

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();

            if (null == prev) {
                sb.append("null");
            } else {
                sb.append(prev.element);
            }

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

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

            return sb.toString();
        }

    }


}

拓展

虚拟头结点

不使用,感觉没必要。

在这里插入图片描述

静态链表

在这里插入图片描述

动态数组能否进一步优化

在这里插入图片描述

注意

  1. 在编写链表过程中,要注意边界测试:比如index为0、size、…时
  2. 编写代码时,首先实现中间元素的添加或者删除操作,然后再考虑 index==0 || index == size || index == size - 1 || size == 0 || size == 1 … 等的这种边界情况。
  3. 编写代码时,思考问题时,要理清思路。比如在删除元素时,由于有rangeCheck方法,所以一定可以获取到元素的。
  4. 编写代码时,可以自己手动画图来理清思路和理解代码的执行过程。
  5. 对于编程来说,一定要细心、细心、再细心,要充分考虑每一种情况、认真考虑边界问题。
  6. 推荐一个网站:https://visualgo.net/zh

参考

小码哥李明杰老师课程: 恋上数据结构与算法 第一季.


本文完,感谢您的关注支持!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值