数据结构

132 阅读10分钟

玩转数据结构

1、为什么要学习数据结构?

  • 数据结构是所有计算机专业的同学必学的课程

  • 数据结构研究的是数据如何在计算机中进行组织和存储,使我们可以高效的获取数据或者修改数据。

1.1、数据结构分为3种结构

1.1.1、线性结构

数组;栈;队列;链表;哈希表

1.1.2、树结构

二叉树;二分搜索树;AVL;红黑树;Treep;Splay;堆;Trie;线段树;K-D树;并查集;哈夫曼树;

1.1.3、图结构

邻接矩阵;邻接表

2、不要小瞧数组

2.1、数组基础

  • 把数据码成一排进行存放

    scores 0 1 2 3 4 5 6 7 .... 索引

  • 数组最大的优点: 快速查询。 scores[2]

  • 数组最好应用于“索引有语意” 的情况。

  • 但并非所有有语意的索引都适用于数组

身份证号

  • 数组也可以处理“索引没有语意” 的情况。

2.2、制作自己的数组类

/**
 * 数组
 */
public class Array {

    private int [] data; // 元素中数据
    private int size;	// 元素个数

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

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

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

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

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

}

2.3、向数组中添加元素

  • 向数组末尾添加元素
/**
     * 向指定位置添加元素
     * @param index 索引
     * @param e 新元素
     */
public void add(int index,int e){
    // 数组个数和数组相等,抛出异常
    if(size == data.length)
        throw  new IllegalArgumentException("Add failed.Array is full");
    if(index < 0 || index > size){
        throw  new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
    }

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

}

2.4、数组中查询元素和修改元素

/**
     * 获取index 索引位置的元素
     * @param index
     * @return
     */
int get(int index){
    if(index < 0 || index > size)
        throw  new IllegalArgumentException("Get failed. Index is illegal.");
    return data[index];
}

/**
     * 设置索引的值
     * @param index
     * @param e
     * @return
     */
void set(int index,int e){
    if(index < 0 || index > size)
        throw  new IllegalArgumentException("Get failed. Index is illegal.");
    data[index] = e;
}

2.5、包含,搜索和删除

2.5.1、包含
 /**
     * 查找数据中是否有元素E
     * @param e
     * @return
     */
    public boolean contains(int e){
        for(int i = 0;i < size; i++ ){
            if(data[i] == e){
                return true;
            }
        }
        return false;
    }


2.5.2、搜索
/**
     * 查找数组中e所在的索引,如果不存在元素E,则返回-1
     * @param e
     * @return
     */
public int find(int e){
    for(int i = 0;i <size;i++ ){
        if(data[i] == e){
            return i;
        }
    }
    return -1;
}
2.5.3、删除
 /**
     * 删除索引中的元素,返回删除的元素
     * @param index
     * @return
     */
    public int remove(int index){
        if (index < 0 || index > size ){
            throw new IllegalArgumentException("remove failed,Index is illegal");
        }
        int ret = data[index];

        for(int i = index +1; i < size; i++ ){
             data[i-1] = data[i];       //数组中 之后元素的值向前挪动
        }
        size--;
        return ret;
    }

    /**
     * 删除第一个元素
     * @return
     */
    public int removeFirst(){
        return remove(0);
    }

    /**
     * 删除末尾元素
     * @return
     */
    public int removeLast(){
        return remove(size-1);
    }

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

数组工具 完整例子:

package com.zf;

/**
 * 数组
 */
public class Array {

    private int [] data;
    private int size;

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

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

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

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

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


    /**
     * 数组末尾 添加一个新元素
     * @param e
     */
    public void addLast(int e){
//        // 数组个数和数组相等,抛出异常
//        if(size == data.length)
//            throw  new IllegalArgumentException("Addlast failed.Array is full");
//        data[size] = e;
//        size++;     // data[size++] = e;
        add(size,e);
    }

    /**
     * 数组开头 添加一个新的元素
     * @param e
     */
    public void addFirst(int e){
        add(0,e);
    }


