数据结构(java版)--------顺序表

一、线性结构

概念:

线性结构是一个有序数据元素的集合;

特点:

1、第一个元素称为首元素,第二个元素称为尾元素
2、除首元素和尾元素外的其他元素都有一个直接前驱和直接后继。
3、元素存在一对一的线性关系

常见的线性结构有:

顺序表、链表、栈、队列

-----------------------ok,先介绍顺序表------------------------------------

二、顺序表

2.1、介绍

顺序表在计算机内存中以数组的形式保存的一种数据结构,它是用一组地址连续的存储单元依次存储线性表中的个个元素。

优点:查询速度快,因为可以根据下标直接添加
缺点:删除和添加速度慢,如果在数组尾部添加元素没有什么影响,如果在数组中间或者头部添加元素需要挪动大量元素,删除同理。
后续看代码和图片就理解啦

画的不好
在这里插入图片描述

强调重点:
一、地址连续
在内存中及物理地址必须是连续的(要知道物理地址和逻辑地址的区别)
二、依次存储
比如图中下标为1和3之间就不能空一个元素,下标为2的位置也必须有值,这就叫依次存储。

2.2、 基本功能的实现:

1、获取顺序表元素个数
2、在表尾添加元素
3、在指定位置添加元素
4、删除指定位置的元素
5、自动扩容(对ArraysList源码的扩容有帮助)
2.3和2.4节重点介绍一下指定位置添加元素和删除元素
2.5节介绍自动扩容
下面是代码实现:

/**
 * 顺序存储结构
 * @author xxp
 */
public class MyArrayList<T> {
    /**
     * 使用数组模拟顺序存储结构
     */
    private Object[] items;
    /**
     * 存储元素的数量
     */
    private int size;

    /**
     * 创建MyArrayList的时候需要指定大小
     * @param capacity
     */
    public MyArrayList(int capacity) {
        //根据用户设定的大小来建立一个顺序表
        this.items=new Object[capacity];
        //初始化顺序表
        this.size=0;
    }

    //-----------顺序存储结构的主要操作方法-----------------------------------
	 /**
     * 扩容方法
     * @param size
     */
    public void reSize(int size){
        //new一个新数组
        Object[] newItems=new Object[size];
        //把老数组中的值转移到新数组中
        for (int i = 0; i < this.size; i++) {
            newItems[i]=this.items[i];
        }
        //把新数组赋值给老数组
        this.items=newItems;

    }
    /**
     * 获取当前存储元素的数量
     * @return
     */
     
    public int size(){
        return this.size;
    }

    /**
     * 获取指定位置的元素
     * @param i
     * @return
     */
    public T get(int i){
        return (T) this.items[i];
    }

    /**
     * 在尾部添加元素
     */
    public void add(T t){
      //判断是否数组已满
        if (this.size==this.items.length){
            //扩容到原来的2倍
           reSize(this.items.length*2);
        }
        //在尾部添加不需要移动元素,直接根据下表添加就行
        items[this.size]=t;
        //添加完后让当前元素大小加1
        size++;
    }

    /**
     * 在指定位置添加元素
     * @param position 添加位置
     * @param t        添加的元素
     */
    public void  add(int position,T t){
        //在指定位置添加元素,必须让元素从添加位置开始每个元素往后移动一个单位
        //元素必须从最后一个元素开始移动,如果从添加的位置移动会导致前面的元素覆盖后面的元素,导致数据丢失
        //size-1的意思:数组下标是从0开始的,size表是的是元素个数
        for (int j = this.size-1; j >= position; j--) {
            this.items[j+1]=this.items[j];
        }
        //全部移动完毕指定位置空出来之后,添加元素
        this.items[position]=t;
        //当前元素数量加1
        size++;
    }

    /**
     * 删除指定位置的元素
     * @param position 位置
     */
    public void remove(int position){
        //删除元素跟添加元素思路一致
        //指定位置的元素直接让后面元素依次往前移动,注意:不能从最后一个元素移动会导致前面元素被覆盖,导致数据丢失
        for (int i = position+1; i <= this.size; i++) {
            this.items[i-1]=this.items[i];
        }
        //当前元素数量减一
        size--;
    }
}

2.3、在指定位置添加元素

 public void  add(int position,T t){
       
        for (int j = this.size-1; j >= position; j--) {
            this.items[j+1]=this.items[j];
        }
        //全部移动完毕指定位置空出来之后,添加元素
        this.items[position]=t;
        //当前元素数量加1
        size++;
    }

在这里插入图片描述

时间复杂度是O(n)
在指定位置添加元素,让元素从添加位置开始每个元素往后移动一个单位
注意:元素必须从最后一个元素开始依次往后移动,如果从添加的位置移动会导致前面的元素覆盖后面的元素,导致数据丢失
size-1的意思:数组下标是从0开始的,size表是的是元素个数

2.4、删除指定位置的元素

    public void remove(int position){
        for (int i = position+1; i <= this.size; i++) {
            this.items[i-1]=this.items[i];
        }
        //当前元素数量减一
        size--;
    }

时间复杂度是O(n)
删除元素跟添加元素思路一致
从指定位置的元素开始,让后面元素依次往前移动。
注意:不能从最后一个元素依次移动会导致前面元素被覆盖,导致数据丢失
在这里插入图片描述

2.5、自动扩容

 /**
     * 扩容方法
     * @param size
     */
    public void reSize(int size){
        //new一个新数组
        Object[] newItems=new Object[size];
        //把老数组中的值转移到新数组中
        for (int i = 0; i < this.size; i++) {
            newItems[i]=this.items[i];
        }
        //把新数组赋值给老数组
        this.items=newItems;

    }
     /**
     * 在尾部添加元素
     */
    public void add(T t){
        //判断是否数组已满
        if (this.size==this.items.length){
            //扩容到原来的2倍
           reSize(this.items.length*2);
        }
        //在尾部添加不需要移动元素,直接根据下表添加就行
        items[this.size]=t;
        //添加完后让当前元素大小加1
        size++;
    }

在添加元素的时候可能会出现数组越界异常,这时候就要进行扩容。
扩容的思路也非常简单:
定义一个新数组,长度为原来的两倍,然后通过遍历把老数组中的元素放到新数组中,最后把新数组赋值给老数组就实现啦扩容。
之后可以在添加操作处加上if判断,判断当前添加元素后是否超过当前数组的长度。

2.6、ArraysList扩容源码

这里我截取一段扩容的源码
可以看到
int newCapacity = oldCapacity + (oldCapacity >> 1);
这句代码就是将老的数组长度通过位运算的方式扩大为原来的1.5倍
elementData = Arrays.copyOf(elementData, newCapacity);
这句代码就是将新数组的数据赋值给老数组,它采用的是Arrays自带的copyOf方法,这个方法最终是采用内存拷贝的方式,这个方式比我们使用for循环赋值要快的多的多
整体思路跟我们的差不多

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //扩大为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值