【从蛋壳到满天飞】JAVA 数据结构解析和算法实现-链表

思惟导图

前言

【从蛋壳到满天飞】JAVA 数据结构解析和算法实现,所有文章大概的内容以下:
Arrays(数组)、Stacks(栈)、Queues(队列)、LinkedList(链表)、Recursion(递归思想)、BinarySearchTree(二分搜索树)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(优先队列)、SegmentTree(线段树)、Trie(字典树)、UnionFind(并查集)、AVLTree(AVL 平衡树)、RedBlackTree(红黑平衡树)、HashTable(哈希表)html

源代码有三个:ES6(单个单个的 class 类型的 js 文件) | JS + HTML(一个 js 配合一个 html)| JAVA (一个一个的工程)java

所有源代码已上传 github,点击我吧,光看文章可以掌握两成,动手敲代码、动脑思考、画图才能够掌握八成。node

本文章适合 对数据结构想了解而且感兴趣的人群,文章风格一如既往如此,就以为手机上看起来比较方便,这样显得比较有条理,整理这些笔记加源码,时间跨度也算将近半年时间了,但愿对想学习数据结构的人或者正在学习数据结构的人群有帮助。git

链表 Linked List

  1. 链表是最基础的动态数据结构
  2. 链表是很是重要的线性数据结构github

    1. 如下三种,底层都是依托静态数组,靠 resize 解决固定容量问题。
    2. 动态数组:所谓动态,是从用户的角度上来看的。
    3. 队列
  3. 链表是真正的动态数据结构面试

    1. 它是数据结构中的一个重点,
    2. 也有多是一个难点,
    3. 它是最简单的一种动态数据结构,
    4. 其它更高级的动态数据结构有 二分搜索树、Trie、
    5. 平衡二叉树、AVL、红黑树等等,
    6. 熟悉了最简单的动态数据结构,
    7. 那么对于更高级的也会比较容易掌握了。
  4. 对于链表来讲它涉及到了计算机领域一个很是重要的概念算法

    1. 更深刻的理解引用(或者指针),
    2. 这个概念和内存相关,
    3. 在 java 里面不须要手动的管理内存,
    4. 可是对链表这种数据结构更加深刻的理解,
    5. 可让你对 引用、指针甚至计算机系统中
    6. 和内存管理相关不少话题有更加深刻的认识。
  5. 链表自己也是有它很是清晰的递归结构的,数组

    1. 只不过因为链表这种数据结构它自己是一种线性的数据结构,
    2. 因此依然能够很是容易的使用循环的方式来对链表进行操做,
    3. 可是链表自己因为它天生也是具备这种递归结构的性质,
    4. 因此它也能让你更加深刻的理解递归机制相应的这种数据结构,
    5. 在学习更加复杂的数据结构的时候,
    6. 对递归这种逻辑机制深刻的理解是必不可缺的。
  6. 链表这种数据结构自己就具备功能性数据结构

    1. 它能够用来组成更加复杂的数据结构,
    2. 好比 图结构、hash 表、栈(链表版)、队列(链表版),
    3. 因此他能够辅助组成其它数据结构。

什么是链表

  1. 数据存储在“节点”(Node)中app

    1. 把数据存在一种单独的结构中,
    2. 这个结构一般管它叫作节点,
    3. 对于链表节点来讲他一般只有两部分,
    4. 一部分就是存储真正的数据,
    5. 另外一部分存储的是当前节点的下一个节点,
    6. 链表其实就像一个火车,每个节点都是一节车箱,
    7. 在车箱中存储数据,而车箱和车箱之间还会进行链接,
    8. 以使得这些数据是整合在一块儿的,
    9. 这样用户能够方便的在全部的这些数据上进行查询等等其它的操做,
    10. 数据与数据之间链接就是用下面的这个 next 来完成的
    class Node {
          E e;
          Node next;
       }
  2. 链表的优势

    1. 真正的动态,不须要处理固定容量的问题,
    2. 它不像静态数组那样一会儿 new 出来一片空间,
    3. 也根本不须要考虑这个空间是否是不够用,
    4. 也根本不用去考虑这个空间是否是开多了。
  3. 对于链表来讲,你须要多少个数据。

    1. 你就能够生成多少个节点,
    2. 把它们挂接起来了,这就是所谓的动态。
  4. 链表的缺点

    1. 和数组相比,它丧失了随机访问的能力。
    2. 不能像数组那样给一个索引
    3. 就能直接访问对应索引位置的元素,
    4. 这是由于从底层机制上数组所开辟的空间
    5. 在内存里是连续分布的,
    6. 因此才可以直接去向索引对应的偏移
    7. 计算出相应的数据所存储的内存地址,
    8. 而后就可以直接用O(1)的复杂度取出这个元素,
    9. 可是链表不一样,链表因为是靠 next 一层一层链接的,
    10. 因此在计算机的底层,每个节点他所在的内存的位置是不一样的,
    11. 只可以靠这个 next 来一层一层的找到这个元素,
    12. 这就是链表最大的缺点。

数组和链表的对比

  1. 数组

    1. 数组最好永远索引有语意的状况,如 scores[2]
    2. 最大的优势:支持快速查询
  2. 链表

    1. 链表不适合用于索引有语意的状况。
    2. 最大的优势:动态存储。
  3. 对比

    1. 数组也能够没有语意,并非全部的时候索引是有语意的,
    2. 也并非全部有语意的这样的一个标志就适合作索引,如身份证号,
    3. 将一个静态数组改变为一个动态数组,
    4. 就是在应付不方便使用索引的时候有关数据存储的问题,
    5. 对于这样的存储数据的需求使用链表是更合适的,
    6. 由于链表最大的优势是动态存储。
  4. 要清楚何时使用数组这样的静态数据结构,

    1. 何时使用链表这类的动态数据结构。
  5. 简单的代码示例MyLinkedList

    public class MyLinkedList<E> {
    
             // 隐藏内部实现,不须要让用户知道
             private class Node {
                   public E e;
                   public Node next;
    
                   public Node (E e, Node next) {
                         this.e = e;
                         this.next = next;
                   }
    
                   public Node (E e) {
                         this(e, null);
                   }
    
                   public Node () {
                         this(null, null);
                   }
    
                   @Override
                   public String toString () {
                         return e.toString();
                   }
             }
       }

在链表中进行添加、插入操做

  1. 链表是经过节点来装载元素

    1. 而且节点和节点之间会进行链接。
    2. 若是想访问这条链表中全部的节点,
    3. 那么就必须把链表的头给存储起来,
    4. 一般这个链表的头叫作的 head,
    5. 应该说在MyLinkedList
    6. 应该有一个变量指向链表中的第一个节点。
  2. 给自定义数组添加元素是从数组尾部开始添加,

    1. 给链表添加元素是从数组头部开始添加,
    2. 由于自定义数组有维护一个 size,
    3. 这个 size 指向的是下一个待添加元素的位置,
    4. 因此你直接将 size 作索引直接赋值便可,
    5. 有 size 这个变量在跟踪数组的尾巴,
    6. 而对于链表来讲 有设置一个链表的头这样的一个变量,
    7. 也就是说有一个变量在跟踪链表的头,
    8. 并无一个变量去跟踪链表的尾巴,
    9. 因此在链表头部添加元素是很是方便。
  3. 添加操做原理

    1. 将一个新的元素挂接到链表头部,
    2. 同时不破坏如今链表的这个结构,
    3. 添加一个新元素 666,它的名字叫 node,
    4. 就只须要使用 node.next = head
    5. 也就是将新添加的元素的 next 指向链表的头,
    6. 这个时候新添加的元素也成为新的链表头,
    7. 也就是head = node
    8. 这样 head 就会指向新的元素 666,也就是 node 这个节点,
    9. 如此一来就完成了将 node 这个节点插入了整个链表的头部,
    10. node 这个变量是在一个函数中声明的,
    11. 因此函数结束以后这个变量的做用域也就结束了,
    12. 链表的 head 也等于 node,
    13. 链表中就新增了一个新元素。
  4. 在链表头部添加元素很是简单,

    1. 只须要建立节点,让节点的 next 指向 head,
    2. 而后让 head 从新指向这个节点,也就是让 head 变成新的元素,
    3. 由于链表是从头部添加元素的。
  5. 在链表中间添加元素,

    1. 定义一个 prev 的变量指向中间元素的前一个元素,
    2. 而后让新增长的元素这样,node.next = prev.next
    3. 以后这样,prev.next = node
    4. 这样一来就在 prev 这个元素和本来 prev 的下一个元素之间
    5. 插入了一个新元素,叫作 node,
    6. 整个过程就是将 prev 的 next(下一个元素)的引用
    7. 传递给新元素的 next(下一个元素),
    8. 而后将 prev 的 next(下一个元素)变动为这个新元素,
    9. 最后就将新元素插入到中间了。
    10. 这个过程的关键是找到待添加的节点的前一个节点,
    11. 这个就是 prev 变量要作的事情。
  6. 在链表的操做中不少时候顺序很是重要,

    1. 如在链表中插入一个元素,在指定的元素后面插入一个元素,
    2. 而且不影响整个链表结构,和在链表中间添加元素同样,
    3. 把本来的node.next = prev.nextprev.next = node
    4. 的顺序变一下,prev.next = node在前,
    5. node.next = prev.next 在后,这样一来逻辑就不成立了,
    6. 链表不只断开了,并且新节点的 next 指向的是本身,死循环了,
    7. 因此同样的代码,顺序颠倒了,获得的结果就是错误的。
  7. 在链表的 index(0-based)位置添加元素 e

    1. 经过索引来操做链表,在链表中不是一个经常使用的操做,
    2. 由于在选择使用链表的时候一般就选择不使用索引了,
    3. 可是这个需求在面试等一些考题中出现的几率很大,
    4. 因此他的主要做用更多的是一个练习。

