3-顺序表

本文详细介绍动态数组的实现原理,包括头部、尾部和任意位置的元素插入与删除操作,以及数组扩容等关键方法。探讨了动态数组相较于静态数组的优势与不足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.定义

2.分类

2.1.静态顺序表

2.2.动态顺序表(动态数组DynamicArray)

3.顺序表的实现

3.1.在数组头部添加元素

3.2.在数组尾部添加元素

3.3.在数组任意index处添加元素

3.4.取得当前数组长度

3.5.取得当前数组index位置处的元素值

3.6.将数组指定位置index处的元素值修改为新的值newData

3.7.判断当前数组中是否包含指定元素值data

3.8.删除数组index索引处的元素值

3.9.删除数组中所有值等于value的元素

3.10.内部扩容方法

3.11.校验当前传入的index是否合法(用在查询,设置,删除)

3.12.将当前动态数组转换为字符串(序列化)

4.此种数据结构的特点

4.1.优点

4.2.缺点

PS:关于IDEA的调试


1.定义

基于动态数组实现的线性表。

用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般用数组存储。在数组上完成数据的增,删,查,改。

2.分类

2.1.静态顺序表

用定长数组存储。适用于确定知道需要存多少数据的场景。

原生int[]数组的问题:一旦定义一数组,长度固定,无法修改。int[] arr = new int[10];

a.数组长度开辟过小,能存储的数据太少;

b.数组长度开辟过大,会浪费大量空间。

2.2.动态顺序表(动态数组DynamicArray)

根据当前存储的数据元素,动态地进行数组的扩容(需要自己实现)。

实际上元素还是存储在int[]中,将原生int[]做了一层包装,使其具有动态扩容的功能。

3.顺序表的实现

所有数据结构无外乎CRUD四大方法。

import java.util.Arrays;

public class DynamicArray {
    //定义属性
    //elementData就是具体存储元素的数组
    private int[] elementData; //是一个数组引用,未赋值,默认值为null
    //size当前动态数组中实际存储的有效元素个数
    private int size;
    //elementData.length数组长度(含无效元素)

    //无参构造方法
    public DynamicArray() {
        this.elementData = new int[10]; //不知道长度,默认存10个
    }

    //有参构造方法
    public DynamicArray(int size) {
        this.elementData = new int[size]; //规定好长度
    }

    /**
     * 3.1.在数组头部添加元素
     * @param data
     */
    public void addFirst(int data) {
        //此时内部小数组已满,需要扩容
        if(size == elementData.length) {
            grow();
        }
        for (int i = size - 1; i >= 0; i--) { //size-1是最后一个元素索引
            elementData[i + 1] = elementData[i];
        }
        //此时index=0,数组头部空间空出来
        elementData[0] = data;
        size++;
    }

    /**
     * 3.2.在数组尾部添加元素
     * @param data
     */
    public void addLast(int data) {
        if(size == elementData.length) {
            grow();
        }
        //数组最后一个元素下标索引为size-1,在其后即size处添加元素
        elementData[size] = data;
        size++;
    }

    /**
     * 3.3.在数组任意index处添加元素
     * @param index
     * @param data
     */
    public void addIndex(int index, int data) {
        //先判断边界条件,index是否合法
        if(index < 0 || index > size) {
            System.err.println("index非法,无法进行插入!");
            return;
        }
        //判断当前数组是否已满
        if(size == elementData.length) {
            grow();
        }
        if(index == 0) {
            addFirst(data);
        } else if(index == size) {
            addLast(data);
        } else {
            for (int i = size - 1; i >= index; i--) {
                elementData[i + 1] = elementData[i];
            }
            //此时index位置空出
            elementData[index] = data;
            size++;
        }
    }

    public void addIndex2(int index, int data) {
        if(index < 0 || index > size) {
            System.err.println("index非法,无法进行插入!");
            return;
        }
        if(size == elementData.length) {
            grow();
        }
        //数据插入
        for (int i = size - 1; i >= index; i--) {
            elementData[i + 1] = elementData[i];
        }
        //此时index位置空出
        elementData[index] = data;
        size++;
    }

    public void addFirst2(int data) {
        addIndex(0, data);
    }

    public void addLast2(int data) {
        addIndex(size, data);
    }

    /**
     * 3.4.取得当前数组长度
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 3.5.取得当前数组index位置处的元素值
     * @param index
     * @return
     */
    public int get(int index) {
        if(rangeCheck(index)) {
            return elementData[index];
        }
        return -1;
    }

