数据结构实现——(泛型)动态数组、双链表

74 阅读5分钟

数据结构实现

数据结构——数组以及链表的原理这里就不再赘述。

动态数组


package com.nylonmin.dataStructure;
​
import java.util.Arrays;
​
/**
 * 自行动手实现动态数组
 * 需求:
 *  当数组元素个数达到底层静态数组的容量上限时,扩容为原来的 2 倍;
 *  当数组元素个数缩减到底层静态数组的容量的 1/4 时,缩容为原来的 1/2。
 *  实现增删改查功能
 * @author nylonmin
 */
public class MyArrayList<T> {
    //真正存储数据的底层数组
    private T[] data;
    //记录当前元素的个数 同样也是从1开始算
    private int size;
    //默认初始容量
    private static final int INIT_CAP=1;
​
    public MyArrayList(){
        this(INIT_CAP);
    }
    public MyArrayList(int initCap){
        this.data = (T[])new Object[initCap];
        this.size = 0;
    }
    //增加元素 默认增加到最后
    public void append(T num){
        //获取总容量
        int cap = this.data.length;
        if(size == cap){
            // 扩容 变为原来的2倍
            resize(2*cap);
        }
        data[size] = num;
        size++;
    }
    //增加元素  到指定的下标
    public void append(T num, int index){
        checkPositionIndex(index);
        int cap=this.data.length;
        //判断是否需要扩容
        if(size==cap){
            resize(cap*2);
        }
        //转移数据
        for(int i=size;i>index;i--){
            this.data[i]=this.data[i-1];
        }
        this.data[index]=num;
        size++;
    }
​
    //删除指定下标的元素 并返回被删除元素的值
    public T deleteAt(int index){
        //检查对应下标下是否有元素
        checkTrueIndex(index);
        int cap = this.data.length;
        if(size<cap/4){
            resize(cap/2);
        }
        T result = this.data[index];
        for(int i=index;i<size;i++){
            this.data[i]=this.data[i+1];
        }
        this.data[size-1]=null;
        size--;
        return result;
    }
    // 查询
    public T get(int index){
        checkTrueIndex(index);
        return this.data[index];
    }
​
    //修改
    public void set(T num,int index){
        checkTrueIndex(index);
        this.data[index]=num;
​
    }
    //获取当前长度
    public int size(){
        return this.data.length;
    }
    //判断是否为空
    public boolean isEmpty(){
        return this.size==0;
    }
    //扩容
    private void resize(int newCap){
        T[] temp =(T[]) new Object[newCap];
        for(int i=0;i<this.size;i++){
            temp[i] =data[i];
        }
        data = temp;
    }
    //判断是否是正常可触及到的index 一般用于删和差
    private boolean isLegalTrueIndex(int index){
        return index>=0 && index<size;
    }
    private void checkTrueIndex(int index){
        if(!isLegalTrueIndex(index)){
            throw new IndexOutOfBoundsException("该下标"+index+"上无元素");
        }
    }
​
​
​
    //判断下标是否合法  或者说判断下标是否是否能够支持插入数据
    private boolean isLegalAddIndex(int index){
        if(index >=0 && index <= size){
            return true;
        }
        return false;
    }
    //判断index位置是否可以添加元素
    private void checkPositionIndex (int index){
        if(!isLegalAddIndex(index)){
            throw new IndexOutOfBoundsException(index+"位置的下标无法添加元素");
        }
    }
​
    @Override
    public String toString() {
        return "MyArrayList{" +
                "data=" + Arrays.toString(data);
    }
}
​

以上代码写的时候有几个有意思的点,用于个人记录:

  • 这里面用到了泛型,那么比如说在构造函数中初始化的时候,一个是要通过new Object[] 来初始化,第二个是要强制类型转换. this.data = (T[])new Object[initCap];
  • 在判断下标是否合法/合理的时候,可以通过抛出异常来进行,然后在调用的时候,可以尝试通过try-catch来捕获异常。

双链表实现

实现双链表,不同于单链表,这里本质上有点借鉴了Java中LinkedList的源码实现方法

LinkedList的实现方法实际上就是;

  • 使用泛型

  • 将class Node作为静态内部类使用

    • 每个Node节点都有两个指针,分别指向前一个节点和后一个节点
  • LinkedList本身有两个节点,分别为head和tail ,均为Node

在个人实现中,大体实现了部分功能,用于自我学习。同时在具体实现过程中,head和tail均为虚拟的,只代表头尾节点,并不实际存储任何元素。

    
package com.nylonmin.LinkedListDemo;
​
/**
 *  自己动手完成双链表数据结构的编写
 *  1、定义虚拟头尾节点
 *  2、Node类作为静态内部类放在MyLinkedList类中
 */