学习方式

  1. 若是刚接触链表,对链表不熟悉,

    1. 而后不少这种操做在脑子里不能很是快的反应出来,
    2. 不清楚他的意义是什么,那你可使用纸笔来稍微画一下,
    3. 每一步程序逻辑的执行意味着当前这个链表变成了什么样子,
    4. 这个过程可以帮助你更加深刻的理解程序,
    5. 包括在调试的时候来看你的程序到底为何出了错误时,
    6. 很是很是有帮助的,不要犯懒,为了更加深刻的理解你的程序,
    7. 不能只盯着你的代码去想,必定要写写画画,
    8. 必要的时候甚至根据你的程序进行具体的调试,
    9. 这些都是很是重要很是有帮助的。

代码示例 (class: MyLinkedList)

  1. MyLinkedList

    public class MyLinkedList<E> {
    
             // 隐藏内部实现,不须要让用户知道
             private class Node {
                   public E e;
                   public Node next;
    
                   public Node (E e, Node next) {
                         this.e = e;
                         this.next = next;
                   }
    
                   public Node (E e) {
                         this(e, null);
                   }
    
                   public Node () {
                         this(null, null);
                   }
    
                   @Override
                   public String toString () {
                         return e.toString();
                   }
             }
    
             private Node head;
             private int size;
    
             public MyLinkedList () {
                   head = null;
                   size = 0;
             }
    
             // ...
             // 其它的构造函数,例如传进来一个数组,将数组转换为链表
    
             // 获取链表中元素的个数
             public int getSize () {
                   return size;
             }
    
             // 返回当前链表是否为空
             public boolean isEmpty () {
                   return size == 0;
             }
    
             // 在链表头部添加一个元素 e
             public void addFirst (E e) {
       //        写法一
       //        Node node = new Node(e, head);
       //        head = node;
    
       //        写法二
       //        Node node = new Node(e);
       //        node.next = head;
       //        head = node;
    
       //        写法三
                   head = new Node(e, head);
                   size ++;
             }
    
             // 在链表指定索引出插入一个元素
             public void insert (int index, E e) {
    
                   if (index < 0 || index > size) {
                         throw new IllegalArgumentException("add error. index < 0 or index > size");
                   }
                   if (index == 0) {
                         addFirst(e);
                   }else {
                         // 第一个prev就是head
                         Node prev = head;
       //            不断的搜索 一直经过next来进行检索
                         for (int i = 0; i < index - 1 ; i++) {
                               prev = prev.next;
                         }
       //            第一种方式
       //            Node node = new Node(e);
       //            node.next = prev.next;
       //            prev.next = node;
    
       //            第二种方式
                         prev.next = new Node(e, prev.next);
                         size ++;
    
                   }
             }
    
             // 在链表尾部添加一个元素
             public void addLast (E e) {
    
                   insert(size, e);
             }
       }

给链表设立虚拟头节点

  1. 在链表中进行指定索引处插入元素时

    1. 对链表头插入元素与对链表其它位置插入元素的逻辑有区别,
    2. 因此就致使每次操做都须要对索引进行判断,
    3. 若是你是在索引 0 的位置插入元素,那么就没有 prev(前一个元素),
    4. 天然就不可使用node.next = prev.nextprev.next = node了,
    5. 那时候你能够直接使用node.next = headhead = node
    6. 不过已经有了向链表头添加元素的方法了,
    7. 因此向头部插入元素时根本就不须要这么作,
    8. 这时候能够经过给链表设立虚拟头节点来解决这个问题。
  2. 为何对链表头插入元素那么特殊?

    1. 由于插入元素时,必须要找到该索引位置的节点以前的一个节点(prev),
    2. 而链表头(head)以前并无任何节点,因此逻辑上就会特殊一些。
    3. 不过在链表的具体实现中有一个很是经常使用的技巧,
    4. 能够把链表头的这种特殊的操做与其它的操做一块儿统一块儿来,
    5. 其实这个方法的想法很简单,既然它没有链表头以前的节点,
    6. 那就本身造一个链表头以前的节点,
    7. 这个链表头以前的节点不会用来存储任何元素,存储的数据为空,
    8. 这个空节点就称之为整个链表头的 head,也能够叫它 dummyHead,
    9. 由于它就是一个假的 head,它也是为链表设立的虚拟头节点,
    10. 这样一来 链表的第一个元素就是 dummyHead 的 next 所对应的那个节点,
    11. 由于 dummyHead 这个位置的元素是根本不存在的,
    12. 对用户来说也是根本没有意义的,
    13. 这只是为了编写逻辑方便而出现的一个虚拟的头节点,
    14. 因此一个这样的内部机制对用户来讲也是不可见的,
    15. 用户不知道链表中有没有虚拟的头节点,
    16. 这和自定义的循环队列有点像,自定义循环队列中有一个不可用的空间,
    17. 有意识地去浪费一个空间,为了编写逻辑更加的方便,
    18. 从而也让性能在总体上获得了提高。
  3. 有了 dummyHead 以后就不须要处理头节点这个特殊的操做

    1. 由于当你在头部插入元素时,能够这样
    2. node.next = dummyHead.nextdummyHead.next = node
    3. 这样你就解决了每次插入时都要进行一次逻辑判断了,
    4. 这样一来就说明了链表中全部的节点都有前一个节点了,
    5. 在初始的时候 dummyHead 指向的是索引为 0 的元素前一个位置的节点。
  4. 链表操做的实际原理

    1. 在没有使用虚拟头节点以前,一直都是在链表头 head 这个地方不停的添加节点,
    2. 而后将 head 这个变量不停的指向新添加的节点 node.next = head.next;head = node;
    3. 在使用了虚拟头节点以后,
    4. 一直是在链表虚拟头 dummyHead 这个地方后面一个位置不停的插入新节点,
    5. dummyHead 从未变更过,永远在链表的第一个位置,
    6. node.next = dummyHead.next; dummyHead.next = node;
    7. 可是dummyHead.next才是链表中第一个实际记录的节点,
    8. 用户会不知道虚拟节点的存在,由于 dummyHead 并不算在链表的实际节点中,
    9. 也就是 size 中不会维护 dummyHead,dummyHead 的存在是为了维护一致的插入操做。

代码示例 (class: MyLinkedList)

  1. MyLinkedList

    public class MyLinkedList<E> {
    
             // 隐藏内部实现,不须要让用户知道
             private class Node {
                   public E e;
                   public Node next;
    
                   public Node (E e, Node next) {
                         this.e = e;
                         this.next = next;
                   }
    
                   public Node (E e) {
                         this(e, null);
                   }
    
                   public Node () {
                         this(null, null);
                   }
    
                   @Override
                   public String toString () {
                         return e.toString();
                   }
             }
    
             private Node dummyHead;
             private int size;
    
             public MyLinkedList () {
                   dummyHead = new Node(null, null);
                   size = 0;
             }
    
             // ...
             // 其它的构造函数,例如传进来一个数组,将数组转换为链表
    
             // 获取链表中元素的个数
             public int getSize () {
                   return size;
             }
    
             // 返回当前链表是否为空
             public boolean isEmpty () {
                   return size == 0;
             }
    
             // 在链表头部添加一个元素 e
             public void addFirst (E e) {
       //        写法一
       //        Node node = new Node(e, head);
       //        head = node;
    
       //        写法二
       //        Node node = new Node(e);
       //        node.next = dummyHead.next;
       //        dummyHead.next = node;
    
       //        写法三
       //        dummyHead.next = new Node(e, dummyHead.next);
       //        size ++;
    
       //        写法四
                   insert(0, e);
             }
    
             // 在链表指定索引出插入一个元素
             public void insert (int index, E e) {
    
                   if (index < 0 || index > size) {
                         throw new IllegalArgumentException("add error. index < 0 or index > size");
                   }
    
                   // 第一个prev就是dummyHead
                   Node prev = dummyHead;
       //            不断的搜索 一直经过next来进行检索
                   for (int i = 0; i < index ; i++) {
                         prev = prev.next;
                   }
       //            第一种方式
       //            Node node = new Node(e);
       //            node.next = prev.next;
       //            prev.next = node;
    
       //            第二种方式
                   prev.next = new Node(e, prev.next);
                   size ++;
             }
    
             // 在链表尾部添加一个元素
             public void addLast (E e) {
    
                   insert(size, e);
             }
       }

