相关文章:
数据结构基本概念
一、逻辑结构
- 集合:元素罗列在一起
- 线性结构:元素前后相继一一对应
- 树形结构:元素存在一对多的关系
- 图结构或网状结构:元素之间存在多对多的关系
二、存储结构
- 顺序存储:地址连续,用数组
- 链式存储:地址不连续,用指针
三、数据结构定义
- 数据结构:为了某种特殊的需求而专门设计的存储方式
四、数据结构的通用的几个操作
- 插入 (add, insert)
- 删除 (delete)
- 修改 (update)
- 查找 (get, indexOf)
- 遍历
- 排序 (sort)
数组实现线性表
一、基本概念
底层是数组,实际上就是Java中的ArrayList。
二、接口
public interface MyList {
// 新增一个元素
void add(Object element);
// 删除一个元素
boolean delete(Object element);
// 根据索引删除一个元素
boolean delete(int index);
// 将指定位置元素更新为新元素
boolean update(int index, Object newElement);
// 当前列表中是否含有target这个元素
boolean contains(Object target);
// 返回指定索引的元素
Object get(int index);
// 返回列表大小
int size();
// 查找指定元素下标
int indexOf(Object element);
}
三、功能实现
public class MyArrayList implements MyList {
// 列表底层结构
private Object[] elements;
// 列表中已经存储的元素个数
private int size = 0;
// 列表的容量大小, 默认为10
private int capacity = 10;
// 自定义容量创建列表
public MyArrayList(int capacity) {
this.capacity = capacity;
elements = new Object[capacity];
}
// 默认构造函数
public MyArrayList() {
elements = new Object[capacity];
}
@Override
public void add(Object element) {
// 扩容:把原来的小容量数组拷贝到新的大容量数组,丢弃小容量数组
if (size >= capacity) {
capacity *= 2;
Object[] newArr = new Object[capacity];
System.arraycopy(elements, 0, newArr, 0, elements.length);
elements = newArr;
}
// 这里的size既表示列表中已经存储的元素个数,也表示列表中的下一个存放元素的下标
// 即size指向elements中第一个为null的元素
elements[size++] = element;
}
@Override
public boolean delete(Object element) {
return indexOf(element) >= 0 && delete(indexOf(element));
}
@Override
public boolean delete(int index) {
if (index > size - 1) return false;
// 进行元素位移覆盖
System.arraycopy(elements, index + 1, elements, index, size - index);
size--;
return true;
}
@Override
public boolean update(int index, Object newElement) {
if (index > size - 1) return false;
elements[index] = newElement;
return true;
}
@Override
public boolean contains(Object target) {
return indexOf(target) >= 0;
}
@Override
public Object get(int index) {
return elements[index];
}
public int size() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(elements[i] + (i == size - 1 ? "" : ", "));
}
sb.append("]");
return sb.toString();
}
@Override
public int indexOf(Object element) {
for (int i = 0; i < size; i++) {
if (elements[i].equals(element)) return i;
}
return -1;
}
}
四、几个关键的地方
-
对于列表的size属性的理解
(1) size值的是列表中已经存储的元素个数
(2) 也可以把size理解成指向底层数组中的第一个为null的元素的指针 -
void add(Object element)
功能实现public void add(Object element) { // 扩容:把原来的小容量数组拷贝到新的大容量数组,丢弃小容量数组 if (size >= capacity) { capacity *= 2; Object[] newArr = new Object[capacity]; System.arraycopy(elements, 0, newArr, 0, elements.length); elements = newArr; } // 这里的size既表示列表中已经存储的元素个数,也表示列表中的下一个存放元素的下标 // 即size指向elements中第一个为null的元素 elements[size++] = element; }
注意:
- 数组的扩容:由于线性表的底层是数组,当线性表存储的元素大于了其容量时,必须进行数组的扩容,扩容的思路为把原来存满的数组拷贝到新的扩容后数组中,丢弃原来的数组,使用现在新的大容量数组。
- 数组的扩容效率很低,所以使用线性表的时候最好预估即将存储的元素数量,为线性表指定capacity。
-
boolean delete(int index)
功能实现public boolean delete(int index) { if (index > size - 1) return false; // 进行元素位移覆盖 System.arraycopy(elements, index + 1, elements, index, size - index); size--; return true; }
注意:
- 数组的位移:将指定下标右边的那部分元素(包括最后一个元素右边的null)向左位移覆盖。
- 数组的位移效率很低,所以最好不要使用线性表进行中间元素的增删。