前言:什么是数据结构?
-
数据结构是计算机存储和组织数据的方式
-
线性结构:线性表(数组,链表,栈,队列,哈希表)
-
树形结构:二叉树,AVL树,红黑树,B树,堆,哈夫曼树,并查集
-
图形结构:邻接矩阵,邻接表
-
在实际应用中,我们要根据实际的应用场景来选择合适的数据结构
1,数组(Array)
- 数组是一种顺序存储的线性表,所有元素的内存地址都是连续的
- array存放在栈空间
- array里面的元素存放堆空间,每个元素占用4个字节
- 在堆空间的内存地址都是连续的
2,动态数组
- 可以通过一个数组来实现动态数组,动态数组的内存是变化的可以实现动态扩容
- 动态数组可以实现 增删改查 的操作
2.1,动态数组的暴露接口
-
外部调用实现的数组的常用函数接口
// 元素的数量 int size(); // 是否为空 boolean isEmpty(); // 是否包含某个元素 boolean contains(E element); // 添加元素到最后面 void add(E element); // 返回index位置对应的元素 E get(int index); // 设置index位置的元素 E set(int index, E element); // 往index位置添加元素 void add(int index, E element); // 删除index位置对应的元素 E remove(int index); // 查看元素的位置 int indexOf(E element); // 清除所有元素 void clear();
2.2,动态数组的设计
-
创建动态ArrayList首先要包含两个元素:
-
size属性:管理数组中元素的数量
-
elements属性:用来管理数组元素的存取
-
创建ArrayList
public class ArrayList { private int size; private E[] elements; }
-
初始化ArrayList,创建size,elements,设置elements的初始化空间
public class ArrayList { private int size; private E[] elements; // 设置elements数组默认的初始化空间 private static final int CAPACITY_DEFAULT = 10; public ArrayList(int capacity) { capacity = capacity < CAPACITY_DEFAULT ? CAPACITY_DEFAULT : capacity; elements = (E[]) new Object[capacity]; } // 默认情况 public ArrayList() { this(CAPACITY_DEFAULT); } }
3,动态数组的实现
3.1,元素的数量
-
我们创建ArrayList,已经初始化size,所以我们只需要返回当前size
/** * 元素的数量 * @return */ public int size() { return size; }
3.2,查看数组是否为空
-
可以通过判断size的数量,判断是否为空
/** * 是否为空 * @return */ public boolean isEmpty() { return size == 0; }
3.3,是否包含某个元素
-
当元素存在是,只需要判断索引内容是否为ELEMENT_NOT_FOUND = -1
/**
- 是否包含某个元素
- @param element
- @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
3.4,添加元素
- 当我们添加元素时,都是在数组的末尾添加。
- 这个 新添加的元素的索引 就是 **当前数组的长度,**size就是当前数组的长度,所以将新添加的元素放在size索引的位置,然后size加1
public void add(E element) {
elements[size] = element;
size++;
}
3.5,根据index查询元素
-
查询时,先检查index是否越界,直接取出对应索引的元素
public E get(int index) { rangeCheck(index); return elements[index]; } //提示输出错误 private void outOfBounds(int index) { throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size); } //检查index是否越界 private void rangeCheck(int index) { if (index < 0 || index >= size) { outOfBounds(index); } }
3.6,设置index位置的元素
-
获取索引位置元素进行替换
public E set(int index, E element) { // 判断索引是否越界 rangeCheck(index); // 取出被替换元素 E oldElement = elements[index]; // 替换元素 elements[index] = element; // 返回被替换元素 return oldElement; }
3.7,在index位置添加的元素
-
arrayList本来有5个元素,在index=2出入一个新的元素。需要把原来index=4的55,移动到index=5,依次向右移动
-
注意:在插入时,最后的元素要先移动,防止元素被覆盖
public void add(int index, E element) {
// 从后向前的顺序, 将元素向后移动
for (int i = size - 1; i >= index; i--) {
elements[i + 1] = elements[i];
}
// 插入元素
elements[index] = element;
// size+1
size++;
}
-
注意:插入的元素也不能越界,插入元素可以插入在最后一个位置,也就是index=size
public void rangeCheckForAdd(int index) { // 当索引小于0 或 大于 size时 抛出异常 if (index < 0 || index > size) { throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size); } }
3.8,删除index位置对应的元素
- arrayList的数组有7个元素,删除index=3的元素,索引为4,5,6的元素需要按顺序依次向左移动
- 去掉最后一个元素,size减1
public E remove(int index) {
// 判断索引是否越界
rangeCheck(index);
// 取出需要删除的元素
E element = elements[index];
// 通过循环, 将index后面的所有元素向前移动一位
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 删除最后一个元素
elements[--size] = null;
// 将删除的元素返回
return element;
}
//
private void rangeCheck(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
}
- 注意:传入index不能越界,不能大于等于size
3.9,查看元素的位置
-
首先判断传入元素是否为null,不为null,通过遍历元素进行对比,返回对应的索引
private static final int ELEMENT_ON_FOUND = -1; public int indexOf(E element) { if (element == null) { for (int i = 0; i < size; i++) { if (elements[i] == null) return i; } }else { for (int i = 0; i < size; i++) { if (element.equals(elements[i])) return i; } } return ELEMENT_ON_FOUND; }
3.10,清空数组
-
通过遍历索引,清空对应元素,size置为0
public void clear() { // 清空存储的数据 for (int i = 0; i < size; i++) { elements[i] = null; } // 将size置为0 size = 0; }
3.11,数组扩容
- 由于原来的数组空间固定,当元素存满时,就需要扩容。
- 数组扩容:原来的oldArray无法扩容,将原来的oldArray数组拷贝一份为newArray,对newArray进行扩容
- 将oldArray的数据移动到对应的newArray,elements的指针指向newArray
private void ensureCapacity() {
// 获取数组当前容量
int oldCapacity = elements.length;
// 如果 当前存储的元素个数 < 当前数组容量, 直接返回
if (size < oldCapacity) return;
// 新数组的容量为原数组容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 创建新数组
E[] newElements = (E[]) new Object[newCapacity];
// 原数组中的元素存储到新数组中
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
// 引用新数组
elements = newElements;
}