数组

操作:增删改查(类比数据库)
属性:容量(capacity)、大小(size)

初始化

成员变量:数组data、 数组大小size
构造函数,传入数组的容量capacity构造Array,初始化size=0
无参数构造函数,默认数组容量capacity=10
获取数组中的元素个数
获取数据的容量
返回数组是否为空


// 定义泛型数组
public class Array<E> {

	private E[] data;
	private int size;

	// 构造函数,传入数组的容量capacity构造Array
	public Array(int capacity){
		data = (E[])new Object[capacity];
		size = 0;
	}
    
    // 无参数构造函数,默认容量10
	public Array(){
		this(10);
	}

	// 获取数组中的元素个数
	public int getSize(){
		return size;
	}

    // 获取数组容量
    public int getCapacity(){
		return data.length;
	}

	// 返回数组是否为空
	public boolean isEmpty(){
		return size == 0;
	}

	// ……

}

添加元素

向数组末尾添加元素e

addLast(int e)


/**
 * 方法1
 */

// 校验:数组是否已满
if(size == data.length){
   // 数组容量已满,抛出异常
}

// 添加元素
data[size] = e;
// 维护size
size++;

/**
 * 方法2
 */

// 直接调用add函数
add(size, e);

向数组头部添加元素e

addFirst(int e)


// 直接调用add函数
add(0, e);

在数组index位置插入元素e

add(int index, int e)


// 校验:index是否合法
if(index < 0 || index > size){
   // index不能为负数,且不能大于size(即数组中间不能含有空元素)
}

// 校验:数组是否已满
if(size == data.length){
   // 处理1:数组容量已满,抛出异常

   // 处理2:进行扩容
   resize(2*data.length);
}

// 将>=index位置的所有元素往后移动一位
for(int i = size - 1; i >= index ; i --){
	data[i+1] = data[i];
}

// index位置插入元素e
data[index] = e;
// 维护size
size++;

获取元素

get(int index)


// 校验:index是否合法
if (index < 0 || index >= szie) {
	// index 非法
}

return data[index];

修改元素

set(int index, int e)


// 校验:index是否合法
if (index < 0 || index >= szie) {
	// index 非法
}

data[index] = e;


包含元素

contains(int e)


for (int i = 0; i < size; i++) {
	if (data[i] == e) {
		return true;
	}
}

return false;

查找元素

find(int e)


// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1

for (int i = 0; i < size; i++) {
	if (data[i] == e) {
		return i;
	}
}

return -1;

删除元素

删除index位置的元素,返回删除的元素

remove(int index)


// 校验:index是否合法
if (index < 0 || index >= szie) {
	// index 非法
}

// 记录待返回的元素
int ret = data[index];

// 依次循环,将index之后位置的元素,统一向前移动一位,将index挤出去
for (int i = index + 1; i < size; i++) {
	data[i - 1] = data[i];
}

// 维护size
size--;
// data[size] = null;

// 方案1
if (size == data.length / 2) {
	resize(data.length/2);
}
// 方案2(防止复杂度震荡)
if (size == data.length / 4 && data.length / 2 != 0) {
	resize(data.length/2);
}

// 返回删除元素 
return ret;

删除第一个元素,返回删除的元素

removeFirst()


return remove(0);

删除最后一个元素,返回删除的元素

removeLast()


return remove(size - 1);

删除元素e

removeElement(int e)


int index = find(e);

if (index != -1) {
	remove(index);
}

使用泛型

让数据结构可以放置“任何”数据类型 不可以是基本数据类型,只能是类对象
boolean 、 byte 、 char 、 short 、 int 、 long 、 float 、 double 每个基本数据类型都有对应的包装类 Boolean 、 Byte 、 Char 、 Short 、 Int 、 Long 、 Float 、 Double

动态数组

resize(int newCapacity)


E[] newData = (E[])new Object[newCapacity];

for (int i = 0; i < size; i++) {
	newData[i] = data[i];
}

data = newData;

时间复杂度

O(1),O(n),O(lgn),O(nlogn),O(n^2)
大O描述的是算法的运行时间和输入数据之间的关系

动态数组的时间复杂度

  • 增:O(n)
  • 删:O(n)
  • 改:已知索引O(1),未知索引O(n)
  • 查:已知索引O(1),未知索引O(n)

均摊复杂度

动态数组扩容与缩容不是每次都发生的 平均看来是每两次操作促发一次 按均摊复杂度分析,动态数组增、删元素时间复杂度为O(1)

复杂度震荡

removeLast时resize过于着急(Eager)
解决方案:Lazy 当size == capacity/4 时,才将capacity减半

上次更新: 3/12/2019, 11:33:58 PM