数组是一个有限的、类型相同的数据的集合,在内存中是一段连续的内存区域,这段内存区域一旦定义,其大小不改变。

数组的长度在定义时确定,数组中的元素的默认值由其数组类型决定。可通过数组下标访问数组中元素,下标的起始位置永远从0开始。
数组在访问操作方面有着独特的性能优势,因为数组不仅仅支持顺序访问,还支持随机访问,如图所示。

我们可以通过下标随机访问数组中任何一个元素,其原理是因为数组中元素的存储是连续的,所以我们可以通过数组内存空间的首地址加上元素的偏移量计算出某一个元素的内存地址,例如,array[n]的地址 = array数组内存空间的首地址 + 每个元素大小*n。
构建一个长度为5的整数数组int[]array=new int[5],在内存中会开辟一块连续的内容空间,假设其首地址为2000,其它元素的地址表示,如图所示。

总之,当我们通过下标去访问数组中的数据时,并不需要从头开始依次遍历数组,因此数组的访问时间复杂度是 O(1),当然这里需要注意,如果不是通过下标去访问,而是通过内容去查找数组中的元素,则时间复杂度不是O(1),极端的情况下需要遍历整个数组的元素,时间复杂度可能是O(n),当然通过不同的查找算法所需的时间复杂度是不一样的。
数组元素的连续性,导致数组在插入和删除元素的时候效率比较低。如果要在数组中间插入一个新元素,就必须要将要相邻的后面的元素全部往后移动一个位置,留出空位给这个新元素,如图所示。

数组插入时,首先要检测数组中是否有足够的空间可以存储新的元素,假如不足还需要对数组进行扩容。一般会重新申请一个相对原数组1.5倍大小的存储空间(因为数组在内存中是一块连续的内存空间,一旦定义其长度不可以再进行修改),并且把原来的数据拷贝到新申请的空间上。假如现在数组空间是足够的,新元素要插入在数组的最开头位置,那整个原始数组都需要向后移动一位,此时的时间复杂度为最坏情况即O(n),如果新元素要插入的位置是最末尾,则无需其它元素移动,则此时时间复杂度为最好情况即O(1),所以平均而言数组插入的时间复杂度是O(n)。
Java中数组插入操作,其代码示例如下:
// function to search a key to
// be deleted
static int findElement(int arr[], int n, int key) {
int i;
for (i = 0; i < n; i++)
if (arr[i] == key)
return i;
return -1;
}
数组的删除与插入类似,假如不是删除最后一个有效元素,其它元素在删除以后,后续元素都要向前移动,如图-5所示:

图-5
数组删除时,如果删除数组末尾的数据,则最好情况时间复杂度为O(1)。如果删除开头的数据,则最坏情况时间复杂度为O(n)。所以平均情况时间复杂度也为O(n)。Java中的数组删除操作,其实现代码如下:
// function to search a key to
// be deleted
static int findElement(int arr[], int n, int key) {
int i;
for (i = 0; i < n; i++)
if (arr[i] == key)
return i;
return -1;
}
// Function to delete an element
static int deleteElement(int arr[], int n, int key) {
// Find position of element to be
// deleted
int pos = findElement(arr, n, key);
if (pos == -1)
{
System.out.println("Element not found");
return n;
}
// Deleting element
int i;
for (i = pos; i< n - 1; i++)
arr[i] = arr[i + 1];
return n -1;
}
}
public class SimpleArrayContainer {
private Object[] array;
private int size;
public SimpleArrayContainer() {
this(10);
}
public SimpleArrayContainer(int capacity) {
this.array=new Object[capacity];
}
private Object[] copyOf(Object[] source,int length) {
//1)创建新数组,并定义其长度为原数组的2倍大小
Object[] newArray=new Object[2*length];
//2)拷贝原有数组内容到新数组
for(int i=0;i<source.length;i++) {
newArray[i]=source[i];
}
//3)返回新创建的数组
return newArray;
}
//定义向数组size位置添加新元素的方法
public void add(Object element) {
//1.数组是否已满了,满了则扩容
if(size==array.length) {
//array=copyOf(array, 2*array.length);
array=Arrays.copyOf(array, 2*array.length);
}
//2.添加新的元素
array[size++]=element;
}
//定义在指定位置添加新的元素的方法
public void add(int index,Object element) {
if(index<0||index>size)
throw new IndexOutOfBoundsException();
if(size==array.length) {
array=Arrays.copyOf(array, 2*array.length);
}
System.arraycopy(array, index, array, index+1, size-index);
array[index]=element;
size++;
}
//定义按指定位置删除数组元素的方法
public Object remove(int index) {
//1.参数校验
if(index<0||index>=size)
throw new IndexOutOfBoundsException();
//2.获取index位置元素
Object element=array[index];
//3.移动元素
// int numMoved=size-index-1;
// if(numMoved>0)
// System.arraycopy(array, index+1, array, index, numMoved);
// //4.修改最后一个元素的值,并且有效元素个数减1
// this.array[--size]=null;
fastRemove(index);
return element;
}
private void fastRemove(int index) {
int numMoved=size-index-1;
if(numMoved>0)
System.arraycopy(array, index+1, array, index, numMoved);
array[--size]=null;
}
//按元素值移除元素
public boolean remove(Object element) {
if(element==null) {
for(int index=0;index<size;index++) {
if(array[index]==null) {
//移除元素
fastRemove(index);
return true;
}
}
}else {
for(int index=0;index<size;index++) {
if(array[index].equals(element)) {
//移除元素
fastRemove(index);
return true;
}
}
}
return false;
}
public Object get(int index) {
if(index<0||index>=size)
throw new IndexOutOfBoundsException();
return this.array[index];
}
@Override
public String toString() {
return Arrays.toString(array);
}
public int size() {
return size;
}
public static void main(String[] args) {
SimpleArrayContainer container=new SimpleArrayContainer(2);
container.add(100);//size
container.add(200);
container.add(300);
System.out.println(container);//[100,200,300]
container.add(1, 400);
System.out.println(container);//[100,400,200,300]
container.remove(1);
System.out.println(container);//[100,200,300]
container.remove((Object)200);
System.out.println(container);//[100,300]
Object element=container.get(0);
System.out.println(element);
}
}
初始数组为Input,然后向左旋转数组实现Output数组结果
Input : arr[] = [1, 2, 3, 4, 5, 6, 7]
rotate Left
Output : arr[] = [3, 4, 5, 6, 7, 1, 2]
初始数组为Input,然后向右旋转数组实现Output数组结果
Input : arr[] = [1, 2, 3, 4, 5, 6, 7]
rotate right
Output : arr[] = [6, 7, 1, 2, 3, 4, 5]
具体实现方案如下:
package com.cy.pj.ds.array;
import java.util.Arrays;
public class RotateLeftArray {
//左转方案1:时间复杂度为O(n),辅助空间复杂度为O(count)
static void doRotateLeft01(int[] source,int count) {
//1.构建临时数组,用于存储需要移动数组右边的元素
int[] temp=new int[count];
for(int i=0;i<temp.length;i++) {//空间复杂度 S=O[count]
temp[i]=source[i];
}
//2.移动source数组中剩余元素
for(int i=count;i<source.length;i++) {//时间复杂度 t=O[n]
source[i-count]=source[i];
}
//3.将temp数组中的元素存储到source数组
for(int i=0;i<temp.length;i++) {
source[source.length-(count-i)]=temp[i];
}
}
//左转方案2:时间复杂度O(n*count),辅助空间O(1)
//分多次移动数组元素
//[1,2,3,4,5,6,7]
//[2,3,4,5,6,7,1]
//[3,4,5,6,7,1,2]
static void doRotateLeft02(int[] source,int count) {
for(int i=0;i<count;i++) {//外层循环控制移动次数,T=O(n*count)
int temp=source[0],j;//空间复杂度,S=O(1)
for(j=0;j<source.length-1;j++) {//移动元素的个数
source[j]=source[j+1];
}
source[j]=temp;
}
}
//右转方案1:时间复杂度为O(n*count),辅助空间为O(1)
static void doRotateRight01(int[] source,int count) {
int len=source.length-1;
for(int i=0;i<count;i++) {
int temp=source[len],j;
for(j=len;j>0;j--) {
source[j]=source[j-1];
}
source[j]=temp;
}
}
public static void main(String[] args) {
//rotate left
//intput:[1,2,3,4,5,6,7]
//rotate left
//output:[3,4,5,6,7,1,2]
int[] source= {1,2,3,4,5,6,7};
//doRotateLeft01(source,2);
//doRotateLeft02(source,2);
//rotate right
//intput:[1,2,3,4,5,6,7]
//rotate right
//output:[6,7,1,2,3,4,5]
doRotateRight01(source, 2);
System.out.println(Arrays.toString(source));
}
}
static int binarySearch(int arr[],
int low, int high, int key)
{
if (high < low)
return -1;
/* low + (high - low)/2; */
int mid = (low + high)/2;
if (key == arr[mid])
return mid;
if (key > arr[mid])
return binarySearch(arr, (mid + 1), high, key);
return binarySearch(arr, low, (mid -1), key);
}
数组连续的内存空间,可以借助CPU的缓存机制,预读数组中的数据,所以数据也就具备了很好的“随机访问”的性能。但有利就有弊,这个限制也让数组的很多操作变得非常低效。例如,数组中删除、插入一个数据时,为了保证连续性,就需要做大量的数据移动操作。