    /**
     * 向指定位置添加元素
     * @param index 索引
     * @param e 新元素
     */
    public void add(int index,int e){
        // 数组个数和数组相等,抛出异常
        if(size == data.length)
            throw  new IllegalArgumentException("Add failed.Array is full");
        if(index < 0 || index > size){
            throw  new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
        }

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

    }

    /**
     * 获取index 索引位置的元素
     * @param index
     * @return
     */
    public int get(int index){
        if(index < 0 || index > size)
            throw  new IllegalArgumentException("Get failed. Index is illegal.");
        return data[index];
    }

    /**
     * 设置索引的值
     * @param index
     * @param e
     * @return
     */
    public void set(int index,int e){
        if(index < 0 || index > size)
            throw  new IllegalArgumentException("Get failed. Index is illegal.");
        data[index] = e;
    }

    /**
     * 查找数据中是否有元素E
     * @param e
     * @return
     */
    public boolean contains(int e){
        for(int i = 0;i < size; i++ ){
            if(data[i] == e){
                return true;
            }
        }
        return false;
    }


    /**
     * 查找数组中e所在的索引,如果不存在元素E,则返回-1
     * @param e
     * @return
     */
    public int find(int e){
        for(int i = 0;i <size;i++ ){
            if(data[i] == e){
                return i;
            }
        }
        return -1;
    }


    /**
     * 删除索引中的元素,返回删除的元素
     * @param index
     * @return
     */
    public int remove(int index){
        if (index < 0 || index > size ){
            throw new IllegalArgumentException("remove failed,Index is illegal");
        }
        int ret = data[index];

        for(int i = index +1; i < size; i++ ){
             data[i-1] = data[i];       //数组中 之后元素的值向前挪动
        }
        size--;
        return ret;
    }

    /**
     * 删除第一个元素
     * @return
     */
    public int removeFirst(){
        return remove(0);
    }

    /**
     * 删除末尾元素
     * @return
     */
    public int removeLast(){
        return remove(size-1);
    }

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
        sb.append("[");
        for(int i = 0;i < size; i++ ) {
            sb.append(data[i]);
            if(i != size - 1){
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }


}

2.6、使用泛型

  • 让我们的数据结构可以放置“任何”数据类型
  • 不可以是基本数据烈行,只能是类对象

boolean,byte,char,short,int,long,float,double

  • 每个基本数据类型都有对应的包装类

Boolean,Byte,Char,Short,Int,Long,Float,Double

package com.zf;

/**
* 数组
*/
public class ArrayListDemo<E> {

   private E[] data;
   private int size;

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

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

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

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

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


   /**
    * 数组末尾 添加一个新元素
    * @param e
    */
   public void addLast(E e){
//        // 数组个数和数组相等,抛出异常
//        if(size == data.length)
//            throw  new IllegalArgumentException("Addlast failed.Array is full");
//        data[size] = e;
//        size++;     // data[size++] = e;
       add(size,e);
   }

   /**
    * 数组开头 添加一个新的元素
    * @param e
    */
   public void addFirst(E e){
       add(0,e);
   }


   /**
    * 向指定位置添加元素
    * @param index 索引
    * @param e 新元素
    */
   public void add(int index,E e){
       // 数组个数和数组相等,抛出异常
       if(size == data.length)
           throw  new IllegalArgumentException("Add failed.Array is full");
       if(index < 0 || index > size){
           throw  new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
       }

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

   }

   /**
    * 获取index 索引位置的元素
    * @param index
    * @return
    */
   public E get(int index){
       if(index < 0 || index > size)
           throw  new IllegalArgumentException("Get failed. Index is illegal.");
       return data[index];
   }

   /**
    * 设置索引的值
    * @param index
    * @param e
    * @return
    */
   public void set(int index,E e){
       if(index < 0 || index > size)
           throw  new IllegalArgumentException("Get failed. Index is illegal.");
       data[index] = e;
   }

   /**
    * 查找数据中是否有元素E
    * @param e
    * @return
    */
   public boolean contains(E e){
       for(int i = 0;i < size; i++ ){
           if(data[i].equals(e)){
               return true;
           }
       }
       return false;
   }


