简谈JAVA基础--双向链表(LinkedList)

双向链表其实与单链表类似,只是存储结构略有不同。单链表采用指针指向下一个节点的方式,而双向链表多了一个指针指向前一个节点。

单链表的原理与实现参考: 简谈JAVA基础--单链表

双链表与单链表相比多了一个指针变量来指向下一个节点的地址。

每个节点包含三部分:  prev前一个节点的指针变量,data节点数据, next 后一个节点的指针变量

在单链表中,由于只有next,所以每个节点的数据地址由上一个节点所知,而对链表的所有操作,都需要通过一个头节点来开始进行。

而双向链表中,由于有了prev的存在,所以可以通过尾节点和头节点来开始进行操作。知道了某个节点,就得到了前后的节点所在位置。

在java中,LinkedList底层就是通过一个双向链表来实现的。有时间可以看一下源码,写的很精妙。




下面是根据LinkedList自己实现的双链表。
结点结构:



链表的存储结构:


插入操作

删除操作

实现双向链表源码( 可以看一下JDK中的LinkedList ):

package Chapter1;

import java.lang.reflect.Array;
import java.util.Collection;


/**
 * 实现双向链表。
 * Created by yukaiji on 2017/9/13.
 */
public class MyLinkedList<T> implements java.io.Serializable {

    private static final long serialVersionUID = -8247167820457785557L;
    /** List长度 **/
    private int     size;
    /** 操作次数 **/
    private int     count;
    /** 头节点 **/
    private Node<T> head;
    /** 尾节点 **/
    private Node<T> last;

    /**
     * 节点类
     */
    private class Node<T> {
        private T data;
        private Node<T> prev;
        private Node<T> next;