@SuppressWarnings("all")
public class MyLinkedList<T> {
    //定义成员变量
    //虚拟头结点
    final private Node<T> head;
    //虚拟尾节点
    final private Node<T> tail;
    //当前双链表中实际元素数量
    private int size;
​
    /**
     * 将Node作为静态内部类放入
     * @param <T>
     */
    private static class Node<T>{
        private Node<T> next;
        private Node<T> prev;
        private T val;
        Node(T val){
            this.val=val;
        }
​
        @Override
        public String toString() {
            return "Node{" +
                    ", val=" + val +
                    '}';
        }
    }
​
    /**
     * 外部类无参构造器
     */
    public MyLinkedList(){
        //初始化头尾节点
        this.head=new Node<>(null);
        this.tail=new Node<>(null);
        head.next=tail;
        tail.prev=head;
        this.size=0;
    }
    /**
     * 增s
     */
    //插入到头部
    public boolean addAtFirst(T val){
        Node<T> inserNode = new Node<>(val);
        Node<T> node = head.next;
        inserNode.next=node;
        inserNode.prev=head;
        head.next=inserNode;
        node.prev=inserNode;
        size++;
        return true;
    }
    //插入到尾部
    public boolean addAtLast(T val){
        Node<T> inserNode = new Node<>(val);
        Node<T> node = tail.prev;
        inserNode.next=tail;
        inserNode.prev=node;
        node.next=inserNode;
        tail.prev=inserNode;
        size++;
        return true;
    }
    //增加到指定下标位置
    public boolean add(int index,T val){
        //如果index不合规 返回false
        if(!isPositionIndex(index)){
            return false;
        }
        if(index ==0 ){
            return addAtFirst(val);
        }
        if(index == size){
            return addAtLast(val);
        }
        Node<T> inserNode = new Node<>(val);
        Node<T> node = getNode(index);
        Node<T> prevNode = node.prev;
        inserNode.next=node;
        inserNode.prev=prevNode;
        prevNode.next=inserNode;
        node.prev=inserNode;
        this.size++;
        return true;
    }
    /**
     * 删除
     */
    public Node<T> remove(int index){
       if(!isElementIndex(index)){
           return null;
       }
       Node<T> node = getNode(index);
       Node<T> preNode = node.prev;
       Node<T> nextNode = node.next;
       node.prev=null;
       node.next=null;
       preNode.next=nextNode;
       nextNode.prev=preNode;
       return node;
​
    }
    /**
     * 改
     */
    public Node<T> set(int index,T val){
        if(!isElementIndex(index)){
            return null;
        }
        Node<T> node = getNode(index);
        node.val=val;
        return node;
    }
    /**
     * 查
     */
    //查询任意index位置节点
    public Node<T> get(int index){
        return getNode(index);
    }
    //查询第一个位置节点
    public Node<T> getFirst(){
        return get(0);
    }
    //查询最后位置节点
    public Node<T> getLast(){
        return get(this.size-1);
    }
    /**
     * 工具方法,判断下标是否合规
     * @param index
     * @return
     */
    private boolean isPositionIndex(int index){
        if(index<0||index>this.size){
            System.out.println(index+"位置无效");
            return false;
        }
        return true;
    }
​
    /**
     * 工具方法,判断下标是否是实实在在有元素的
     * @param index
     * @return
     */
    private boolean isElementIndex(int index){
        if(this.size==0||index<0||index>=this.size){
            System.out.println(index+"位置无元素");
            return false;
        }
        return true;
    }
​
    /**
     * 判断链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return this.size==0;
    }
​
    /**
     * 获取下标对应节点
     * @param index
     * @return 返回下标对应的节点
     */
    private Node<T> getNode(int index){
        //如果下标根本没有元素 那么返回null
        if(!isElementIndex(index)){
            return null;
        }
        //由于是双链表 判断从head取还是从tail取好
        //如果下标过了一半了 那么从尾部取
        if(index >= this.size>>1){
            return getNodeFromTail(index);
        }
        return getNodeFromHead(index);
​
    }
    /**
     * 为getNode服务,从head头结点取到对应下标的Node
     * @param index
     * @return
     */
    private Node<T> getNodeFromHead(int index){
        Node<T> curNode = head;
        int curIndex=-1;
        while(curIndex!=index){
            curNode=curNode.next;
            curIndex++;
        }
        return curNode;
    }
    /**
     * 为getNode服务,从tail尾结点取到对应下标的Node
     * @param index
     * @return
     */
    private Node<T> getNodeFromTail(int index){
        Node<T> curNode = tail;
        int curIndex=size;
        while(curIndex!=index){
            curNode=curNode.prev;
            curIndex--;
        }
        return curNode;
    }
    /**
     *
     */
    public void printMyList(){
        if(this.size==0){
            System.out.println("当前链表为空");
            return;
        }
        Node<T> curNode = head.next;
        while(curNode.next!=tail){
            System.out.print(curNode.val+"->");
            curNode=curNode.next;
        }
        System.out.println(curNode.val);
​
    }
}
​

测试:

package com.nylonmin.LinkedListDemo;
​
public class Test {
    public static void main(String[] args) {
        MyLinkedList<Integer> list = new MyLinkedList<>();
        list.addAtLast(1);
        list.addAtLast(2);
        list.addAtLast(3);
        list.addAtFirst(0);
        list.add(2, 100);
        list.printMyList();
        list.remove(0);
        list.printMyList();
        System.out.println(list.get(3));
        //  0->1->100->2->3
        //1->100->2->3
        //Node{, val=2}
    }
}
​