在链表中进行遍历、查询、修改操做

  1. 若是要找指定索引元素的前一个节点

    1. 那么就要从dummyHead开始遍历,
    2. 若是要找指定索引元素的那个节点,
    3. 那么就要从dummyHead.next开始遍历。

代码示例(class: MyLinkedList, class: Main)

  1. MyLinkedList

    public class MyLinkedList<E> {
    
             // 隐藏内部实现,不须要让用户知道
             private class Node {
                   public E e;
                   public Node next;
    
                   public Node (E e, Node next) {
                         this.e = e;
                         this.next = next;
                   }
    
                   public Node (E e) {
                         this(e, null);
                   }
    
                   public Node () {
                         this(null, null);
                   }
    
                   @Override
                   public String toString () {
                         return e.toString();
                   }
             }
    
             private Node dummyHead;
             private int size;
    
             public MyLinkedList () {
                   dummyHead = new Node(null, null);
                   size = 0;
             }
    
             // ...
             // 其它的构造函数,例如传进来一个数组,将数组转换为链表
    
             // 获取链表中元素的个数
             public int getSize () {
                   return size;
             }
    
             // 返回当前链表是否为空
             public boolean isEmpty () {
                   return size == 0;
             }
    
             // 在链表头部添加一个元素 e
             public void addFirst (E e) {
       //        写法一
       //        Node node = new Node(e, head);
       //        head = node;
    
       //        写法二
       //        Node node = new Node(e);
       //        node.next = dummyHead.next;
       //        dummyHead.next = node;
    
       //        写法三
       //        dummyHead.next = new Node(e, dummyHead.next);
       //        size ++;
    
       //        写法四
                   insert(0, e);
             }
    
             // 在链表指定索引出插入一个元素
             public void insert (int index, E e) {
    
                   if (index < 0 || index > size) {
                         throw new IllegalArgumentException("add or insert error. index < 0 or index > size");
                   }
    
                   // 第一个prev就是dummyHead
                   Node prev = dummyHead;
       //            不断的搜索 一直经过next来进行检索,找指定索引的节点的前一个元素
                   for (int i = 0; i < index ; i++) {
                         prev = prev.next;
                   }
       //            第一种方式
       //            Node node = new Node(e);
       //            node.next = prev.next;
       //            prev.next = node;
    
       //            第二种方式
                   prev.next = new Node(e, prev.next);
                   size ++;
             }
    
             // 在链表尾部添加一个元素
             public void addLast (E e) {
    
                   insert(size, e);
             }
    
             // get
             public E get (int index) {
    
                   if (index < 0 || index >= size) {
                         throw new IllegalArgumentException("get error. index < 0 or index >= size");
                   }
    
                   Node cur = dummyHead.next;
                   for (int i = 0; i < index ; i++) {
                         cur = cur.next;
                   }
    
                   return cur.e;
             }
    
             // getFirst
             public E getFirst () {
                   return get(0);
             }
    
             // getLast
             public E getLast () {
                   return get(size - 1);
             }
    
             // set
             public void set (int index, E e) {
    
                   if (index < 0 || index >= size) {
                         throw new IllegalArgumentException("set error. index < 0 or index >= size");
                   }
    
                   Node node = dummyHead.next;
                   for (int i = 0; i < index; i++) {
                         node = node.next;
                   }
                   node.e = e;
             }
    
             // contains
             public boolean contains (E e) {
    
       //        第一种方式
       //        Node node = dummyHead;
       //        for (int i = 0; i < size - 1 ; i++) {
       //            node = node.next;
       //
       //            if (node.e.equals(e)) {
       //                return true;
       //            }
       //        }
    
       //        第二种方式
                   Node node = dummyHead.next;
                   while (node != null) {
                         if (node.e.equals(e)) {
                               return true;
                         } else {
                               node = node.next;
                         }
                   }
                   return  false;
             }
    
             @Override
             public String toString () {
    
                   StringBuilder sb = new StringBuilder();
                   sb.append("链表长度:" + size + ",链表信息:");
       //        // 写法一
       //        Node node = dummyHead.next;
       //        while (node != null) {
       //            sb.append(node + "->");
       //            node = node.next;
       //        }
       //        写法二
                   for (Node node = dummyHead.next; node != null ; node = node.next) {
                         sb.append(node + "->");
                   }
    
                   sb.append("NULL");
                   return sb.toString();
             }
       }
  2. Main

    public class Main {
    
             public static void main(String[] args) {
                   MyLinkedList<Integer> mkl = new MyLinkedList<Integer>();
    
                   for (int i = 1; i <= 5 ; i++) {
                         mkl.addFirst(i);
                         System.out.println(mkl);
                   }
                   mkl.insert(2, 88888);
                   System.out.println(mkl);
             }
       }

在链表中进行删除操做

  1. 链表元素的删除

    1. 假设要删除索引为 2 位置的元素
    2. 就是要索引为 2 位置的元素的前一个元素,
    3. 还要索引为 2 位置的元素的后一个元素,
    4. 让前一个元素的 next 等于后一个元素,
    5. 也就是前一个元素的 next 等于索引为 2 的元素的 next。
    6. 也就是让 prev 指向待删除的那个节点的前一个节点,
    7. prev 这个节点的 next 就是要删除的那个节点 delNode,
    8. 而后让prev.next = delNode.next
    9. 这样就让待删除的那个节点的前一个节点的 next,
    10. 也就是直接指向了待删除的那个节点的后一个节点了,
    11. 而后让delNode.next = null 就完成了删除,
    12. 千万不要这样delNode = delNode.next
    13. 这个并非删除,而是将待删除节点的引用指向了下一个节点,
    14. 经过设置为 null 才是真正的与当前链表脱离关系。