    /**
     * 3.6.将数组指定位置index处的元素值修改为新的值newData
     * @param index
     * @param newData
     * @return 修改前的元素值
     */
    public int set(int index, int newData) {
        if(rangeCheck(index)) {
            int oldValue = elementData[index];
            elementData[index] = newData;
            return oldValue;
        }
        return -1;
    }

    /**
     * 3.7.判断当前数组中是否包含指定元素值data
     * @param data
     * @return
     */
    public boolean contains(int data) {
        for (int i = 0; i < size; i++) {
            if(elementData[i] == data) {
                return true;
            }
        }
        return false;
    }

    /**
     * 3.8.删除数组index索引处的元素值
     * @param index
     */
    public void removeIndex(int index) {
        if(rangeCheck(index)) {
            //从index位置开始,将之后的元素统一前移一位
            for (int i = index; i < size - 1; i++) {
                elementData[i] = elementData[i + 1];
            }
            size--;
            //确保数组中最后元素也还原为默认值
            elementData[size] = 0;
        }
    }

    /**
     * 3.9.删除数组中所有值等于value的元素
     * @param value
     */
    public void removeValue(int value) {
        for (int i = 0; i < size; i++) {
            while(elementData[i] == value && i < size) { //i < size保证不会越界,此处可能会出现多个连续待删除的元素
                //此时位置i就是待删除元素下标
                removeIndex(i);
            }
        }
    }

    /**
     * 3.10.内部扩容方法
     */
    private void grow() {
        int oldLength = elementData.length;
        int newLength = oldLength << 1;
        //将原数组搬移到新数组
        elementData = Arrays.copyOf(elementData, newLength);
    }

    /**
     * 3.11.校验当前传入的index是否合法(用在查询,设置,删除)
     * @param index
     * @return
     */
    private boolean rangeCheck(int index) {
        if(index < 0 || index >= size) {
            System.err.println("当前index非法,index >= 0 && index < size");
            return false;
        }
        return true;
    }

    /**
     * 3.12.将当前动态数组转换为字符串 序列化
     * @return
     */
    public String toString() {
        //每个元素都在elementData中存储
        String ret = "[";
        for (int i = 0; i < size; i++) {
            ret += elementData[i];
            if(i != size - 1) {
                ret += ", ";
            }
        }
        ret += "]";
        return ret;
    }

    //测试
    public static void main(String[] args) {
        DynamicArray dynamicArray = new DynamicArray(2);
        dynamicArray.addFirst(1);
        dynamicArray.addFirst(2);
        dynamicArray.addFirst(3);
        dynamicArray.addLast(4);
        dynamicArray.addLast(4);
        dynamicArray.addLast(4);
        dynamicArray.addIndex(1, 11);
        dynamicArray.removeIndex(2);
        dynamicArray.removeValue(4);
        dynamicArray.set(2,9);
        System.out.println(dynamicArray.get(0));
        System.out.println(dynamicArray.contains(9));
        System.out.println(dynamicArray);
    }
}

3.1.在数组头部添加元素

/**
 * 在数组头部添加元素
 * @param data
 */
public void addFirst(int data) {
    //此时内部小数组已满,需要扩容
    if(size == elementData.length) {
       grow();
    }

    //将数组头部以及之后的元素依次向后移动一个单位(从后往前移动)
    for (int i = size - 1; i >= 0; i--) { //(size - 1)是最后一个元素的索引
         elementData[i + 1] = elementData[i];
    }

    //此时数组头部位置空出来,将元素插入即可
    elementData[0] = data;
    size++;
}

3.2.在数组尾部添加元素

/**
 * 在数组尾部添加元素
 * @param data
 */
public void addLast(int data) {
    //扩容
    if(size == elementData.length) {
       grow();
    }

    //数组中最后一个元素下标索引为size - 1, 在其后即size处添加元素
    elementData[size] = data;
    size++;
}

3.3.在数组任意index处添加元素

/**
 * 在数组任意index处添加元素
 * @param index
 * @param data
 */
