乐趣区

数据结构(二)数组

数组就是把数据码成一排进行存放:数组的最大优点:快速查询。scores[2]
我们基于 Java 的静态数组,封装一个属于自己的动态数组类 Array,加深对于数组这种数据结构的理解。

我们基于 Java 静态数组 data 来封装我们的动态数组 Array 类,capacity 表示数组容量,可以通过 data.length 获得。size 表示数组元素个数,初始为 0(也可以看成是下一个新增元素的索引位置)。
据此,设计 Array 类结构。
初始数组类结构
public class Array<E> {

private E[] data;
private int size;

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

// 无参数的构造函数,默认数组的容量 capacity=10
public Array() {
this(10);
}

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

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

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

}
向数组末尾添加元素
添加元素前:添加元素后:分析得出,如果要向数组添加元素 e,只要在 size 所在索引位置设置元素 e,然后 size 向后移动(size++)即可。
据此,增加添加元素相关的代码:
// 在所有元素后添加一个新元素
public void addLast(E e) {
if(size == data.length) {// 数组容量已填满,则不能再添加
throw new IllegalArgumentException(“AddLast failed. Array is full.”);
}
data[size] = e;
size++;
}
向指定位置添加元素
添加前:添加后:
分析得出,只要把要插入元素的索引位置的当前元素以及其后的所有元素,都往后挪动一位,然后在指定索引位置设置新元素即可,最后 size++。为避免元素覆盖,具体挪的时候,需要从后往前推进完成元素挪动的整个过程。
修改代码:
// 在第 index 个位置插入一个新元素 e
public void add(int index, E e) {
if(size == data.length) {
throw new IllegalArgumentException(“Add failed. Array is full.”);
}

if(index < 0 || index > size) {
throw new IllegalArgumentException(“Add failed. Require index >= 0 and index <= size.”);
}

for(int i = size – 1; i >= index; i–) {
data[i + 1] = data[i];
}

data[index] = e;
size++;
}
调整 addLast,复用 add 方法,同时增加一个 addFirst:
// 在所有元素后添加一个新元素
public void addLast(E e) {
add(size, e);
}

// 在所有元素前添加一个新元素
public void addFirst(E e) {
add(0, e);
}
获取元素和修改元素
// 获取 index 索引位置的元素
public E get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Get failed. Index is illegal.”);
}
return data[index];
}

// 修改 index 索引位置的元素
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Set failed. Index is illegal.”);
}
data[index] = e;
}
包含、搜索
// 查找数组中是否有元素 e
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) {
return true;
}
}
return false;
}

// 查找数组中元素 e 所在的索引,如果不存在元素 e,则返回 -1
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) {
return i;
}
}
return -1;
}
从数组中删除元素
删除前:
删除后:
分析得出,只要将要删除位置之后的元素都往前挪动一位即可。然后 size 减 1。
修改代码:
// 从数组中删除 index 位置的元素,返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Remove failed. Index is illegal.”);
}
E ret = data[index];
for (int i = index + 1; i < size; i++) {
data[i-1] = data[i];
}
size–;

return ret;
}

// 从数组中删除第一个元素,返回删除的元素
public E removeFirst() {
return remove(0);
}

// 从数组中删除最后一个元素,返回删除的元素
public E removeLast() {
return remove(size – 1);
}

// 从数组中删除元素 e(只删除一个)
public boolean removeElement(E e) {
int index = find(e);
if (index != -1) {
remove(index);
return true;
}
return false;
}
动态数组
容量开太大,浪费空间,容量开小了,空间不够用。所以需要实现动态数组。
具体做法如下:就是在方法中开辟一个更大容量的数组,循环遍历原来的数组元素,设置到新的数组上。然后将原数组的 data 指向新数组。
修改代码:
// 数组容量扩容 / 缩容
public void resize(int newCapacity) {
E[] newData = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
修改添加元素的代码,添加时自动扩容:
// 在第 index 个位置插入一个新元素 e
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException(“AddLast failed. Require index >= 0 and index <= size”);
}
if (size == data.length) {
resize(2 * data.length); // 扩容为原来的 2 倍
}