代码示例(class: MyLinkedList, class: Main)

  1. MyLinkedList

    public class MyLinkedList<E> {
    
             // 隐藏内部实现,不须要让用户知道
             private class Node {
                   public E e;
                   public Node next;
    
                   public Node (E e, Node next) {
                         this.e = e;
                         this.next = next;
                   }
    
                   public Node (E e) {
                         this(e, null);
                   }
    
                   public Node () {
                         this(null, null);
                   }
    
                   @Override
                   public String toString () {
                         return e.toString();
                   }
             }
    
             private Node dummyHead;
             private int size;
    
             public MyLinkedList () {
                   dummyHead = new Node(null, null);
                   size = 0;
             }
    
             // ...
             // 其它的构造函数,例如传进来一个数组,将数组转换为链表
    
             // 获取链表中元素的个数
             public int getSize () {
                   return size;
             }
    
             // 返回当前链表是否为空
             public boolean isEmpty () {
                   return size == 0;
             }
    
             // 在链表头部添加一个元素 e
             public void addFirst (E e) {
       //        写法一
       //        Node node = new Node(e, head);
       //        head = node;
    
       //        写法二
       //        Node node = new Node(e);
       //        node.next = dummyHead.next;
       //        dummyHead.next = node;
    
       //        写法三
       //        dummyHead.next = new Node(e, dummyHead.next);
       //        size ++;
    
       //        写法四
                   insert(0, e);
             }
    
             // 在链表指定索引出插入一个元素
             public void insert (int index, E e) {
    
                   if (index < 0 || index > size) {
                         throw new IllegalArgumentException("add or insert error. index < 0 or index > size");
                   }
    
                   // 第一个prev就是dummyHead
                   Node prev = dummyHead;
       //            不断的搜索 一直经过next来进行检索,找指定索引的节点的前一个元素
                   for (int i = 0; i < index ; i++) {
                         prev = prev.next;
                   }
       //            第一种方式
       //            Node node = new Node(e);
       //            node.next = prev.next;
       //            prev.next = node;
    
       //            第二种方式
                   prev.next = new Node(e, prev.next);
                   size ++;
             }
    
             // 在链表尾部添加一个元素
             public void addLast (E e) {
    
                   insert(size, e);
             }
    
             // get
             public E get (int index) {
    
                   if (index < 0 || index >= size) {
                         throw new IllegalArgumentException("get error. index < 0 or index >= size");
                   }
    
                   Node cur = dummyHead.next;
                   for (int i = 0; i < index ; i++) {
                         cur = cur.next;
                   }
    
                   return cur.e;
             }
    
             // getFirst
             public E getFirst () {
                   return get(0);
             }
    
             // getLast
             public E getLast () {
                   return get(size - 1);
             }
    
             // set
             public void set (int index, E e) {
    
                   if (index < 0 || index >= size) {
                         throw new IllegalArgumentException("set error. index < 0 or index >= size");
                   }
    
                   Node node = dummyHead.next;
                   for (int i = 0; i < index; i++) {
                         node = node.next;
                   }
                   node.e = e;
             }
    
             // contains
             public boolean contains (E e) {
    
       //        第一种方式
       //        Node node = dummyHead;
       //        for (int i = 0; i < size - 1 ; i++) {
       //            node = node.next;
       //
       //            if (node.e.equals(e)) {
       //                return true;
       //            }
       //        }
    
       //        第二种方式
                   Node node = dummyHead.next;
                   while (node != null) {
                         if (node.e.equals(e)) {
                               return true;
                         } else {
                               node = node.next;
                         }
                   }
                   return  false;
             }
    
             // remove
             public E remove (int index) {
    
                   if (index < 0 || index >= size) {
                         throw new IllegalArgumentException("remove error. index < 0 or index >= size");
                   }
    
                   Node prev = dummyHead;
                   for (int i = 0; i < index ; i++) {
                         prev = prev.next;
                   }
                   Node delNode = prev.next;
                   prev.next = delNode.next;
                   size --;
    
                   E e = delNode.e;
                   delNode.next = null;
    
                   return e;
             }
    
             // removeFirst
             public E removeFirst () {
                 return remove(0);
             }
    
             // removeLast
             public E removeLast () {
                   return remove(size - 1);
             }
    
             @Override
             public String toString () {
    
                   StringBuilder sb = new StringBuilder();
                   sb.append("链表长度:" + size + ",链表信息:");
       //        // 写法一
       //        Node node = dummyHead.next;
       //        while (node != null) {
       //            sb.append(node + "->");
       //            node = node.next;
       //        }
       //        写法二
                   for (Node node = dummyHead.next; node != null ; node = node.next) {
                         sb.append(node + "->");
                   }
    
                   sb.append("NULL");
                   return sb.toString();
             }
       }
  2. Main

    public class Main {
    
             public static void main(String[] args) {
                   MyLinkedList<Integer> mkl = new MyLinkedList<Integer>();
    
                   for (int i = 1; i <= 5 ; i++) {
                         mkl.addFirst(i);
                         System.out.println(mkl);
                   }
                   mkl.insert(2, 88888);
                   System.out.println(mkl);
    
                   mkl.remove(2);
                   System.out.println(mkl);
    
                   mkl.removeFirst();
                   System.out.println(mkl);
    
                   mkl.removeLast();
                   System.out.println(mkl);
             }
       }

链表的时间复杂度分析

  1. 增:O(n):在只对链表头进行操做时为O(1)
  2. 删:O(n):在只对链表头进行操做时为O(1)
  3. 改:O(n)
  4. 查:O(n):只查链表头的元素时为O(1)
  5. 链表增删改查的时间复杂度

    1. 总体上要比数组增删改查的时间复杂度要差,
    2. 由于数组有一个优点,
    3. 你有索引的话你就能够快速访问,
    4. 而链表没有这样的优点
    5. 可是链表的优点是,
    6. 若是你只对链表头进行增删的操做,
    7. 那么它的时间复杂度是 O(1)级别的,
    8. 并且它总体是动态的,因此不会大量的浪费内存空间。 6.链表适合作的事情
    9. 不去修改、也不去查任意的元素,
    10. 就算查,也只能查链表头的元素,
    11. 查的时候,只有那样才是 O(1)级别的时间复杂度,
    12. 而且增长删除的时候只对链表头进行操做,
    13. 只有在这种时候才会和数组总体的时间复杂度是同样的,
    14. 不过链表总体是动态的,不会去大量的浪费内存空间,
    15. 因此它具备必定的优点。
  6. 链表还有诸多的改进的方式

    1. 因此应用链表在一些状况下仍然是有它的优点的,
    2. 最最重要的是 链表自己是一种最最基础的动态数据结构,
    3. 对这种动态数据结构要深入的理解和掌握,
    4. 对于学习更重要的动态数据结构是有巨大的帮助,
    5. 好比说二叉树、平衡二叉树、AVL、红黑树等等。

 添加操做 O(n)

  1. addLast(e)O(n)

    1. 会从头遍历到尾,
    2. 找到最后一个节点以后才能添加元素
  2. addFirst(e)O(1)

    1. 直接从头部添加元素
  3. insert(index, e)O(n/2) = O(n)

    1. 会去遍历链表中每个节点,
    2. 检索到合适的位置才会添加这个元素。

删除操做 O(n)

  1. removeLast()O(n)

    1. 会去遍历链表中每个节点,
    2. 找到最后一个节点后才会删除
  2. removeFirst()O(1)

    1. 直接从头部删除这个节点
  3. remove(index)O(n/2) = O(n)

    1. 会去遍历链表中每个节点,
    2. 检索到合适的位置才会移除这个元素

修改操做 O(n)

  1. set(index, e)O(n)

    1. 会去遍历链表中每个节点,
    2. 找到了合适位置的节点后,
    3. 才会修改元素

查找操做 O(n)

  1. get(index)O(n)

    1. 会去遍历链表中每个节点,
    2. 找到了合适位置的节点后,
    3. 返回这个元素。
  2. contains(e)O(n)

    1. 会去遍历链表中每个节点,
    2. 返回 是否有相同元素的 bool 值。
  3. find(e)O(n)

    1. 这个方法对链表来讲是没有用的,
    2. 就算你拿到了那个索引你也不能快速访问。

使用链表来实现栈

  1. 对链表进行添加操做时

    1. 时间复杂度为O(n)
    2. 可是在只对链表头进行操做时为O(1)
  2. 对链表进行删除操做时

    1. 时间复杂度为O(n)
    2. 可是在只对链表头进行操做时为O(1)
  3. 对链表进行查询操做时

    1. 时间复杂度为O(n)
    2. 可是在只查链表头的元素时为O(1)
  4. 这些特性很符合栈的需求

    1. 栈是后进先出的,而且栈只查栈顶的元素,
    2. 因此可使用链表来实现栈这样的数据结构。

链表实现的栈

  1. 首先定义接口IMyLinkedListStack

    1. 而后让MyLinkedListStack来实现这些接口。
  2. IMyLinkedListStack

    1. void push(E e):添加一个元素
    2. E pop():移除一个元素
    3. E peek():查看栈顶的元素
    4. int getSize():获取栈中实际的元素个数
    5. boolean isEmpty():判断栈是否为空

代码示例

  1. (Interface: IMyLinkedListStack, class: MyLinkedList,

    1. class: MyLinkedListStack, class: Main)
  2. IMyLinkedListStack

    public interface IMyLinkedListStack<E> {
             /**
              * @param e
*/
        void push (E e);

        /**
         * @return e
         * 出栈
         */
        E pop ();

        /**
         * @return e
         * 查看栈顶的一个元素
         */
        E peek ();

        /**
         * @return size
         * 查看栈中实际元素的个数
         */
        int getSize ();

        /**
         * @return not empty
         * 判断栈中是否为空
         */
        boolean isEmpty ();
  }
3. `MyLinkedList`
public class MyLinkedList<E> {

        // 隐藏内部实现,不须要让用户知道
        private class Node {
              public E e;
              public Node next;

              public Node (E e, Node next) {
                    this.e = e;
                    this.next = next;
              }

              public Node (E e) {
                    this(e, null);
              }

              public Node () {
                    this(null, null);
              }

              @Override
              public String toString () {
                    return e.toString();
              }
        }

        private Node dummyHead;
        private int size;

        public MyLinkedList () {
              dummyHead = new Node(null, null);
              size = 0;
        }

        // ...
        // 其它的构造函数,例如传进来一个数组,将数组转换为链表

        // 获取链表中元素的个数
        public int getSize () {
              return size;
        }

        // 返回当前链表是否为空
        public boolean isEmpty () {
              return size == 0;
        }

        // 在链表头部添加一个元素 e
        public void addFirst (E e) {
  //        写法一
  //        Node node = new Node(e, head);
  //        head = node;

  //        写法二
  //        Node node = new Node(e);
  //        node.next = dummyHead.next;
  //        dummyHead.next = node;

  //        写法三
  //        dummyHead.next = new Node(e, dummyHead.next);
  //        size ++;

  //        写法四
              insert(0, e);
        }

        // 在链表指定索引出插入一个元素
        public void insert (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("add or insert error. index < 0 or index > size");
              }