   /**
    * 查找数组中e所在的索引,如果不存在元素E,则返回-1
    * @param e
    * @return
    */
   public int find(E e){
       for(int i = 0;i <size;i++ ){
           if(data[i].equals(e)){
               return i;
           }
       }
       return -1;
   }


   /**
    * 删除索引中的元素,返回删除的元素
    * @param index
    * @return
    */
   public E remove(int index){
       if (index < 0 || index > size ){
           throw new IllegalArgumentException("remove failed,Index is illegal");
       }
       E ret = data[index];

       for(int i = index +1; i < size; i++ ){
            data[i-1] = data[i];       //数组中 之后元素的值向前挪动
       }
       size--;
//        data[size] = null;
       return ret;
   }

   /**
    * 删除第一个元素
    * @return
    */
   public E removeFirst(){
       return remove(0);
   }

   /**
    * 删除末尾元素
    * @return
    */
   public E removeLast(){
       return remove(size-1);
   }


   @Override
   public String toString(){
       StringBuilder sb = new StringBuilder();
       sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
       sb.append("[");
       for(int i = 0;i < size; i++ ) {
           sb.append(data[i]);
           if(i != size - 1){
               sb.append(", ");
           }
       }
       sb.append("]");
       return sb.toString();
   }


}

2.7、动态数组

  • 扩容空间
 /**
     * 扩容空间 
     * @param newCapacity
     */
    private void resize(int newCapacity){
        E[] newData = (E[])new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;

    }

