数据结构---->数组

1.什么是数组?

           数组是一种线性的数据结构.它同一组连续的内存空间,来存储一组具有相同类型的数据。

简单说明几点:

          (1).线性表:就是数据排成像一条线一样的结构。每个线性表的数据最多只有前和后两个方向。除了数组,链表,队列,栈等也是线性表结构。

              

                对立的是非线性表,比如二叉树,堆,图等之所以被称为非线性,是因为,在非线性表中,数据之间并不简单的前后关系。

             

         (2).数组是连续的内存空间和相同的数据类型。正是因为这样数据的随机访问的速度很快,有利就有弊,比如在数组中删除和插入一个数据,为了保证连续性,就需要大量的数据搬用   的工作。

2.说到数据的访问,那你知道数组是如何实现根据下标随机访问数组元素的吗?

      我们拿一个长度为 10 的 int 类型的数组 int[] a = new int[10] 来举例。在我画的这个图 中,计算机给数组 a[10],分配了一块连续内存空间 1000~1039,其中,内存块的首地址 为 base_address = 1000。

      我们知道,计算机会给每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。 当计算机需要随机访问数组中的某个元素时,它会首先通过啊a[i]_address=base-address+i * data_type_size 的寻址公式,计算出该元素 存储的内存地址.

其中 data_type_size 表示数组中每个元素的大小。我们举的这个例子里,数组中存储的是 int 类型数据,所以 data_type_size 就为 4 个字节。这个公式非常简单,我就不多做解释 了。

 ,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)

3.数组为了保持内存数据的连续性,会导致插入,删除这两个操作的效率比较低,那为什么会导致低效,有什么改进的方法?

      假设数组长度是n ,现在需要将一个数据插入到数组的第K个位置,为了吧k位置挪出来,给新来的数据,我们需要吧k—n 这部分的元素顺序往后移动一位,那么时间的复杂度是多少呢?

下面我们来分析一下:

     如果在数组的末尾插入元素,那就不需要移动元素了,那么时间的复杂度为O(1),如果在数组的开头插入元素,那么所有的数据需要一次向后移动一位,所以最坏的时间复杂度是O(N),因为我们在每个位置插入元素的概率是一样的,所以平均时间复杂度(1+2+。。。。+n)/n=O(n)

     如果数组中的数据是有序的,我们在某一个位置插入一个新元素时,就必须搬移k之后的数据。但是数组中的数据是无序的,没有规律,数据只是存储数据的集合。在这种情况下,如果要将元素插入到K位置,为了避免大规模的数据移动,之间将K位置的数据搬移到数组元素的最后,把新的元素之间放到第K个位置。删除的操作,为了内存的连续性,也需要移动数据,和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的 数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

    实际上,在某些特殊场景下,我们并不一定非得追求数组中数据的连续性。如果我们将多次 删除操作集中在一起执行,删除的效率是不是会提高很多呢?

    我们继续来看例子。数组 a[10] 中存储了 8 个元素:a,b,c,d,e,f,g,h。现在,我 们要依次删除 a,b,c 三个元素,为了避免 d,e,f,g,h 这几个数据会被搬移三次,我们可以先记录下已经删除的数据。 每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存 储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

 

4.容器能否完全替换数组?

(1).在Java中提供的容器类,ArrayList最大的优点就是可以将很多数组操作的细节封装起来,便于调用,另一个优点是支持动态扩容。

(2).数组本身在定义的时候预先指定了大小,因为需要分配连续的内存空间,如果我们分配了大小为10的数组,当第11个元素需要存储到数组当中时,我们需要重新分配一块更大的空间,将原来的数据复制过去,然后将新的数据插入。

(3).使用ArrayList,我们完全不需要关心底层的扩容逻辑,ArrayList 已经帮我们实现好了,存储空间不够的时候,会将空间自动扩容1.5倍的大小。

(4).ArrayList无法存储基本的数据类型,(8种基本数据类型),而是引用类型,所以基本数据类型,可以选用数组.

5.代码实现数组的增加删除查询的操作?

public class ArrayTest {

    private Object[] elementData; //定义一个空的数组

    private int size; //数组的大小

    //初始话数组的大小
    public ArrayTest() {
        elementData = new Object[10];
    }

    /**
     * 增加元素
     */
    public boolean add(Object obj) {
        ensureCapacityInternal(size + 1); 
        elementData[size++] = obj;
        return true;

    }
    //主要的判断数组是否需要扩容
    private void ensureCapacityInternal(int minCapaCity) {

        if (minCapaCity - elementData.length > 0) {
            grow(minCapaCity);
        }
    }
    //数组扩容的操作
    private void grow(int minCapaCity) {
        int oldcapaCity = elementData.length;
        int newCapaCity = oldcapaCity + oldcapaCity >> 1;
        if (newCapaCity - minCapaCity < 0) {
            newCapaCity = minCapaCity;
        }
        if (newCapaCity - Integer.MAX_VALUE > 0) {
            newCapaCity = Integer.MAX_VALUE;
        }
        elementData = Arrays.copyOf(elementData, newCapaCity);
    }

    /**
     * 删除数组中的元素,通过索引
     */
    public Object remove(int index) {
        rangCheck(index);
        Object oldValue = elementData[index];
        int moveNum = size - index - 1; //数组移动的次数
        if (moveNum > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, moveNum);
        }
        elementData[--size] = null;
        return oldValue;
    }
    //查询指定索引下数组的元素
    public Object get(int index) {
        rangCheck(index);
        return elementData[index];
    }

    private void rangCheck(int index) {
        if (index < 0 && index > size) {
            throw new IndexOutOfBoundsException("index:" + index + "size:" + size);
        }
    }


    public static void main(String[] args) {
        ArrayTest arrayTest = new ArrayTest();
        for (int i = 0; i < 10; i++) {
            arrayTest.add("第" + i + "个元素");
        }
        arrayTest.remove(3);
        for (int i = 0; i < 9; i++) {
            System.out.println(arrayTest.get(i));
        }
    }
}

6.总结:

数组用一块连续的内存空 间,来存储相同类型的一组数据,最大的特点就是支持随机访问,但插入、删除操作也因此 变得比较低效,平均情况时间复杂度为 O(n)。在平时的业务开发中,我们可以直接使用编 程语言提供的容器类,但是,如果是特别底层的开发,直接使用数组可能会更合适。

 

猜你喜欢

转载自www.cnblogs.com/xiaofuzi123456/p/11651331.html