public void addIndex(int index, int data) {
    //边界条件判断index是否合法
    //index = 0 头插; index = size 尾插
    if(index < 0 || index > size) {
       System.err.println("index非法,无法进行插入!");
       return;
    }
        
    //判断是否需要扩容
    if(size == elementData.length) {
       grow();
    }
        
    //数据插入
    if(index == 0) { //头插
       addFirst(data);
    } else if(index == size) { //尾插
       addLast(data);
    } else { //在index处添加
        //将index处及之后元素依次向后移动一个单位(从后向前移动)
        for (int i = size - 1; i >= index ; i--) {
             elementData[i + 1] = elementData[i];
        }
        //此时index处位置空出,插入元素即可
        elementData[index] = data;
        size++;
    }
}

3.4.取得当前数组长度

/**
 * 取得当前数组长度
 * @return
 */
public int getSize() {
    return size;
}

3.5.取得当前数组index位置处的元素值

/**
 * 取得当前index处的元素值
 * @param index
 * @return
 */
public int get(int index) {
    if(rangeCheck(index)) {
       return elementData[index];
    }
    return -1;
}

3.6.将数组指定位置index处的元素值修改为新的值newData

/**
 * 将数组指定位置index处的元素值修改为新的值newData
 * @param index
 * @param newData
 * @return 修改前的元素
 */
public int set(int index, int newData) {
    if(rangeCheck(index)) {
       int oldValue = elementData[index];
       elementData[index] = newData;
       return oldValue;
    }
    return -1;
}

3.7.判断当前数组中是否包含指定元素值data

/**
 * 判断当前集合中是否包含指定元素data
 * @param data
 * @return
 */
public boolean contains(int data) {
    for (int i = 0; i < size; i++) {
        if(elementData[i] == data) {
            return true;
        }
    }
    return false;
}

3.8.删除数组index索引处的元素值

/**
 * 删除数组index索引处的元素值
 * @param index
 */
public void removeIndex(int index) {
    //判断index合法
    if(rangeCheck(index)) {
       //从index位置开始,将之后的元素统一前移一个单位(从前向后移)
       for (int i = index; i < size - 1; i++) { //i < size - 1 走到倒数第二个元素(为了不越界)
            elementData[i] = elementData[i + 1]; //elementData[i + 1] 走到最后一个元素
       }
       size--;

       //确保数组中最后一个元素也还原为默认值
       elementData[size] = 0;
    }
}

3.9.删除数组中所有值等于value的元素

/**
 * 删除数组中所有值等于value的元素
 * @param value
 */
public void removeValue(int value) {
    for (int i = 0; i < size; i++) { //i < size 能走到最后一个元素
        while(elementData[i] == value && i < size) { //i < size 保证不会越界,此处可能会出现多个连续的待删除的元素,用while保证删除的正确性
            //此时位置i就是待删除元素的下标
            removeIndex(i);
        }
    }
}

3.10.内部扩容方法

/**
 * 内部扩容方法
 */
private void grow() {
    int oldLength = elementData.length;
    int newLength = oldLength << 1; //左移一位相当于 * 2
    //将原数组搬移到新数组
    elementData = Arrays.copyOf(elementData, newLength);
}

3.11.校验当前传入的index是否合法(用在查询,设置,删除)

/**
 * 效验当前传入的index是否合法(用在查询,设置,删除)
 * @param index
 * @return
 */
private boolean rangeCheck(int index) {
    if(index < 0 || index >= size) {
       //错误输出
       System.err.println("当前index非法,index >= 0 && index < size");
       return false;
    }
    return true;
}

3.12.将当前动态数组转换为字符串(序列化)

/**
 * 将当前动态数组转换为字符串(序列化)
 * @return
 */
public String toString() {
    String ret = "[";
    for (int i = 0; i < size; i++) {
        ret += elementData[i];
        if(i != size - 1) {
            ret += ", ";
        }
    }
    ret += "]";
    return ret;
}

4.此种数据结构的特点

4.1.优点

  • 查询快:当知道数组索引index时,可以立即查询元素值;
  • 修改快:当知道数组索引index时,可以立即修改元素值。

4.2.缺点

  • 在顺序表的头部/中间位置插入元素,删除元素时很耗时。时间复杂度为O(n),存在大量元素搬移工作;(而链表的插入和删除时间复杂度是O(1))
  • 顺序表的扩容也是一个耗时操作,需要释放旧空间,拷贝原有数组的值,申请新空间O(n);
  • 一般来说,顺序表的扩容都是按照原来的一倍扩容,造成大量空间的浪费。

PS:关于IDEA的调试

将出问题代码的那一行左边加断点:

选择调试运行:

调试区:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值