              // 第一个prev就是dummyHead
              Node prev = dummyHead;
  //            不断的搜索 一直经过next来进行检索,找指定索引的节点的前一个元素
              for (int i = 0; i < index ; i++) {
                    prev = prev.next;
              }
  //            第一种方式
  //            Node node = new Node(e);
  //            node.next = prev.next;
  //            prev.next = node;

  //            第二种方式
              prev.next = new Node(e, prev.next);
              size ++;
        }

        // 在链表尾部添加一个元素
        public void addLast (E e) {

              insert(size, e);
        }

        // get
        public E get (int index) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("get error. index < 0 or index > size");
              }

              Node cur = dummyHead.next;
              for (int i = 0; i < index ; i++) {
                    cur = cur.next;
              }

              return cur.e;
        }

        // getFirst
        public E getFirst () {
              return get(0);
        }

        // getLast
        public E getLast () {
              return get(size - 1);
        }

        // set
        public void set (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("set error. index < 0 or index > size");
              }

              Node node = dummyHead.next;
              for (int i = 0; i < index; i++) {
                    node = node.next;
              }
              node.e = e;
        }

        // contains
        public boolean contains (E e) {

  //        第一种方式
  //        Node node = dummyHead;
  //        for (int i = 0; i < size - 1 ; i++) {
  //            node = node.next;
  //
  //            if (node.e.equals(e)) {
  //                return true;
  //            }
  //        }

  //        第二种方式
              Node node = dummyHead.next;
              while (node != null) {
                    if (node.e.equals(e)) {
                          return true;
                    } else {
                          node = node.next;
                    }
              }
              return  false;
        }

        // remove
        public E remove (int index) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("remove error. index < 0 or index > size");
              }

              Node prev = dummyHead;
              for (int i = 0; i < index ; i++) {
                    prev = prev.next;
              }
              Node delNode = prev.next;
              prev.next = delNode.next;
              size --;

              E e = delNode.e;
              delNode.next = null;

              return e;
        }

        // removeFirst
        public E removefirst () {
            return remove(0);
        }

        // removeLast
        public E removeLast () {
              return remove(size - 1);
        }

        @Override
        public String toString () {

              StringBuilder sb = new StringBuilder();
              sb.append("链表长度:" + size + ",链表信息:");
  //        // 写法一
  //        Node node = dummyHead.next;
  //        while (node != null) {
  //            sb.append(node + "->");
  //            node = node.next;
  //        }
  //        写法二
              for (Node node = dummyHead.next; node != null ; node = node.next) {
                    sb.append(node + "->");
              }

              sb.append("NULL");
              return sb.toString();
        }
  }
4. `MyLinkedListStack`
public class MyLinkedListStack<E> implements IMyLinkedListStack<E> {
        private MyLinkedList<E> mkl;

        public MyLinkedListStack () {
              mkl = new MyLinkedList<E>();
        }

        /**
         * @param e 入栈
         */
        @Override
        public void push (E e) {
              mkl.addFirst(e);
        }

        /**
         * @return e
         * 出栈
         */
        @Override
        public E pop () {
              return mkl.removefirst();
        }

        /**
         * @return e
         * 查看栈顶的一个元素
         */
        @Override
        public E peek () {
              return mkl.getFirst();
        }

        /**
         * @return size
         * 查看栈中实际元素的个数
         */
        @Override
        public int getSize () {
              return mkl.getSize();
        }

        /**
         * @return not empty
         * 判断栈中是否为空
         */
        @Override
        public boolean isEmpty () {
              return mkl.isEmpty();
        }

        @Override
        public String toString () {
              int size = getSize();

              StringBuilder sb = new StringBuilder();
              sb.append("MyLinkedlistStack: 元素个数=" + size);
              sb.append(", stack top=[ ");
              for (int i = 0; i < size ; i++) {
                    sb.append(mkl.get(i));
                    sb.append("->");
              }
              sb.append("NULL ]");

              return sb.toString();
        }
  }
5. `Main`

public class Main {

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

           for (int i = 1; i <= 5 ; i++) {
                 mkls.push(i);
                 System.out.println(mkls);
           }

           System.out.println(mkls.peek());

           for (int i = 0; i < 5 ; i++) {
                 System.out.println(mkls);
                 mkls.pop();
           }
     }

}

## 自定义数组栈对比自定义链表栈

1. 自定义数组栈与自定义链表栈的性能相差不多
   1. 可是随着操做的次数增加,数组栈会慢慢强过链表栈,
   2. 自定义链表栈中有太多的 new 操做,
   3. new 操做在有一些系统上比较耗费性能的,
   4. 由于它在不停的在内存中寻找能够开辟空间的地方来进行开辟空间,
   5. 自定义数组栈中有比较多的扩容操做,
   6. 因此这个比较是相对比较复杂的,
   7. 和你的语法、操做系统、编译器、解释器都有关系,
   8. 不过他们的时间复杂度都是`O(1)`级别的,
   9. 因此他们之间的性能差别无非就 1-2 倍这样,
   10.   在最极端的状况下 3-5 倍就已经很难了,
   11.   不会有几百倍的巨大的差别,由于毕竟他们的时间复杂度同样。

### 代码示例

1. `(Interface: IStack, class: MyLinkedList,`
   1. `class: MyLinkedListStack, class: MyArray,`
   2. `class: MyStack, class: Main)`
2. `IStack`
public interface IStack<E> {
        /**
         * @param e
         * 入栈
         */
        void push (E e);

        /**
         * @return e
         * 出栈
         */
        E pop ();

        /**
         * @return e
         * 查看栈顶的一个元素
         */
        E peek ();

        /**
         * @return size
         * 查看栈中实际元素的个数
         */
        int getSize ();

        /**
         * @return not empty
         * 判断栈中是否为空
         */
        boolean isEmpty ();
  }
3. `MyLinkedList`
public class MyLinkedList<E> {

        // 隐藏内部实现,不须要让用户知道
        private class Node {
              public E e;
              public Node next;

              public Node (E e, Node next) {
                    this.e = e;
                    this.next = next;
              }

              public Node (E e) {
                    this(e, null);
              }

              public Node () {
                    this(null, null);
              }

              @Override
              public String toString () {
                    return e.toString();
              }
        }

        private Node dummyHead;
        private int size;

        public MyLinkedList () {
              dummyHead = new Node(null, null);
              size = 0;
        }

        // ...
        // 其它的构造函数,例如传进来一个数组,将数组转换为链表

        // 获取链表中元素的个数
        public int getSize () {
              return size;
        }

        // 返回当前链表是否为空
        public boolean isEmpty () {
              return size == 0;
        }

        // 在链表头部添加一个元素 e
        public void addFirst (E e) {
  //        写法一
  //        Node node = new Node(e, head);
  //        head = node;

  //        写法二
  //        Node node = new Node(e);
  //        node.next = dummyHead.next;
  //        dummyHead.next = node;

  //        写法三
  //        dummyHead.next = new Node(e, dummyHead.next);
  //        size ++;

  //        写法四
              insert(0, e);
        }

        // 在链表指定索引出插入一个元素
        public void insert (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("add or insert error. index < 0 or index > size");
              }

              // 第一个prev就是dummyHead
              Node prev = dummyHead;
  //            不断的搜索 一直经过next来进行检索,找指定索引的节点的前一个元素
              for (int i = 0; i < index ; i++) {
                    prev = prev.next;
              }
  //            第一种方式
  //            Node node = new Node(e);
  //            node.next = prev.next;
  //            prev.next = node;

  //            第二种方式
              prev.next = new Node(e, prev.next);
              size ++;
        }

        // 在链表尾部添加一个元素
        public void addLast (E e) {

              insert(size, e);
        }

        // get
        public E get (int index) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("get error. index < 0 or index > size");
              }

              Node cur = dummyHead.next;
              for (int i = 0; i < index ; i++) {
                    cur = cur.next;
              }

              return cur.e;
        }

        // getFirst
        public E getFirst () {
              return get(0);
        }

        // getLast
        public E getLast () {
              return get(size - 1);
        }

        // set
        public void set (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("set error. index < 0 or index > size");
              }

              Node node = dummyHead.next;
              for (int i = 0; i < index; i++) {
                    node = node.next;
              }
              node.e = e;
        }

        // contains
        public boolean contains (E e) {

  //        第一种方式
  //        Node node = dummyHead;
  //        for (int i = 0; i < size - 1 ; i++) {
  //            node = node.next;
  //
  //            if (node.e.equals(e)) {
  //                return true;
  //            }
  //        }

  //        第二种方式
              Node node = dummyHead.next;
              while (node != null) {
                    if (node.e.equals(e)) {
                          return true;
                    } else {
                          node = node.next;
                    }
              }
              return  false;
        }

        // remove
        public E remove (int index) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("remove error. index < 0 or index > size");
              }

              Node prev = dummyHead;
              for (int i = 0; i < index ; i++) {
                    prev = prev.next;
              }
              Node delNode = prev.next;
              prev.next = delNode.next;
              size --;

              E e = delNode.e;
              delNode.next = null;

              return e;
        }

        // removeFirst
        public E removefirst () {
            return remove(0);
        }

        // removeLast
        public E removeLast () {
              return remove(size - 1);
        }

        @Override
        public String toString () {

              StringBuilder sb = new StringBuilder();
              sb.append("链表长度:" + size + ",链表信息:");
  //        // 写法一
  //        Node node = dummyHead.next;
  //        while (node != null) {
  //            sb.append(node + "->");
  //            node = node.next;
  //        }
  //        写法二
              for (Node node = dummyHead.next; node != null ; node = node.next) {
                    sb.append(node + "->");
              }

              sb.append("NULL");
              return sb.toString();
        }
  }
