一、顺序存储结构
数据在存放的过程中,是有顺序的,内存(存放的引用)是连续的。实际开发中使用ArrayList。
顺序表的概念:
- a1是a2的前驱,ai+1 是ai的后继,a1没有前驱,an没有后继
- n(size)为线性表的长度 ,若n == 0 时,线性表为空表,若n==size时,线性表放满了。
【例1】
线性表:int[] array = new int[]{1, 2, 3, 4, 5}
【例2】
class Students {
Student[40] stus; // 对象数组是线性表
int size;
}
二、顺序表的操作
顺序表的操作:查询,修改,插入,删除,排序
(一)查询(二分查找请见后续文章)
public int search(int[] list, int des) {
for (int i = 0; i < list.length; i++) {
if (list[i] == des) {
return i;
}
}
return -1;
}
(二)修改
public void modify(int[] list, int index, int des) {
list[index] = des;
}
(三)插入
不可以随意的插入,必须要把要插入的位置以后全部往后移,才能插入到指定的位置。
// 数组要预留出来空余内容,暂时不考虑动态数组
public void instert(int[] list, int index, int des) {
// 如果在尾部插入数据,那么for循环没有执行
for (int i = list.length - 1; i > index; i--) {
list[i] = list[i-1];
}
list[index] = des;
}
- 优点:尾部插入效率高,支持随机访问
- 缺点:中间插入或者删除效率低
(四)删除
public void delete(int[] list, int index) {
for (int i = index; i < list.length - 1; i++) {
list[i] = list[i+1];
}
list[list.length - 1] = 0;
}
三、顺序表的操作之【排序】 – 蛮力法
蛮力法(brute force method,也称为穷举法或枚举法) 是一种简单直接地解决问题的方法, 常常直接基于问题的描述, 所以,蛮力法也是最容易应用的方法。 但是,用蛮力法设计的算法时间特性往往也是最低的(当n => ∞的时候), 典型的指数时间算法一般都是通过蛮力搜索而得到的 。(即输入资料的数量依线性成长,所花的时间将会以指数成长)
Arrays.sort(Object[] a); 实际上用的是binarySort + 递归 + 分开截断,至少执行了100行代码以上,所以不建议直接使用JDK提供的排序方法
- 当n => ∞的时候,效率是最低的。【e.g.】y = n2 ,当n = 100时,y=10000,需要计算10000才能获得最终的排序。
- 但是如果在数据量足够小的情况下,是所有算法中最快的。
(一)冒泡排序
通过相邻的两两比较后,将最值固定到当前排的最后面。
关键代码的执行次数,就是交换的次数:
- 第一次:n次
- 第二次:n-1次
- 第三次:n-2次
- …
- 第m次:1次
总共交换次数(即时间复杂度):(n-1) + (n-2) + (n-3) + … + 1 = ((n-1) * ((n-1) + 1 ) / 2 = n * (n-1) / 2
当数据量足够小(3-5个数据),时间复杂度就趋向于n。如果n < 5,即会比任何排序执行速度都快。
public void bubbleSort(int[] list) {
for (int i = list.length - 1; i > 0; i--) {
boolean flag = true;
for (int j = 0; j < i; j++) {
if (list[j] > list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
flag = false;
}
}
if (flag) { // 减少一定的排序次数
break;
}
}
}
【实例】斗牛采用的就是冒泡排序,具体见GitHub。
(二)选择排序
先把第一位固定下来,再从后面挑最小的,然后交换,最值在最前面
选择排序的交换次数会比冒泡排序少。
public void selectSort(int[] list) {
for (int i = 0; i < list.length; i++) {
int index = i;
for (int j = index + 1; j < list.length; j++) {
if (list[i] > list[j]) {
index = j; // 获取最值的角标
}
}
if (i != index) { // 减少循环次数
int temp = list[i];
list[i] = list[index];
list[index] = temp;
}
}
}
四、ArrayList原码简单分析
public class ArrayList<E> {
private static final int DEFAULT_CAPACITY = 10; // 默认容量,会开辟一个大小为10的数组
private static final Object[] EMPTY_ELEMENTDATA = {}; // 初始化一个长度为空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // (默认容量)初始化一个长度为空的数组
transient Object[] elementData; // 对象数组(什么对象都能存储)
// transient表示不参与序列化,瞬态变量,一瞬间的变量,不具备持久化功能,不会存到物理存储空间上
private int size;
// 默认构造,创建一个长度为空的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 给定一个数组容量,创建对应长度的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
// 【增】添加到末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 检查数组容量
elementData[size++] = e;
return true;
}
// 【增】在指定位置上添加对应的元素
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // 检查数组容量
System.arraycopy(elementData, index, elementData, index + 1, size - index);
// 将数组elementData的index位置,拷贝size - index个元素,到数组elementData的 index + 1的位置
// 即index位置上的元素往后挪动一位。
// arraycopy通过循环实现数组的拷贝
elementData[index] = element; // 将新的元素element,放到index的位置上
size++;
}
// 容量检测的过程,如果当前size + 1,还满足数组容量,直接操作,如果不满足要求,扩容。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity); -- > 调用到: grow(minCapacity);
}
// 数组扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // oldCapacity + oldCapacity / 2^1 = 1.5 * oldCapacity
//(右移:除以2的几次幂, 左移:乘以2的几次幂)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); // 数组拷贝的动作,把原来的elementData拷贝的新的newCapacity
}
}
- 扩容的过程:
- 代码执行过程图示:
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;