        public Node(Node<T> prev, T data, Node<T> next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

    /**
     * 构造方法
     */
    public MyLinkedList() {
    }

    /**
     * 添加对象到表头
     */
    public void addFirst(T data) {
        Node<T> headNode = head;
        // 创建一个新的节点
        Node<T> newNode = new Node<>(null, data, headNode);
        // 将头节点指向新的节点
        head = newNode;
        // 如果原先的头节点是null 代表新添加的该链表的第一个节点。
        // 将尾节点指向新节点,因为该链表只存在一个节点,头尾相同
        if (headNode == null) {
            last = newNode;
        } else {
            // 如果不是第一个节点。则将前一个节点的perv指向新添加的节点
            headNode.prev = newNode;
        }
        size++;
        count++;
    }

    /**
     * 添加对象到表尾
     */
    public void addLast(T data) {
        Node<T> lastNode = last;
        // 创建一个新的节点
        Node<T> newNode = new Node<T>(lastNode, data, null);
        // 将尾节点指向新的节点
        last = newNode;
        // 如果原先的尾节点为空,代表该节点为该链表的第一个节点
        // 头节点也为新节点,首尾相同
        if (lastNode == null) {
            head = newNode;
        } else {
            // 如果不是第一个节点。则将前一个节点的next指向新添加的节点
            lastNode.next = newNode;
        }
        size++;
        count++;
    }

    /**
     * 从链表头部删除一个节点
     */
    public void removeFirst() {
        // 创建头节点的下一个节点对象
        Node<T> headNext = head.next;
        // 因为要将该头结点删除,所以把数据和下个节点的指针置为空
        head.data = null;
        head.next = null;
        // 头节点指向该节点的下一个 (也就是删除头结点)
        head = headNext;
        // 如果头结点的下一个为空,说明要删除的节点是链表中唯一的节点。
        // 将尾节点置为null
        if (headNext == null) {
            last = null;
        } else {
            // 否则将删除后的头节点的prev置为null  (头节点perv永远为null)
            head.prev = null;
        }
        size--;
        count++;
    }


    /**
     * 从链表尾部删除一个节点(内容与removeFirst相反,参考removeFirst)
     */
    public void removeLast() {
        Node<T> lastPrev = last.prev;
        last.data = null;
        last.prev = null;
        last = lastPrev;
        if (lastPrev == null) {
            head = null;
        } else {
            last.next = null;
        }
        size--;
        count++;
    }

    /**
     * 打印链表
     */
    public void printList() {
        for (Node node = head; node != null; node = node.next) {
            System.out.print(node.data + " ");
        }
        System.out.println();
    }

    /**
     * 链表长度
     */
    public int size() {
        return size;
    }

    /**
     * 链表是否为空
     */
    public boolean isEmpty() {
        return head == null && last == null;
    }

    /**
     * 链表是否包含对象o
     */
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    /**
     * 链表转换为数组
     */
    public Object[] toArray() {
        Object[] array = new Object[size];
        int i = 0;
        for (Node node = head; node != null; node = node.next) {
            array[i++] = node.data;
        }
        return array;
    }

    /**
     * 链表转换为指定类型数组
     */
    public <E> E[] toArray(E[] arr) {
        // 如果传递的数组长度小于链表的长度,创建一个指定类型、链表长度的新数组
        if (arr.length < size) {
            arr = (E[]) Array.newInstance(arr.getClass().getComponentType(), size);
        }
        Object[] array = arr;
        int i = 0;
        for (Node node = head; node != null; node = node.next) {
            array[i++] = node.data;
        }
        // 将最后一位置为null代表数组结束
        if (arr.length > size) {
            array[size] = null;
        }
        return arr;
    }

    /**
     * 获得传递对象所在的索引位置
     */
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node node = head; node != null; node = node.next) {
                if (node.data == null) {
                    return index;
                }
                index++;
            }
        } else {
            for (Node node = head; node != null; node = node.next) {
                if (o.equals(node.data)) {
                    return index;
                }
                index++;
            }
        }
        return -1;
    }

    /**
     * 添加操作,默认采用表尾插入
     */
    public void add(T t) {
        addLast(t);
    }


    /**
     * 删除指定节点。
     * 这里有四种情况:
     * 1.节点在头部
     * 2.节点在尾部
     * 3.节点在中间
     * 4.无节点
     */
    public T removeNode(Node<T> node) {
        T data = node.data;
        Node<T> nodeNext = node.next;
        Node<T> nodePrev = node.prev;

        if (nodePrev == null) {
            head = nodeNext;
        } else {
            nodePrev.next = nodeNext;
        }

        if (nodeNext == null) {
            last = nodePrev;
        } else {
            nodeNext.prev = nodePrev;
        }
        size--;
        count++;
        return data;
    }

    /**
     * 根据传递对象删除第一个遇到的元素。
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<T> node = head; node != null; node = node.next) {
                if (node.data == null) {
                    removeNode(node);
                    return true;
                }
            }
        } else {
            for (Node<T> node = head; node != null; node = node.next) {
                if (o.equals(node.data)) {
                    removeNode(node);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 删除c列表中所有在链表中对应的第一个相同元素。
     */
    public boolean removeAll(Collection<?> c) {
        Object[] objs = c.toArray();
        for (Object o : objs) {
            T t = (T) o;
            remove(t);
        }
        return true;
    }

    /**
     * 删除index位置的节点
     */
    public T remove(int index) {
        return removeNode(node(index));
    }

    /**
     * 清空链表
     */
    public void clear() {
        for (Node<T> node = head; node != null; ) {
            Node<T> nodeNext = node.next;
            node.data = null;
            node.next = null;
            node.prev = null;
            node = nodeNext;
        }
        head = last = null;
        size = 0;
        count++;
    }

    /**
     * 将c插入到链表尾部
     */
    public boolean addAll(Collection<? extends T> c) {
        // 直接调用在链表尾部插入c
        return addAll(size, c);
    }

    /**
     * 将c插入到链表的index节点前
     */
    public boolean addAll(int index, Collection<? extends T> c) {
        indexIsException(index);
        Object[] array = c.toArray();
        int num = array.length;
        if (num == 0) {
            return false;
        }
        Node<T> pred, succ;
        // 这里判断是否从尾部插入
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            // 获得index位置的节点
            succ = node(index);
            // 记录该节点的前一个节点。
            pred = succ.prev;
        }

        for (Object o : array) {
            // 类型转换
            T t = (T) o;
            // 创建新的节点,前节点指针指向index的前节点。
            Node<T> node = new Node<>(pred, t, null);
            // 如果index前节点为null ,代表index 为头节点
            if (pred == null) {
                // 这样将头结点指向新插入的节点
                head = node;
            } else {
                // 将index前节点的后节点指针指向新的节点(因为插入的新节点在指定index的前面)
                pred.next = node;
            }
            pred = node;

        }
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += num;
        count++;
        return true;
    }

    /**
     * 获取index节点数据
     */
    public T get(int index) {
        return node(index).data;
    }

    public boolean retainAll(Collection<?> c) {
        return false;
    }

    /**
     * 在索引index处添加一个对象
     */
    public void add(int index, T element) {
        indexIsException(index);
        if (index == size) {
            addLast(element);
        } else {
            Node<T> succ = node(index);
            Node<T> prev = succ.prev;
            Node<T> node = new Node<>(prev, element, succ);
            succ.prev = node;
            if (prev == null) {
                head = node;
            } else {
                prev.next = node;
            }
        }
        size++;
        count++;
    }

    /**
     * 判断索引是否小于0
     */
    public Boolean indexIsException(int index) {
        if (index < 0) {
            System.out.println("索引小于0");
            return false;
        }
        return true;
    }

    /**
     * 返回指定对象最后出现的位置
     */
    public int lastIndexOf(Object o) {
        int count = size;
        if (o == null) {
            for (Node<T> node = last; node != null; node = node.prev) {
                count--;
                if (node.data == null) {
                    return count;
                }
            }
        } else {
            for (Node<T> node = last; node != null; node = node.prev) {
                count--;
                if (o.equals(node.data)) {
                    return count;
                }
            }
        }
        return -1;
    }

    /**
     * 获取index位置的节点
     */
    Node<T> node(int index) {
        // 这里为什么判断传过来的index 是否小于链表长度的一半呢?
        // 因为这里涉及到效率问题,如果小于一半,从前向后遍历,如果大于一般从后向前遍历。
        if (index < (size >> 1)) {
            Node<T> x = head;
            // 从前向后遍历,x每次为当前节点的下一个,直到找到index节点
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<T> x = last;
            // 从后向前遍历,x每次为当前节点的前一个,直到找到index节点
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
}



测试函
public static void main(String[] args) {
        MyLinkedList<String> myLinkedList = new MyLinkedList<String>();
        myLinkedList.add("a");
        myLinkedList.add("b");
        myLinkedList.add("c");
        myLinkedList.add("d");
        myLinkedList.add("e");
        myLinkedList.printList();

        myLinkedList.remove("c");
        myLinkedList.printList();
        myLinkedList.addFirst("1");
        myLinkedList.printList();
        myLinkedList.addLast("2");
        myLinkedList.printList();

        List<String> addList = new ArrayList<String>();
        addList.add("A");
        addList.add("B");
        myLinkedList.addAll(addList);
        myLinkedList.printList();

        System.out.println(myLinkedList.get(3));
        System.out.println(myLinkedList.contains("d"));
        System.out.println(myLinkedList.indexOf("e"));

        myLinkedList.removeFirst();
        myLinkedList.printList();

        myLinkedList.removeLast();
        myLinkedList.printList();

        myLinkedList.clear();
        myLinkedList.printList();
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值