4. `MyLinkedListStack`
public class MyLinkedListStack<E> implements IStack<E> {
        private MyLinkedList<E> mkl;

        public MyLinkedListStack () {
              mkl = new MyLinkedList<E>();
        }

        /**
         * @param e 入栈
         */
        @Override
        public void push (E e) {
              mkl.addFirst(e);
        }

        /**
         * @return e
         * 出栈
         */
        @Override
        public E pop () {
              return mkl.removefirst();
        }

        /**
         * @return e
         * 查看栈顶的一个元素
         */
        @Override
        public E peek () {
              return mkl.getFirst();
        }

        /**
         * @return size
         * 查看栈中实际元素的个数
         */
        @Override
        public int getSize () {
              return mkl.getSize();
        }

        /**
         * @return not empty
         * 判断栈中是否为空
         */
        @Override
        public boolean isEmpty () {
              return mkl.isEmpty();
        }

        @Override
        public String toString () {
              int size = getSize();

              StringBuilder sb = new StringBuilder();
              sb.append("MyLinkedlistStack: 元素个数=" + size);
              sb.append(", stack top=[ ");
              for (int i = 0; i < size ; i++) {
                    sb.append(mkl.get(i));
                    sb.append("->");
              }
              sb.append("NULL ]");

              return sb.toString();
        }
  }
5. `MyArray`
public class MyArray<E> {
        private E [] data;
        private int size;

        // 构造函数,传入数组的容量capacity构造Array
        public MyArray (int capacity) {
              data = (E[])new Object[capacity];
              size = 0;
        }

        // 无参数的构造函数,默认数组的容量capacity=10
        public MyArray () {
  //        this( capacity: 10);
              this(10);
        }

        // 获取数组中的元素实际个数
        public int getSize () {
              return size;
        }

        // 获取数组的总容量
        public int getCapacity () {
              return data.length;
        }

        // 返回数组是否为空
        public boolean isEmpty () {
              return size == 0;
        }

        // 从新给数组扩容
        private void resize (int newCapacity) {

              E[] newData = (E[])new Object[newCapacity];

              int index = size - 1;
              while (index > -1) {
                    newData[index] = get(index);
                    index --;
              }

              data = newData;
        }

        // 给数组添加一个新元素
        public void add (E e) {

              if (size == data.length) {
  //            throw new IllegalArgumentException("add error. Array is full.");
                    resize(2 * data.length);
              }

              data[size] = e;
              size++;
        }

        // 向全部元素后添加一个新元素 (与 add方法功能同样) push
        public void addLast (E e) {

              // 复用插入元素的方法
              insert(size, e);
        }

        // 在全部元素前添加一个新元素 unshift
        public void addFirst (E e) {

              insert(0, e);
        }

        // 在index索引的位置插入一个新元素e
        public void insert (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("insert error. require index < 0 or index > size");
              }

              if (size == data.length) {
  //            throw new IllegalArgumentException("add error. Array is full.");
                    resize(2 * data.length);
              }

              for (int i = size - 1; i >= index; i--) {
                    data[i + 1] = data[i];
              }

              data[index] = e;
              size++;
        }

        // 获取index索引位置的元素
        public E get (int index) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }
              return data[index];
        }

        // 获取数组中第一个元素(纯查看)
        public E getFirst () {
              return get(0);
        }

        // 获取数组中最后一个元素(纯查看)
        public E getLast () {
              return get(size - 1);
        }

        // 修改index索引位置的元素为e
        public void  set (int index, E e) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }
              data[index] = e;
        }

        // 查找数组中是否有元素e
        public boolean contain (E e) {

              for (int i = 0; i < size; i++) {
  //            if (data[i] == e) { // 值比较 用 ==
                    if (data[i].equals(e)) { // 引用比较 用 equals()

                                return true;
                    }
              }
              return false;
        }

        // 查找数组中元素e所在的索引,若是不存在元素e,则返回-1
        public int find (E e) {

              for (int i = 0; i < size; i++) {
                    if (data[i].equals(e)) {
                          return i;
                    }
              }
              return -1;
        }

        // 查找数组中全部元素e所在的索引,最后返回存放 全部索引值的 自定义数组
        public MyArray findAll (E e) {

              MyArray ma = new MyArray(20);

              for (int i = 0; i < size; i++) {
                    if (data[i].equals(e)) {
                          ma.add(i);
                    }
              }

              return  ma;

  //        int[] result = new int[ma.getSize()];
  //        for (int i = 0; i < ma.getSize(); i++) {
  //            result[i] = ma.get(i);
  //        }
  //
  //        return  result;
        }

        // 从数组中删除第一个元素, 返回删除的元素
        public E removeFirst () {
              return remove(0);
        }

        // 从数组中删除最后一个元素, 返回删除的元素
        public E removeLast () {
              return remove(size - 1);
        }

        // 从数组中删除第一个元素e
        public void removeElement (E e) {
              int index = find(e);
              if (index != -1) {
                    remove(index);
              }
  //        if (contain(e)) {
  //            int index = find(e);
  //            remove(index);
  //        }
        }

        // 从数组中删除全部元素e
        public void removeAllElement (E e) {

              int index = find(e);
              while (index != -1) {
                    remove(index);
                    index = find(e);
              }
  //        while (contain(e)) {
  //            removeElement(e);
  //        }
        }

        // 从数组中删除index位置的元素, 返回删除的元素
        public E remove (int index) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }

              E temp = data[index];

              for (int i = index; i < size - 1; i++) {
                    data[i] = data[i + 1];
              }

  //        for (int i = index + 1; i < size; i++) {
  //            data[i - 1] = data[i];
  //        }
              size --;
  //        data[size] = 0;
              data[size] = null;

              // 防止复杂度震荡 防止容量为4,size为1时,data.length / 2 为 0
              if(size == data.length / 4 && data.length / 2 != 0) {
                    resize(data.length / 2);
              }

              return temp;
        }

        @Override
        // @Override: 方法名 日期-开发人员
        public String toString () {

              StringBuilder sb = new StringBuilder();
              String arrInfo = "Array: size = %d,capacity = %d\n";
              sb.append(String.format(arrInfo, size, data.length));
              sb.append('[');
              for (int i = 0; i < size - 1; i ++) {
                    sb.append(data[i]);
                    sb.append(',');
              }
              sb.append(data[size - 1]);
              sb.append(']');

              return sb.toString();
        }
  }
6. `MyStack`
public class MyStack<E> implements IStack<E> {
        // 借用自定义个动态数组
        private MyArray<E> ma;

        public MyStack () {
            ma = new MyArray<E>();
        }

        public MyStack (int capacity) {
              ma = new MyArray<E>(capacity);
        }

        /**
         * @param e
         * @return 入栈
         */
        @Override
        public void push(E e) {
              ma.addLast(e);
        }

        /**
         * @return 出栈
         */
        @Override
        public E pop() {
              return ma.removeLast();
        }

        /**
         * @return 查看栈顶的元素
         */
        @Override
        public E peek() {
              return ma.getLast();
        }

        /**
         * @return 获取栈中实际元素的个数
         */
        @Override
        public int getSize() {
              return ma.getSize();
        }

        /**
         * @return 判断栈是否为空
         */
        @Override
        public boolean isEmpty() {
              return ma.isEmpty();
        }

        // 返回栈的容量
        public int getCapacity () {
              return ma.getCapacity();
        }

        @Override
        // @Override: 方法名 日期-开发人员
        public String toString () {
              int size = ma.getSize();
  //        int capacity = ma.getCapacity();

              StringBuilder sb = new StringBuilder();
  //        String arrInfo = "Stack: size = %d,capacity = %d\n";
  //        sb.append(String.format(arrInfo, size, capacity));
              sb.append("Stack: [");
              for (int i = 0; i < size - 1; i ++) {
                    sb.append(ma.get(i));
                    sb.append(',');
              }
              if (!ma.isEmpty()) {
                    sb.append(ma.getLast());
              }
              sb.append("] right is stack top !");

              return sb.toString();
        }
  }
