数据结构与算法【链表:一】Java实现

目录

链表

单向链表

哨兵链表

双向链表

环形链表


链表

链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。

随机访问性能

根据 index 查找,时间复杂度 O(n)

插入或删除性能

  • 起始位置:O(1)
  • 结束位置:如果已知 tail 尾节点是 O(1)[双向链表],不知道 tail 尾节点是 O(n)
  • 中间位置:根据 index 查找时间 + O(1)

单向链表

单向链表中每个元素只知道下一个节点位置

单向链表的简单实现

public class SinglyLinkList implements Iterable {
    private Node head = null;

    //节点类
    private static class Node {
        int value;
        Node next;

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
    }

    //提供链表方法
    public void addFirst(int value) {
        head = new Node(value, head);
    }

    //向链表尾部添加节点
    public void addLast(int value) {
        Node last = findLast();
        if (last == null) {
            addFirst(value);
        }
        last.next = new Node(value, null);
    }

    private Node findLast() {
        if (head.next == null) {
            return null;
        }
        Node p = head;
        while (p.next != null) {
            p = p.next;
        }
        return p;
    }

    //遍历链表
    public void loop(Consumer consumer) {
        Node pointer = head;
        while (pointer != null) {
            consumer.accept(pointer.value);
            pointer = pointer.next;
        }
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node pointer = head;
            @Override
            public boolean hasNext() {
                return pointer != null;
            }

            @Override
            public Integer next() {
                int value = pointer.value;
                pointer = pointer.next;
                return value;
            }
        };
    }

    // 根据索引查找节点值
    private Node findByIndex(int index) {
        int i = 0;
        for (Node p = head; p != null; p = p.next, i++) {
            if (index == i) {
                return p;
            }
        }
        return null;
    }

    public int get(int index) {
        Node node = findByIndex(index);
        if (node == null) {
            throw new IllegalArgumentException("索引超出范围");
        }
        return node.value;
    }

    //插入元素
    public void insert(int index, int value) {
        if (index == 0) {
            addFirst(value);
            return;
        }
        //查找索引的上一个节点
        Node node = findByIndex(index - 1);
        if (node == null) {
            throw new IllegalArgumentException("索引超出范围");
        }
        node.next = new Node(value, node.next);
    }

    //移除首节点
    public void removeFirst() {
        if (head==null){
            throw new RuntimeException("链表为空。无法移除");
        }
        head = head.next;
    }

    //删除指定节点
    public void remove(int index){
        if (index==0){
            removeFirst();
        }
        //找到要删除的节点的前驱节点
        Node node = findByIndex(index - 1);
        Node remove = node.next;
        if (remove==null){
            //要删除的节点位置为空
            throw new IllegalArgumentException("索引越界");
        }
        node.next = remove.next;
    }
}

这种实现方法有一个弊端,那就是每次添加或是删除元素时都需要进行判断链表是否为空。因此提出了哨兵链表的概念

哨兵链表

所谓哨兵列表,就是头节点不存储数据,只为简化边缘判断使用。

具体代码如下

public class SinglyLinkListSentinel implements Iterable {
    //头节点指向哨兵节点。值可以随便给
    private Node head = new Node(723468, null);

    //节点类
    private static class Node {
        int value;
        Node next;

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
    }

    public void addFirst(int value) {
        //在链表头部添加节点时,应该在哨兵之后。
        insert(0, value);
    }

    //向链表尾部添加节点
    public void addLast(int value) {
        Node last = findLast();
        last.next = new Node(value, null);
    }

    private Node findLast() {
        Node p = head;
        while (p.next != null) {
            p = p.next;
        }
        return p;
    }

    //遍历链表
    public void loop(Consumer consumer) {
        //从哨兵节点之后的节点开始遍历
        Node pointer = head.next;
        while (pointer != null) {
            consumer.accept(pointer.value);
            pointer = pointer.next;
        }
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node pointer = head.next;

            @Override
            public boolean hasNext() {
                return pointer != null;
            }

            @Override
            public Integer next() {
                int value = pointer.value;
                pointer = pointer.next;
                return value;
            }
        };
    }

    // 根据索引查找节点值
    private Node findByIndex(int index) {
        //这里设置为-1是排除哨兵节点带来的影响
        int i = -1;
        for (Node p = head; p != null; p = p.next, i++) {
            if (index == i) {
                return p;
            }
        }
        return null;
    }

    public int get(int index) {
        Node node = findByIndex(index);
        if (node == null) {
            throw new IllegalArgumentException("索引超出范围");
        }
        return node.value;
    }

    //插入元素
    public void insert(int index, int value) {
        //查找索引的上一个节点
        Node node = findByIndex(index - 1);
        if (node == null) {
            throw new IllegalArgumentException("索引超出范围");
        }
        node.next = new Node(value, node.next);
    }

    //移除首节点
    public void removeFirst() {
        remove(0);
    }

    //删除指定节点
    public void remove(int index) {
        //找到要删除的节点的前驱节点
        Node node = findByIndex(index - 1);
        Node remove = node.next;
        if (remove == null) {
            //要删除的节点位置为空
            throw new IllegalArgumentException("索引越界");
        }
        node.next = remove.next;
    }
}

双向链表

每个元素知道上一个元素与下一个元素的地址

简单实现如下

public class DoublyLinkedListSentinel implements Iterable{
    //头哨兵
    private Node head = new Node(null, null, 0);
    //尾哨兵
    private Node tail = new Node(null, null, 0);

