下面笔者将结束一种简单的数据结构-线性表中的顺序表。相信大家也会在程序中时常用到ArrayList这个类,对这个ArrayList的相关方法的使用也并不陌生。笔者所实现的一个顺序表(MyArrayList)和java类库中的ArrayList相似,但是没有类库中的复杂,只是简单的介绍ArrayList中的重要方法,以加深对顺序表的学习和理解,如果在程序中还是建议读者采用类库中的ArrayList。当然笔者也可以参照类库中的ArrayList自己实现一个自己的顺序表。
线性表:即具有相同类型的有限序列。 顺序表:是用一段地址连续的存储单元(数组)依次存储线性表的数据元素,元素之间在物理位置上具有邻接关系。
链表: 用一组任意的存储单元(内存空间可以不连续)存放线性表的元素,在C语言中通过指针链接每个元素,而java中则通过引用实现这种链接。
接下来,笔者先介绍顺序表,再介绍链表以及它俩之间的区别。
笔者定义了一个自己的顺序表类: MyArrayList 用 data 数组以及 length 存放数据元素以及数据的大小,并在构造函数中初始化。在这里笔者也通过泛型可以传递任何对象参数。
private Object data[];
private int length;
// 不制定线性表默认大小为10
public MyArrayList() {
this(10);
}
// 创建制定大小的线性表
public MyArrayList(int n) {
data = new Object[n];
length = 0;
}
// 传入数组创建线性表
public MyArrayList(T a[]) {
this.data = new Object[a.length];
for (int i = 0; i < a.length; i++) {
data[i] = a[i];
}
length = a.length;
}
顺序表中插入元素。若不制定插入位置,则默认在数组的最后面插入;若当前数组已满,则扩展数组的大小为当前数组大小的两倍,并将数据插入到制定位置。插入式,若在数组的最后面插入(且数组未满),时间复杂度为常数,当数组已满或者在数组中间插入,需要将插入位置后面的元素分别后移,所以时间复杂度为O(n)。综上,时间复杂度为O(n)。
// 在制定位置插入
public void add(int n, T t) {
if (n > length || n < 0) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
if (length == data.length) { // 扩展数组,这里选择扩展为2倍
Object[] obj = new Object[data.length * 2];
for (int i = 0; i < data.length; i++) {
obj[i] = data[i];
}
data = obj;
}
for (int i = length; i > n; i--) { // 中间插入,后面的项往后移动
data[i] = data[i - 1];
}
data[n] = t;
length++;
}
// 插入一个元素,默认在在数组项的最后面插入
public boolean add(T t) {
add(length, t);
return true;
}
查找: 按位查找, 直接获取指定位置的元素,时间复杂度为 常数。
// 按照位置查找元素
@SuppressWarnings("unchecked")
public T get(int i) {
if (i < 0 && i > length) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
return (T) data[i - 1];
}
按值查找,并返回元素位置,时间复杂度为 O(n)。
// 获取元素所在的位置
public int indexOf(T t) {
for (int i = 0; i < length; i++) {
if (data[i].equals(t)) {
return i + 1;
}
}
return 0;
}
删除: 由于需要保持数据在存储空间的物理位置的连续性,若删除的为最后一个元素,时间复杂度为常数,否则删除一个元素后需要将删除位置后面的所有元素前移一位,所以时间复杂度为O(n)。
// 删除元素
public boolean remove(T t) {
try {
int i = this.indexOf(t);
remove(i);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
return false;
}
}
// 已知元素的位置,删除元素
public Object remove(int n) {
if (n >= length || n < 0) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
Object o = data[n - 1];
for (int i = n - 1; i < length - 1; i++) {
data[i] = data[i + 1];
}
length--;
return o;
}
顺序表的 判空、获取大小、遍历。 判空、获取大小时间复杂度都为常数,遍历为O(n)
// 判断是否为空
public boolean isEmpty() {
return length == 0;
}
// 获取线性表的大小
public int size(){
return length;
}
// 访问整个顺序
public void print(){
for(int i = 0; i < length; i++){
System.out.print(data[i] + " ");
}
}
由上面可知,顺序表中采用数组存储元素,在顺序表中插入或删除元素,即在数组中间中间插入或删除元素,需要付出很高的代价,插入或删除元素后相关的元素都必须作相应的移动,然而在线性表中查找就相对容易,制定位置后即可通过数组的下标找到元素。在接下来的链表中我们可以看到链表中插入删除非常容易,而查找就必须遍历链表才能返回结果。
全部代码以及测试
package org.TT.myArrayList;
/**
* @author Administrator
*
* @param <T>
*/
public class MyArrayList<T> {
private Object data[];
private int length;
// 不制定线性表默认大小为10
public MyArrayList() {
this(10);
}
// 创建制定大小的线性表
public MyArrayList(int n) {
data = new Object[n];
length = 0;
}
// 传入数组创建线性表
public MyArrayList(T a[]) {
this.data = new Object[a.length];
for (int i = 0; i < a.length; i++) {
data[i] = a[i];
}
length = a.length;
}
// 在制定位置插入
public void add(int n, T t) {
if (n > length || n < 0) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
if (length == data.length) { // 扩展数组,这里选择扩展为2倍
Object[] obj = new Object[data.length * 2];
for (int i = 0; i < data.length; i++) {
obj[i] = data[i];
}
data = obj;
}
for (int i = length; i > n; i--) { // 中间插入,后面的项往后移动
data[i] = data[i - 1];
}
data[n] = t;
length++;
}
// 插入一个元素,默认在在数组项的最后面插入
public boolean add(T t) {
add(length, t);
return true;
}
// 按照位置查找元素
@SuppressWarnings("unchecked")
public T get(int i) {
if (i < 0 && i > length) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
return (T) data[i - 1];
}
// 获取元素所在的位置
public int indexOf(T t) {
for (int i = 0; i < length; i++) {
if (data[i].equals(t)) {
return i + 1;
}
}
return 0;
}
// 删除元素
public boolean remove(T t) {
try {
int i = this.indexOf(t);
remove(i);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
return false;
}
}
// 已知元素的位置,删除元素
public Object remove(int n) {
if (n >= length || n < 0) {
throw new ArrayIndexOutOfBoundsException("参数不合法");
}
Object o = data[n - 1];
for (int i = n - 1; i < length - 1; i++) {
data[i] = data[i + 1];
}
length--;
return o;
}
// 判断是否为空
public boolean isEmpty() {
return length == 0;
}
// 获取线性表的大小
public int size(){
return length;
}
// 访问整个顺序
public void print(){
for(int i = 0; i < length; i++){
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
String a[] = { "a", "b", "c", "d", "e", "f", "g"};
MyArrayList<String> list = new MyArrayList<>(a);
list.print();
list.add(3,"TT");
list.add("h");
System.out.println();
System.out.println("在第三个元素后面添加:0 ,在最后面添加:20 ");
System.out.print("添加后: ");
list.print();
list.remove(1);
list.remove("g");
System.out.println();
System.out.println("删除第二个元素: b , 删除指定元素: g");
System.out.print("删除后:");
list.print();
System.out.println();
System.out.println("顺序表是否为空: " + list.isEmpty());
System.out.println("顺序表的大小: " + list.size());
}
}
测试结果