7. `Main`
import java.util.Random;
  public class Main {

        private static double testStack (IStack<Integer> s, int openCount) {
              long startTime = System.nanoTime();

              Random random = new Random();
              for (int i = 1; i <= openCount ; i++) {
                    s.push(random.nextInt(Integer.MAX_VALUE));
              }
  //
              while (!s.isEmpty()) {
                    s.pop();
              }
              // ..

              long endTime = System.nanoTime();

              return  (endTime - startTime) / 1000_000_000.0;
        }

        public static void main(String[] args) {
              MyLinkedListStack<Integer> mkls = new MyLinkedListStack<Integer>();
              MyStack<Integer> ms = new MyStack<Integer>();

              double msTime = testStack(ms, 100000);
              double mklsTime = testStack(mkls, 100000);

              System.out.println("MyStack,time:" + msTime + "s.");
              System.out.println("MyLinkedListStack,time:" + mklsTime + "s.");

        }
  }
## 使用链表来实现队列

1. 对链表进行添加操做时
1. 时间复杂度为`O(n)`,
2. 只对链表头进行操做时为`O(1)`,
3. 对链表尾部进行操做时为`O(n)`
2. 对链表进行删除操做时
1. 时间复杂度为`O(n)`,
2. 只对链表头进行操做时为`O(1)`,
3. 对链表尾部进行操做时为`O(n)`
3. 对链表进行查询操做时
1. 时间复杂度为`O(n)`,
2. 只查链表头的元素时为`O(1)`,
3. 查链表尾部的元素时为`O(n)`
4. 队列中的操做
1. 在线性结构的一端插入元素,
2. 在另一端删除元素,
3. 因此必然会在线性结构的两端同时操做,
4. 此时就会有一端的操做的复杂度是`O(n)`级别,
5. 这个问题在用自定义数组实现时也遇到了,
6. 也正由于如此产生了循环队列,
7. 经过改进使用数组来实现队列的方式,
8. 因此链表也能够进行改进。
5. 改进链表
1. 不能使用以前的链表来进行队列的实现,
2. 设置一个 head 变量指向链表的头部第一个节点,
3. 设置一个 tail 变量指向链表的尾部第一个节点,
4. 有了 tail 这个变量,那么在链表的尾部添加一个元素很是容易,
5. 有了 head 和 tail 以后在两端添加节点是很是容易的,
6. 可是没法使用`O(1)`去删除尾部的节点,
7. 从 tail 这一端删除元素并不容易,
8. 可是若是将 head 做为队首,将 tail 做为队尾,
9. 队列操做是从队尾进队首出的,
10.   只须要从队尾 tail 插入元素,从队首 head 删除元素,
11.   这样一来就能够实现队列的功能。
6. 链表中再也不有插入的功能因此不须要 dummyHead
1. 可是因为没有 dummyHead,因此须要注意链表为空的状况。
7. 让自定义动态数组队列与自定义链表队列进行对比
1. 自定义动态数组队列的性能最差
2. 自定义动态数组循环队列与自定义链表队列的性能相近。
8. 与链表相关的有一个很是重要的内容
1. 就是递归,由于链表自己具备自然的递归性质,
2. 同时它又是一种很是简单的数据结构,
3. 因此链表是一种很是好的
4. 研究学习递归的逻辑机制的的数据结构。

### 代码示例

1. `(Interface: IMyQueue, class: MyArray, class: MyQueue,`
1. `class: MyLoopQueue, class: MyLinkedListQueue, class: Main)`
2. `IMyQueue`
public interface IMyQueue<E> {
        /**
         - @param e
         -  入队
         */
        void enqueue (E e);

        /**
         - @return e
         -  出队
         */
        E dequeue ();

        /**
         - @return e
         -  查看队首的元素
         */
        E getFront ();

        /**
         - @return number
         -  获取队列中的实际元素个数
         */
        int getSize ();

        /**
         - @return bool
         -   获取队列是否为空的bool值
         */
        boolean isEmpty ();
  }
3. `MyArray`
public class MyArray<E> {
        private E [] data;
        private int size;

        // 构造函数,传入数组的容量capacity构造Array
        public MyArray (int capacity) {
              data = (E[])new Object[capacity];
              size = 0;
        }

        // 无参数的构造函数,默认数组的容量capacity=10
        public MyArray () {
  //        this( capacity: 10);
              this(10);
        }

        // 获取数组中的元素实际个数
        public int getSize () {
              return size;
        }

        // 获取数组的总容量
        public int getCapacity () {
              return data.length;
        }

        // 返回数组是否为空
        public boolean isEmpty () {
              return size == 0;
        }

        // 从新给数组扩容
        private void resize (int newCapacity) {

              E[] newData = (E[])new Object[newCapacity];

              int index = size - 1;
              while (index > -1) {
                    newData[index] = get(index);
                    index --;
              }

              data = newData;
        }

        // 给数组添加一个新元素
        public void add (E e) {

              if (size == data.length) {
  //            throw new IllegalArgumentException("add error. Array is full.");
                    resize(2 * data.length);
              }

              data[size] = e;
              size++;
        }

        // 向全部元素后添加一个新元素 (与 add方法功能同样) push
        public void addLast (E e) {

              // 复用插入元素的方法
              insert(size, e);
        }

        // 在全部元素前添加一个新元素 unshift
        public void addFirst (E e) {

              insert(0, e);
        }

        // 在index索引的位置插入一个新元素e
        public void insert (int index, E e) {

              if (index < 0 || index > size) {
                    throw new IllegalArgumentException("insert error. require index < 0 or index > size");
              }

              if (size == data.length) {
  //            throw new IllegalArgumentException("add error. Array is full.");
                    resize(2 * data.length);
              }

              for (int i = size - 1; i >= index; i--) {
                    data[i + 1] = data[i];
              }

              data[index] = e;
              size++;
        }

        // 获取index索引位置的元素
        public E get (int index) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }
              return data[index];
        }

        // 获取数组中第一个元素(纯查看)
        public E getFirst () {
              return get(0);
        }

        // 获取数组中最后一个元素(纯查看)
        public E getLast () {
              return get(size - 1);
        }

        // 修改index索引位置的元素为e
        public void  set (int index, E e) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }
              data[index] = e;
        }

        // 查找数组中是否有元素e
        public boolean contain (E e) {

              for (int i = 0; i < size; i++) {
  //            if (data[i] == e) { // 值比较 用 ==
                    if (data[i].equals(e)) { // 引用比较 用 equals()

                                return true;
                    }
              }
              return false;
        }

        // 查找数组中元素e所在的索引,若是不存在元素e,则返回-1
        public int find (E e) {

              for (int i = 0; i < size; i++) {
                    if (data[i].equals(e)) {
                          return i;
                    }
              }
              return -1;
        }

        // 查找数组中全部元素e所在的索引,最后返回存放 全部索引值的 自定义数组
        public MyArray findAll (E e) {

              MyArray ma = new MyArray(20);

              for (int i = 0; i < size; i++) {
                    if (data[i].equals(e)) {
                          ma.add(i);
                    }
              }

              return  ma;

  //        int[] result = new int[ma.getSize()];
  //        for (int i = 0; i < ma.getSize(); i++) {
  //            result[i] = ma.get(i);
  //        }
  //
  //        return  result;
        }

        // 从数组中删除第一个元素, 返回删除的元素
        public E removeFirst () {
              return remove(0);
        }

        // 从数组中删除最后一个元素, 返回删除的元素
        public E removeLast () {
              return remove(size - 1);
        }

        // 从数组中删除第一个元素e
        public void removeElement (E e) {
              int index = find(e);
              if (index != -1) {
                    remove(index);
              }
  //        if (contain(e)) {
  //            int index = find(e);
  //            remove(index);
  //        }
        }

        // 从数组中删除全部元素e
        public void removeAllElement (E e) {

              int index = find(e);
              while (index != -1) {
                    remove(index);
                    index = find(e);
              }
  //        while (contain(e)) {
  //            removeElement(e);
  //        }
        }

        // 从数组中删除index位置的元素, 返回删除的元素
        public E remove (int index) {

              if (index < 0 || index >= size) {
                    throw new IllegalArgumentException("get error. index < 0 or index >= size ");
              }

              E temp = data[index];

              for (int i = index; i < size - 1; i++) {
                    data[i] = data[i + 1];
              }

  //        for (int i = index + 1; i < size; i++) {
  //            data[i - 1] = data[i];
  //        }
              size --;
  //        data[size] = 0;
              data[size] = null;

              // 防止复杂度震荡 防止容量为4,size为1时,data.length / 2 为 0
              if(size == data.length / 4 && data.length / 2 != 0) {
                    resize(data.length / 2);
              }

              return temp;
        }

        @Override
        // @Override: 方法名 日期-开发人员
        public String toString () {

              StringBuilder sb = new StringBuilder();
              String arrInfo = "Array: size = %d,capacity = %d\n";
              sb.append(String.format(arrInfo, size, data.length));
              sb.append('[');
              for (int i = 0; i < size - 1; i ++) {
                    sb.append(data[i]);
                    sb.append(',');
              }
              sb.append(data[size - 1]);
              sb.append(']');

              return sb.toString();
        }
  }