    public DoublyLinkedListSentinel() {
        //初始化时,对头尾哨兵进行指定
        head.next = tail;
        tail.prev = head;
    }

    private static class Node {
        Node prev;
        Node next;
        int value;

        public Node(Node prev, Node next, int value) {
            this.prev = prev;
            this.next = next;
            this.value = value;
        }
    }

    //提供双向链表方法
    public Node findByIndex(int index) {
        int i = -1;
        for (Node p = head; p != tail; p = p.next, i++) {
            if (index == i) {
                return p;
            }
        }
        return null;
    }

    //插入首元素
    public void addFirst(int value) {
        insert(0, value);
    }

    //插入元素
    public void insert(int index, int value) {
        Node prev = findByIndex(index - 1);
        if (prev == null) {
            throw new RuntimeException("下标越界");
        }
        Node next = prev.next;
        Node node = new Node(prev, next, value);
        prev.next = node;
        next.prev = node;
    }

    //向尾节点添加
    public void addLast(int value){
        Node prev = tail.prev;
        Node addNode = new Node(prev, tail, value);
        prev.next = addNode;
        tail.prev = addNode;
    }
    //删除首节点
    public void removeFirst(){
        if (head.next==tail){
            throw new RuntimeException("链表为空");
        }
        Node remove = head.next;
        Node next = remove.next;
        head.next = next;
        next.prev = head;
    }

    //删除元素
    public void remove(int index) {
        Node prev = findByIndex(index - 1);
        if (prev == null) {
            throw new RuntimeException("下标越界");
        }
        Node remove = prev.next;
        Node next = remove.next;
        if (remove==tail){
            throw new RuntimeException("下标越界");
        }
        prev.next = next;
        next.prev = prev;
    }

    //获取元素
    public int get(int index){
        Node node = findByIndex(index);
        return node.value;
    }

    //遍历元素
    public void loop(Consumer consumer) {
        Node pointer = head.next;
        while (pointer != tail) {
            consumer.accept(pointer.value);
            pointer = pointer.next;
        }
    }

    //迭代器实现
    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node p =head.next;
            @Override
            public boolean hasNext() {
                return p!=tail;
            }

            @Override
            public Integer next() {
                int value = p.value;
                p = p.next;
                return value;
            }
        };
    }
}

环形链表

环形链表的尾节点指向的是头节点head

数据结构与算法【链表:一】Java实现_第1张图片

环形链表我们也可以引入哨兵,空链表时,哨兵既做头节点也做尾节点。

数据结构与算法【链表:一】Java实现_第2张图片

简单实现代码如下

public class DoublyLinkedListSentinelTo implements Iterable {
    private Node sentinel = new Node(null, null, -1);

    public DoublyLinkedListSentinelTo() {
        sentinel.prev = sentinel;
        sentinel.next = sentinel;
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node p = sentinel.next;

            @Override
            public boolean hasNext() {
                return p != sentinel;
            }

            @Override
            public Integer next() {
                int value = p.value;
                p = p.next;
                return value;
            }
        };
    }

    private static class Node {
        Node prev;
        Node next;
        int value;

        public Node(Node prev, Node next, int value) {
            this.prev = prev;
            this.next = next;
            this.value = value;
        }
    }

    //提供循环链表的方法
    public Node findByIndex(int index) {
        int i = 0;
        for (Node p = sentinel.next; p != sentinel; i++, p = p.next) {
            if (index == i) {
                return p;
            }
        }
        return null;
    }

    public void addFirst(int value) {
        Node next = sentinel.next;
        Node addNode = new Node(sentinel, next, value);
        sentinel.next = addNode;
        next.prev = addNode;
    }

    public void insert(int index, int value) {
        if (index == 0) {
            addFirst(value);
        }
        Node prev = findByIndex(index - 1);
        if (prev == null) {
            throw new RuntimeException("下标越界");
        }
        Node next = prev.next;
        Node insertNode = new Node(prev, next, value);
        next.prev = insertNode;
        prev.next = insertNode;
    }

    public void addLast(int value) {
        Node prev = sentinel.prev;
        Node addNode = new Node(prev, sentinel, value);
        prev.next = addNode;
        sentinel.prev = addNode;
    }

    //删除第一个节点
    public void removeFirst() {
        Node remove = sentinel.next;
        if (remove == sentinel) {
            throw new RuntimeException("下标越界");
        }
        Node next = remove.next;
        sentinel.next = next;
        next.prev = sentinel;
    }

    //删除最后一个节点
    public void removeLast() {
        Node remove = sentinel.prev;
        if (remove == sentinel) {
            throw new RuntimeException("下标越界");
        }
        Node prev = remove.prev;
        prev.next = sentinel;
        sentinel.prev = prev;
    }

    //删除指定删除节点
    public void remove(int index) {
        if (index == 0) {
            removeFirst();
        }
        Node prev = findByIndex(index - 1);
        Node remove = prev.next;
        if (remove == sentinel) {
            throw new RuntimeException("下标越界");
        }
        Node next = remove.next;
        prev.next = next;
        next.prev = prev;
    }

    //遍历节点
    public void loop(Consumer consumer) {
        for (Node p = sentinel.next; p != sentinel; p = p.next) {
            consumer.accept(p.value);
        }
    }
}

你可能感兴趣的:(java,链表,数据结构)