for (int i = size – 1; i >= index; i–) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
修改删除元素的代码,必要时自动缩容:
// 从数组中删除 index 位置的元素,返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Remove failed. Index is illegal.”);
}
E ret = data[index];
for (int i = index + 1; i < size; i++) {
data[i – 1] = data[i];
}
size–;
data[size] = null; // loitering objects != memory leak

if (size == data.length / 4 && data.length / 2 != 0) {// 缩减数组使用 lazy 方式(避免复杂度震荡),在 1 / 4 的时候缩容
resize(data.length / 2); // 缩容为原来的一半
}
return ret;
}
测试数组
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format(“Array: size = %d, capacity = %d\n”, size, data.length));
res.append(“[“);
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size – 1) {
res.append(“, “);
}
}
res.append(“]”);
return res.toString();
}

public static void main(String[] args) {

Array<Integer> arr = new Array<>();
for (int i = 0; i < 10; i++) {
arr.addLast(i);
}
System.out.println(arr);

arr.add(1, 100);
System.out.println(arr);

arr.addFirst(-1);
System.out.println(arr);

arr.remove(2);
System.out.println(arr);

arr.removeElement(4);
System.out.println(arr);

arr.removeFirst();
System.out.println(arr);

}
console 输出:
Array: size = 10, capacity = 10
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array: size = 11, capacity = 20
[0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array: size = 12, capacity = 20
[-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array: size = 11, capacity = 20
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array: size = 10, capacity = 20
[-1, 0, 1, 2, 3, 5, 6, 7, 8, 9]
Array: size = 9, capacity = 20
[0, 1, 2, 3, 5, 6, 7, 8, 9]
完整代码
public class Array<E> {

private E[] data;
private int size;

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

// 无参数的构造函数,默认数组的容量 capacity=10
public Array() {
this(10);
}

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

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

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

// 在第 index 个位置插入一个新元素 e
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException(“Add failed. Require index >= 0 and index <= size”);
}
if (size == data.length) {
resize(2 * data.length); // 扩容为原来的 2 倍
}

for (int i = size – 1; i >= index; i–) {
data[i + 1] = data[i];
}

data[index] = e;
size++;
}

// 在所有元素后添加一个新元素
public void addLast(E e) {
add(size, e);
}

// 在所有元素前添加一个新元素
public void addFirst(E e) {
add(0, e);
}

// 获取 index 索引位置的元素
public E get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Get failed. Index is illegal.”);
}
return data[index];
}

// 修改 index 索引位置的元素
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Set failed. Index is illegal.”);
}
data[index] = e;
}

// 查找数组中是否有元素 e
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) {
return true;
}
}
return false;
}

// 查找数组中元素 e 所在的索引,如果不存在元素 e,则返回 -1
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) {
return i;
}
}
return -1;
}

// 从数组中删除 index 位置的元素,返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException(“Remove failed. Index is illegal.”);
}
E ret = data[index];
for (int i = index + 1; i < size; i++) {
data[i – 1] = data[i];
}
size–;
data[size] = null; // loitering objects != memory leak

if (size == data.length / 4 && data.length / 2 != 0) {// 缩减数组使用 lazy 方式(避免复杂度震荡),在 1 / 4 的时候才缩容
resize(data.length / 2); // 缩容为原来的一半
}

return ret;
}

// 从数组中删除第一个元素,返回删除的元素
public E removeFirst() {
return remove(0);
}

// 从数组中删除最后一个元素,返回删除的元素
public E removeLast() {
return remove(size – 1);
}

// 从数组中删除元素 e(只删除一个)
public boolean removeElement(E e) {
int index = find(e);
if (index != -1) {
remove(index);
return true;
}
return false;
}

public void resize(int newCapacity) {
E[] newData = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}

@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format(“Array: size = %d, capacity = %d\n”, size, data.length));
res.append(“[“);
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size – 1) {
res.append(“, “);
}
}
res.append(“]”);
return res.toString();
}

public static void main(String[] args) {

Array<Integer> arr = new Array<>();
for (int i = 0; i < 10; i++) {
arr.addLast(i);
}
System.out.println(arr);

arr.add(1, 100);
System.out.println(arr);

arr.addFirst(-1);
System.out.println(arr);

arr.remove(2);
System.out.println(arr);

arr.removeElement(4);
System.out.println(arr);

arr.removeFirst();
System.out.println(arr);

}

}

退出移动版