4. `MyQueue`
public class MyQueue<E> implements IMyQueue<E> {
        private MyArray<E> ma;

        public MyQueue () {
              ma = new MyArray<E>();
        }

        public MyQueue (int capacity) {
              ma = new MyArray<E>(capacity);
        }

        /**
         - @param e
         -  入队
         */
        @Override
        public void enqueue (E e) {
              ma.addLast(e);
        }

        /**
         - @return e
         -  出队
         */
        @Override
        public E dequeue () {
              return ma.removeFirst();
        }

        /**
         - @return e
         -  查看队首的元素
         */
        @Override
        public E getFront () {
              return ma.getFirst();
        }

        /**
         - @return number
         -  获取队列中的实际元素个数
         */
        @Override
        public int getSize () {
              return ma.getSize();
        }

        /**
         - @return bool
         -  获取队列是否为空的bool值
         */
        @Override
        public boolean isEmpty () {
              return ma.isEmpty();
        }

        // 获取队列容量
        public int getCapacity () {
              return ma.getCapacity();
        }

        @Override
        public String toString () {
              int size = ma.getSize ();
              StringBuilder sb = new StringBuilder();
              sb.append("Queue: head [");
              for (int i = 0; i < size - 1; i ++) {
                    sb.append(ma.get(i));
                    sb.append(',');
              }
              if(!isEmpty()) {
                    sb.append(ma.getLast());
              }
              sb.append("] foot. left is queue top!");

              return sb.toString();
        }
  }
5. `MyLoopQueue`
public class MyLoopQueue<E> implements IMyQueue<E> {
        private E[] data;
        private int front, tail;
        private int size;

        public MyLoopQueue (int capacity) {
              // 这个数组的容量为 传进来的指定容量+1,
              // 由于会有意识的浪费一个空间,只有+1后,
              // 才能装下用户指望传进来的全部数据
              data = (E[])new Object[capacity + 1];

              front = tail = size = 0;
        }

        public MyLoopQueue () {
              this(10);
        }

        public int getCapacity () {
              return data.length - 1;
        }

        private void resize (int newCapacity) {

              E[] newData = (E[]) new Object[newCapacity + 1];
              for (int i = 0; i < size; i++) {
  //            索引可能会越界,因而就要取余一下,
  //            若是越界了,就从队首开始
                    newData[i] = data[(front + i) % data.length];
              }
              data = newData;
              front = 0;
              tail = size;
        }

        /**
         * @param e 入队
         */
        @Override
        public void enqueue(E e) {

              if ((tail + 1) % data.length == front) {
                    resize(getCapacity() * 2);
              }

              data[tail] = e;
  //        tail在队列中循环
              tail = (tail + 1) % data.length;
              size ++;
        }

        /**
         * @return e
         * 出队
         */
        @Override
        public E dequeue() {

              if(isEmpty()) {
                    throw new IllegalArgumentException("can't dequeue from an empty queue.");
              }

              E e = data[front];
              data[front] = null;
              front = (front + 1) % data.length;
              size -- ;

              if (getCapacity() / 4 == size && getCapacity() / 2 != 0) {
                    resize(getCapacity() / 2);
              }

              return e;
        }

        /**
         * @return e
         * 查看队首的元素
         */
        @Override
        public E getFront() {
              if (isEmpty()) {
                    throw new IllegalArgumentException("queue is empty.");
              }
              return data[front];
        }

        /**
         * @return number
         * 获取队列中的实际元素个数
         */
        @Override
        public int getSize() {
              return size;
        }

        /**
         * @return bool
         * 获取队列是否为空的bool值
         */
        @Override
        public boolean isEmpty() {
              return front == tail;
        }

        @Override
        public String toString () {
              StringBuilder sb = new StringBuilder();
              sb.append(String.format("Queue: size = %d,capacity = %d \n", size, getCapacity()));
              sb.append("Queue: head [");
  //        第一种遍历方式
  //        for (int i = 0; i < size - 1; i ++) {
  //            sb.append(data[(front + i) % data.length]);
  //            sb.append(',');
  //        }
  //        if(!isEmpty()) {
  //            sb.append(data[(front + size - 1) % data.length]);
  //        }

              // 第二种遍历方式
              for (int i = front; i != tail ; i = (i + 1) % data.length) {
                    sb.append(data[i]);

                    if ((i + 1) % data.length != tail) {
                          sb.append(',');
                    }
              }

              sb.append("] foot. left is queue top!");
              sb.append("\n");

              return sb.toString();
        }

  }
6. `MyLinkedListQueue`
public class MyLinkedListQueue<E> implements IMyQueue<E> {

        private class Node {
              public E e;
              public Node next;

              public Node (E e, Node next) {
                    this.e = e;
                    this.next = next;
              }

              public Node (E e) {
                    this(e, null);
              }

              public Node () {
                    this(null, null);
              }

              @Override
              public String toString () {
                    return e.toString();
              }
        }

        private Node head, tail;
        private int size;

        public MyLinkedListQueue () {
              head = tail = null;
              size = 0;
        }

        /**
         * @param e 入队
         */
        @Override
        public void enqueue(E e) {
  //        // 第一种方式
  //        Node node = new Node(e);
  //        node.next = tail;
  //        tail = node;

  //        第二种方式
  //        head = new Node(e, head);

              // 链表尾部为空
              if (tail == null) {
                    tail = new Node(e);
                    head = tail;
              } else {
                    tail.next = new Node(e);
                    tail = tail.next;
              }
              size ++;

              // 不须要管头节点,由于头节点是不须要动的。

        }

        /**
         * @return e
         * 出队
         */
        @Override
        public E dequeue() {

              if (isEmpty()) {
                    throw new IllegalArgumentException("can not dequeue from an empty queue.");
              }

              Node node = head;
              head = head.next;
              node.next = null;
              if (head == null) {
                    tail = null;
              }
              size --;

              return node.e;
        }

        /**
         * @return e
         * 查看队首的元素
         */
        @Override
        public E getFront() {

              if (isEmpty()) {
                    throw new IllegalArgumentException("can not dequeue from an empty queue.");
              }

              return head.e;
        }

        /**
         * @return number
         * 获取队列中的实际元素个数
         */
        @Override
        public int getSize() {
              return size;
        }

        /**
         * @return bool
         * 获取队列是否为空的bool值
         */
        @Override
        public boolean isEmpty() {
              return size == 0;
        }

        @Override
        public String toString () {

              StringBuilder sb = new StringBuilder();
              sb.append("MyLinkedListQueue: 元素个数=" + size);
              sb.append(", queue front [ ");

              for (Node node = head; node != null; node = node.next) {
                    sb.append(node);
                    sb.append("->");
              }
              sb.append("NULL ] tail");

              return sb.toString();
        }

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

              for (int i = 1; i <= 5 ; i++) {
                    mkls.enqueue(i);
                    System.out.println(mkls);
              }

              System.out.println(mkls.getFront());

              for (int i = 0; i < 5 ; i++) {
                    System.out.println(mkls);
                    mkls.dequeue();
              }
        }
  }
7. `Main`
import java.util.Random;

  public class Main {

        private static double testQueue (IMyQueue<Integer> q, int openCount) {
              long startTime = System.nanoTime();

              Random random = new Random();
              for (int i = 1; i <= openCount ; i++) {
                    q.enqueue(random.nextInt(Integer.MAX_VALUE));
              }
  //
              while (!q.isEmpty()) {
                    q.dequeue();
              }
              // ..

              long endTime = System.nanoTime();

              return  (endTime - startTime) / 1000_000_000.0;
        }
        public static void main(String[] args) {

              IMyQueue<Integer> mq = new MyQueue<Integer>();
              IMyQueue<Integer> mlq = new MyLoopQueue<Integer>();
              IMyQueue<Integer> mklq = new MyLinkedListQueue<Integer>();

              double mqTime = testQueue(mq, 100000);
              double mlqTime = testQueue(mlq, 100000);
              double mklqTime = testQueue(mklq, 100000);

              System.out.println("MyQueue,time:" + mqTime + "s.");
              System.out.println("MyLoopQueue,time:" + mlqTime + "s.");
              System.out.println("MyLinkedListQueue,time:" + mklqTime + "s.");
        }
  }
相关文章
相关标签/搜索