   public void add(int index,E e){
        // 数组个数和数组相等,抛出异常

        if(index < 0 || index > size){
            throw  new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
        }

        if(size == data.length)
            resize(2 * data.length);	//扩容数组 

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




  /**
     * 删除索引中的元素,返回删除的元素
     * @param index
     * @return
     */
    public E remove(int index){
        if (index < 0 || index > size ){
            throw new IllegalArgumentException("remove failed,Index is illegal");
        }
        E ret = data[index];

        for(int i = index +1; i < size; i++ ){
             data[i-1] = data[i];       //数组中 之后元素的值向前挪动
        }
        size--;
//        data[size] = null;

        //如果当前数组容量 等于数组一半
        if(size == data.length / 4 && data.length / 2 != 0 )
            resize(data.length / 2); // 缩容数组 ,容量缩小 

        return ret;
    }

ArratListDemo

package com.zf;

/**
 * 数组
 */
public class ArrayListDemo<E> {

    private E[] data;
    private int size;

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

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

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

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

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


    /**
     * 数组末尾 添加一个新元素
     * @param e
     */
    public void addLast(E e){
//        // 数组个数和数组相等,抛出异常
//        if(size == data.length)
//            throw  new IllegalArgumentException("Addlast failed.Array is full");
//        data[size] = e;
//        size++;     // data[size++] = e;
        add(size,e);
    }

    /**
     * 数组开头 添加一个新的元素
     * @param e
     */
    public void addFirst(E e){
        add(0,e);
    }


    /**
     * 向指定位置添加元素
     * @param index 索引
     * @param e 新元素
     */
    public void add(int index,E e){
        // 数组个数和数组相等,抛出异常

        if(index < 0 || index > size){
            throw  new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
        }

        if(size == data.length)
            resize(2 * data.length);

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

    }

    /**
     * 获取index 索引位置的元素
     * @param index
     * @return
     */
    public E get(int index){
        if(index < 0 || index > size)
            throw  new IllegalArgumentException("Get failed. Index is illegal.");
        return data[index];
    }

    /**
     * 取出第一个元素
     * @return
     */
    public E getFirst(){
        return get(0);
    }

    /**
     * 取出最后一个元素
     * @return
     */
    public E getLast(){
        return get(size-1);
    }

    /**
     * 设置索引的值
     * @param index
     * @param e
     * @return
     */
    public void set(int index,E e){
        if(index < 0 || index > size)
            throw  new IllegalArgumentException("Get failed. Index is illegal.");
        data[index] = e;
    }

    /**
     * 查找数据中是否有元素E
     * @param e
     * @return
     */
    public boolean contains(E e){
        for(int i = 0;i < size; i++ ){
            if(data[i].equals(e)){
                return true;
            }
        }
        return false;
    }


    /**
     * 查找数组中e所在的索引,如果不存在元素E,则返回-1
     * @param e
     * @return
     */
    public int find(E e){
        for(int i = 0;i <size;i++ ){
            if(data[i].equals(e)){
                return i;
            }
        }
        return -1;
    }


    /**
     * 删除索引中的元素,返回删除的元素
     * @param index
     * @return
     */
    public E remove(int index){
        if (index < 0 || index > size ){
            throw new IllegalArgumentException("remove failed,Index is illegal");
        }
        E ret = data[index];

        for(int i = index +1; i < size; i++ ){
             data[i-1] = data[i];       //数组中 之后元素的值向前挪动
        }
        size--;
//        data[size] = null;

        //如果当前数组容量 等于数组一半
        if(size == data.length / 2)
            resize(data.length / 2); // 缩容数组 ,容量缩小

        return ret;
    }

    /**
     * 删除第一个元素
     * @return
     */
    public E removeFirst(){
        return remove(0);
    }

    /**
     * 删除末尾元素
     * @return
     */
    public E removeLast(){
        return remove(size-1);
    }


    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
        sb.append("[");
        for(int i = 0;i < size; i++ ) {
            sb.append(data[i]);
            if(i != size - 1){
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 扩容空间
     * @param newCapacity
     */
    private void resize(int newCapacity){
        E[] newData = (E[])new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;

    }

}

3、栈 Stack

3.1、栈和栈的应用:撤销操作和操作系统
  • 栈也是一种线性结构
  • 相比数组,栈对应的操作是数组的子集
  • 只能从一端添加元素,也只能从一端取出元素
  • 这一端称为栈顶
4 栈顶
3
2
1
  • 栈是一种后进先出的数据结构 (先进去1,2,3,4 ) 取的时候 先取(4,3,2,1)

  • 在计算机的世界中,栈拥有者不可思议的作用

  • 撤销操作 (栈理解成撤销)

  • 程序调用的系统栈

3.2、栈的基本实现
  • 从用户的角度看,支持这些操作就好
  • 具体底层实现,用户不关心
  • 实际底层有多种实现方式

接口

Interface Stack<E>  {
	
    void push(E);	//添加元素,入栈

    E pop();	// 出栈,拿出栈顶元素

    E peek();	// 看栈顶的元素是谁  

    int getSize();	// 栈一共多少个元素

    boolean isEmpty();	// 栈是否为空  

}

实现类:

class ArrayStack implement Stack<E>
    
    
    
    
3.3、栈的另一个应用:括号和匹配
  • undo 操作 - 编译器
  • 系统调用栈 - 操作系统
  • 括号匹配 - 编译器
3.4、关于Leetcode的更多说明

给定一个只包括 () {} [] 的字符串,判断字符串是否有效,括号必须以正确的顺序关闭, () {} [] 是有效的,(】 {] 不是有效的

解决思路:

Solution.java

public class Solution {
      /**
     * 判断输入的值是否匹配
     * @param  chatStr ({[]})
     * @return
     */
    public boolean isValid(String chatStr){

        Stack<String> stack = new Stack<>();
        for (int i = 0; i < chatStr.length(); i++ ){
            String s = String.valueOf(chatStr.charAt(i));
            if(s.equals("(") || s.equals("{") || s.equals("[")){ 
                stack.push(s);
            }else{
                if(stack.isEmpty()){
                    return false;
                }

                // 得到入栈中的 栈顶元素   ( { [
                String topStr = stack.pop();

                // 判断栈顶的元素 跟输入的内容是否匹配,如 () {} []
                if(s.equals(")") && !topStr.equals("(")){
                    return false;
                }

                if(s.equals("}") && !topStr.equals("{")){
                    return false;
                }

                if(s.equals("]") && !topStr.equals("[")){
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

}
3.5、 数组队列 Queue
  • 队列也是一种线性结构

  • 相比数组,队列对应的操作是数组的子集

  • 只能从一端(队尾)添加元素,只能从另一端(对首)取出元素

  • 队列是一种先进先出的数据结构(先到先得)

队列的实现

interface Queue<E>
    void enqueue(E);	// 入队

	E dequeue();		// 出队

	E getFront();		// 队首

	int getSize();		// 

	boolean isEmpty();

ArrayQueue

package com.zf.com.zf.queue;

import com.zf.ArrayListDemo;

public class ArrayQueue<E> implements Queue<E> {

    private ArrayListDemo<E> array;

    public ArrayQueue(int capacity) {
        array = new ArrayListDemo<>(capacity);
    }

    public ArrayQueue() {
        array = new ArrayListDemo<>();
    }

    @Override
    public void enqueue(E e) {
        array.addLast(e);
    }

    @Override
    public E dequeue() {
        return array.removeFirst();
    }

    @Override
    public E getFront() {
        return array.getFirst();
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }

    public int getCapacity(){
        return array.getCapacity();
    }

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

       sb.append("Queue: ");
       sb.append("front [");
       for(int i = 0 ; i < array.getSize();i ++ ){
           sb.append(array.get(i));
           if( i != array.getSize() - 1 ){
               sb.append(", ");
           }
       }
       sb.append("] tail ");
       return sb.toString();
    }
}

3.6、循环队列

front == tail 队列为空

tail+1 == front 队列满 (tail+1) % c == front 队列满

数组队列的问题

跟钟表一样

LoopQueue

public class LoopQueue<E> implements  Queue<E> {

    private E[] data;
    private int front,tail;
    private int size;

    /**
     * 初始构造函数
     * @param capacity
     */
    public LoopQueue(int capacity) {
        data  = (E[])new Object[capacity+1];
        front = 0;
        tail = 0;
        size = 0;
    }

    public LoopQueue() {
       this(10);
    }

    /**
     * 数组长度 -1
     * @return
     */
    public int getCapacity(){
        return data.length - 1;
    }

    @Override
    public void enqueue(E e) {

    }

    @Override
    public E dequeue() {
        return null;
    }

    @Override
    public E getFront() {
        return null;
    }

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

    @Override
    public boolean isEmpty() {
        return front == tail;
    }
}


3.7、循环队列的实现

LoopQueue.java

package com.zf.com.zf.queue;

import java.util.Random;

/**
 * 循环队列
 * @param <E>
 */
public class LoopQueue<E> implements  Queue<E> {

    private E[] data;   //队列数据
    private int front,tail;     // 队首  队尾下一个位置
    private int size;   //个数

    /**
     * 初始构造函数
     * @param capacity
     */
    public LoopQueue(int capacity) {
        data  = (E[])new Object[capacity+1];    //循环队列 浪费一个单位 容积+1
        front = 0;
        tail = 0;
        size = 0;
    }

    public LoopQueue() {
       this(10);
    }

    /**
     * 循环队列 浪费一个单位 容积+1
     * 数组长度 -1
     * @return
     */
    public int getCapacity(){
        return data.length - 1;
    }

    @Override
    public void enqueue(E e) {
        //判断队列是否满了
        if( (tail + 1) % data.length == front) {
            resize(getCapacity() * 2);
        }
        data[tail] = e;
        tail = (tail+1)%data.length;
        size++;


    }

    /**
     * 扩容数组
     * @param newCapacity
     */
    private void resize(int newCapacity){
        //新的数据
        E[] newData = (E[])new Object[newCapacity + 1];

        for(int i = 0 ;i < size; i++ ){
            //将原来data中的数据放到新的data中
            newData[i] = data[(i+front) % data.length];

        }
        data = newData;
        front = 0;
        tail = size;
    }


    @Override
    public E dequeue() {
        if(isEmpty()){
            throw new IllegalArgumentException("Cannot dequeue from an empty queue");
        }
        E ret = data[front];
        data[front] = null;
        front = (front + 1) %data.length;
        size--;
        if(size== getCapacity() / 4 && getCapacity() / 2 != 0){
            resize(getCapacity()/2);
        }
        return ret;
    }

    @Override
    public E getFront() {
        if(isEmpty()){
            throw new IllegalArgumentException("Cannot dequeue from an empty queue");
        }
        return data[front];
    }

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

    @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("front [");
       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("] tail");
       return sb.toString();
    }


    // 测试使用q 运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
    private static double testQueue(Queue<Integer> q,int opCount){
        long startTime = System.nanoTime();
        Random r = new Random();

        for (int i =0;i < opCount ; i++ ){
            q.enqueue(r.nextInt(Integer.MAX_VALUE));
        }

        for (int i = 0; i < opCount ; i ++ ){
            q.dequeue();
        }

        long endTime = System.nanoTime();
        return (endTime - startTime) / 1000000000.0;
    }

    public static void main(String[] args) {
        int opCount =  100000;
        LoopQueue<Integer> loopQueue = new LoopQueue<>();


        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();

        System.out.println("ArrayQueue  "+testQueue(arrayQueue,opCount) +"S");
        testQueue(loopQueue,opCount);
        System.out.println("testQueue  "+testQueue(loopQueue,opCount) +"S");
    }
}

4、什么是链表?

4.1、什么是链表?

线性数据结构

底层依托静态数组

靠resize解决固定容量问题

  • 动态数组
  • 队列
为什么链表很重要
  • 链表 真正的动态数据结构
  • 最简单的动态数据结构
  • 更深入的理解引用(或者指针)
  • 更深入的理解递归
链表LinkedList
  • 数据存储在“节点”(Node)中
// 链表跟火车一样  一阶一阶的 next 是null 是最后一个节点 
class Node {
    E e;	// 存储真正的数据
    Node next;	// 指向当前节点的 下一个节点
}
// 1 → 2 → 3 → null
  • 优点:真正的动态,不需要处理固定容量的问题
  • 缺点:丧失了随机访问的能力
数据和链表的对比
  • 数组最好用于索引有语意的情况。socres[2]

  • 最大的优点: 支持快速查询

  • 链表不适用于索引有语意的情况

  • 最大优点:动态

LinkedList.java

package com.zf.linked;

/**
 * 链表
 * @param <E>
 */
public class LinkedList<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();
        }
    }
}

4.2、在链表中添加元素

在链表头添加元素
node.next = head
head = node;
size++;
在链表中间添加元素

在索引为2的地方添加元素666

  • 关键:找到要添加的节点的前一个节点 顺序很重要
node.next = prev.next;
prev.next = node;

LinkedList.java

package com.zf.linked;

/**
 * 链表
 * @param <E>
 */
public class LinkedList<E> {

    private Node head;  // 头部

    private int size;

    public LinkedList(){
        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);
////         node.next  = head; //元素头
////         head = node;   //head 更新一下

        head = new Node(e,head);
        size++;
    }


    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void add(int index,E e){
        if(index < 0 || index > size ){
            throw new IllegalArgumentException("Add failed. Illegal index.");
        }

        if(index == 0){
            addFirst(e);
        }else{
            Node prev = head;   //下一个节点
            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++;
        }
    }


    /**
     * 向元素末尾添加一个新的元素e
     * @param e
     */
    public void addLast(E e){
        add(size,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();
        }
    }
}

4.3、使用链表的虚拟头结点

为链表设立虚拟头节点
null01234null
 ↑
dummyHead 
    
    

    
package com.zf.linked;

/**
 * 链表
 * @param <E>
 */
public class LinkedList<E> {

    private Node dymmyHead;  // 虚拟头节点

    private int size;

    public LinkedList(){
        dymmyHead = 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);
////         node.next  = head; //元素头
////         head = node;   //head 更新一下

        add(0,e);
        size++;
    }


    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void add(int index,E e){
        if(index < 0 || index > size ){
            throw new IllegalArgumentException("Add failed. Illegal index.");
        }
            Node prev = dymmyHead;   //下一个节点
            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++;

    }


    /**
     * 向元素末尾添加一个新的元素e
     * @param e
     */
    public void addLast(E e){
        add(size,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();
        }
    }
}
    

4.4、链表的遍历,查询和修改

LinkedList.java

package com.zf.linked;

/**
 * 链表
 * @param <E>
 */
public class LinkedList<E> {

    private Node dymmyHead;  // 头部

    private int size;

    public LinkedList(){
        dymmyHead = 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);
////         node.next  = head; //元素头
////         head = node;   //head 更新一下

        add(0,e);
        size++;
    }


    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void add(int index,E e){
        if(index < 0 || index > size ){
            throw new IllegalArgumentException("Add failed. Illegal index.");
        }
            Node prev = dymmyHead;   //前一个
            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++;

    }


    /**
     * 向元素末尾添加一个新的元素e
     * @param e
     */
    public void addLast(E e){
        add(size,e);
    }

    /**
     * 获得链表的第index(0-based)个位置元素
     * 在链表中不是一个常用的操作,练习用
     * @param index
     * @return
     */
    public E get(int index){
        if(index < 0 || index >= size ){
            throw new IllegalArgumentException("Add failed. Illegal index.");
        }
        Node cur = dymmyHead.next;  // 从索引为0 的元素遍历
        for(int i = 0;i< index;i ++ ){
            cur = cur.next;
        }
        return cur.e;
    }

    //获得链表第一个元素
    public E getFirst(){
      return get(0);
    }

    // 获得链表最后一个元素
    public E getLast(){
        return get(size-1);
    }

    // 修改链表的第index(0-based)个位置的元素为e
    // 练习用
    public void set(int index,E e){
        if(index < 0 || index >= size ){
            throw new IllegalArgumentException("update failed. Illegal index.");
        }

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

    //查找链表中是否有元素e
    public boolean contains(E e){
        Node cur = dymmyHead.next;
        while(cur != null ){
            if (cur.e.equals(e)){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

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

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

      Node cur = dymmyHead.next;
      while(cur != null ){
          sb.append(cur  + "->");
          cur = cur.next;
      }

      // for 循环
//        for(Node cur = dymmyHead.next; cur != null; cur = cur.next){
//            sb.append(cur + "->");
//        }
//        sb.append("NULL");

      sb.append("NULL");
      return sb.toString();
    }

    public static void main(String[] args) {

        LinkedList<Integer> linkedList = new LinkedList<>();

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


        linkedList.add(2,666);
        System.out.println(linkedList);

    }
}

4.5、从链表中删除元素

链表元素的删除
  • 删除索引为2位置的元素

prev.next = delNode.next
delNode.next = null    
    
    
 /**
     * 从链表中删除元素
     * @param index
     * @return
     */
    public E remove(int index){
        if(index < 0 || index >= size ){
            throw new IllegalArgumentException("Remove failed. Illegal index.");
        }

        Node prev = dymmyHead;

        for(int i = 0;i < index; i ++ ){
            prev = prev.next;
        }

        Node retNode = prev.next;   // 实例 删除节点
        prev.next = retNode.next;   // 下个节点 = 删除节点
        retNode.next = null;    // 删除的节点设置为null
        size--;
        return retNode.e;
    }    

4.6、使用链表实现栈

interface Stack<E> 

5、链表和递归

5.1、递归基础与递归的宏观语意

递归
  • 本质上,将原来的问题,转化为更小的同一问题

  • 举例: 数组求和

    Sum( arr[0...n-1]) = arr[0] + Sum(arr[1....n-1]) ← 更小的同一问题

    Sum( arr[1...n-1 ] ) = arr[1] + Sum(arr[2...n-1]) ← 更小的问题

    Sum ( arr[n-1...n-1]) = arr[n-1] + Sum([]) ← 最基本的问题

  • 注意递归函数的宏观语意

  • 递归函数就是一个函数,完成一个功能

// 计算 arr[l...n] 范围里的数字和

递归求和
package com.zf.dg;

public class Sum {

    public static void main(String[] args) {
        int arrs [] = {1,2,3,4,5};
        System.out.println(sum(arrs));
    }

    public  static int sum (int arr []){
        return sum(arr,0);
    }

    /**
     * 递归求和
     * @param arr
     * @param l
     * @return
     */
    private static int sum(int [] arr,int l){
        if( l == arr.length){   // 如果索引 等于数组的长度  则返回0
            return 0;       //求解最基本问题
        }
        return arr[l] + sum(arr,l+1);   // 把原问题转化成更小的问题
    }
}

5.2、链表的天然递归结构性质

链表和递归

5.3、递归运行的机制,递归的微观解读

5.4、递归算法的调试