一、线性结构:
如果一个数据元素序列满足:
(1)除第一个和最后一个数据元素外,每个数据元素只有一个前驱数据元素和一个后继数据元素;
(2)第一个数据元素没有前驱数据元素;
(3)最后一个数据元素没有后继数据元素。
则称这样的数据结构为线性结构。
二、线性表抽象数据类型:
1、线性表抽象数据类型的概念:
线性表抽象数据类型主要包括两个方面:既数据集合和该数据集合上的操作集合。
数据集合:
可以表示为a0,a1,a2,...an-1,每个数据元素的数据类型可以是任意的类型。
操作集合包括如下:
1.插入
2.查找
3.删除
4.判断是否为空
5.求元素个数
2、设计线性表抽象数据类型的Java接口:
代码如下:
1 package com.myutil.list;
2
3 public interface List {
4 //插入元素
5 public void insert(int index,Object obj) throws Exception;
6 //重载插入方法
7 public void insert(Object obj) throws Exception;
8 //获取指定位置的元素
9 public Object get(int index) throws Exception;
10 //删除元素
11 public void delete(int index) throws Exception;
12 //获得线性表长度
13 public int size();
14 //判断线性表是否为空
15 public boolean isEmpty();
16 }
然后我们让子类去实现这个接口就行了。
三、顺序表:(在物理存储结构上连续,大小固定)
1、顺序表的概念:
计算机有两种基本的存储结构(物理存储结构):顺序结构、离散结构。使用顺序结构实现的线性表称为顺序表。如下图所示:
Java内存中,栈内存和堆内存占了很大一部分空间:栈内存的存储是顺序结构,堆内存的存储是离散结构。
2、设计顺序表类:
我们在上面第二段的List接口基础之上,设计一个顺序表:
(1)List.java:(线性表,和上面的第二段中代码一样)
1 package com.myutil.list;
2
3 public interface List {
4 //插入元素
5 public void insert(int index,Object obj) throws Exception;
6 //重载插入方法
7 public void insert(Object obj) throws Exception;
8 //获取指定位置的元素
9 public Object get(int index) throws Exception;
10 //删除元素
11 public void delete(int index) throws Exception;
12 //获得线性表长度
13 public int size();
14 //判断线性表是否为空
15 public boolean isEmpty();
16 }
(2)SequentailList.java:(核心代码)
1 package com.myutil.list;
2
3 //SequentialList:顺序表
4
5 public class SequentialList implements List {
6
7 //默认的顺序表的最大长度
8 private final int defaultSize = 10;
9 //最大长度
10 private int maxSize;
11 //当前长度
12 private int size;
13 //对象数组
14 Object[] listArray;
15
16
17 public SequentialList() {
18 init(defaultSize);
19 }
20
21 public SequentialList(int size) {
22 init(size);
23 }
24
25 //顺序表的初始化方法(建立顺序表)
26 private void init(int size) {
27 maxSize = size;
28 this.size = 0;
29 listArray = new Object[size];
30 }
31
32 @Override
33 public void insert(int index, Object obj) throws Exception {
34 //如果当前线性表已满,那就不允许插入数据
35 if (size == maxSize) {
36 throw new Exception("顺序表已满,无法插入!");
37 }
38 //插入位置编号是否合法
39 if (index < 0 || index > size) {
40 throw new Exception("参数错误!");
41 }
42 //移动元素
43 for (int j = size - 1; j >= index; j--) {
44 listArray[j + 1] = listArray[j];
45 }
46
47 listArray[index] = obj; //不管当前线性表的size是否为零,这句话都能正常执行,即都能正常插入
48 size++;
49 }
50
51 @Override
52 public void insert(Object obj) throws Exception {
53 insert(size, obj);
54 }
55
56 @Override
57 public Object get(int index) throws Exception {
58 if (index < 0 || index >= size) {
59 throw new Exception("参数错误!");
60 }
61 return listArray[index];
62 }
63
64 @Override
65 public void delete(int index) throws Exception {
66 if (isEmpty()) {
67 throw new Exception("顺序表为空,无法删除!");
68 }
69 if (index < 0 || index > size - 1) {
70 throw new Exception("参数错误!");
71 }
72 //移动元素
73 for (int j = index; j < size - 1; j++) {
74 listArray[j] = listArray[j + 1];
75 }
76 size--;
77 }
78
79 @Override
80 public int size() {
81 return size;
82 }
83
84
85 @Override
86 public boolean isEmpty() {
87 return size == 0;
88 }
89 }
我们来看一下第54行的插入操作insert()方法:如果需要在index位置插入一个数据,那么index后面的元素就要整体往后移动一位。这里面需要特别注意的是:
插入操作:移动元素时,要从后往前操作,不能从前往后操作,不然元素会被覆盖的。
删除元素:移动元素时,要从前往后操作。
(3)测试类:
1 package com.myutil.list;
2
3 public class Test {
4
5 public static void main(String[] args) {
6
7 SequentialList list = new SequentialList(20);
8
9 try {
10 list.insert(0, 100);
11 list.insert(0, 50);
12 list.insert(1, 20);
13 list.insert(60);
14
15 for (int i = 0; i < list.size(); i++) {
16 System.out.println("第" + i + "个数为" + list.get(i));
17 }
18
19 } catch (Exception e) {
20 e.printStackTrace();
21 }
22 }
23 }
我们要注意插入的规则是什么,不然会觉得这个顺序表打印输出的顺序很奇怪。
运行效果:
第0个数为50
第1个数为20
第2个数为100
第3个数为60
3、顺序表效率分析:
- 顺序表插入和删除一个元素的时间复杂度为O(n)。
- 顺序表支持随机访问,顺序表读取一个元素的时间复杂度为O(1)。因为我们是可以通过下标直接访问的,所以时间复杂度是固定的,和问题规模无关。
4、顺序表的优缺点:
- 顺序表的优点是:支持随机访问;空间利用率高(连续分配,不存在空间浪费)。
- 顺序表的缺点是:大小固定(一开始就要固定顺序表的最大长度);插入和删除元素需要移动大量的数据。
5、顺序表的应用:
设计一个顺序表,可以保存100个学生的资料,保存以下三个学生的资料,并打印输出。
代码实现:
(1)List.java:
和上面的代码保持不变
(2)SequentailList.java:
和上面的代码保持不变
(3)Students.java:学生类
1 package com.myutil.list.use;
2
3 //学生类
4 public class Students {
5
6 private String id;// 学号
7 private String name;// 姓名
8 private String gender;// 性别
9 private int age;// 年龄
10
11 public Students() {
12
13 }
14
15 public Students(String sid, String name, String gender, int age) {
16 this.id = sid;
17 this.name = name;
18 this.gender = gender;
19 this.age = age;
20 }
21
22
23 public String getId() {
24 return id;
25 }
26
27 public void setId(String id) {
28 this.id = id;
29 }
30
31 public String getName() {
32 return name;
33 }
34
35 public void setName(String name) {
36 this.name = name;
37 }
38
39 public String getGender() {
40 return gender;
41 }
42
43 public void setGender(String gender) {
44 this.gender = gender;
45 }
46
47 public int getAge() {
48 return age;
49 }
50
51 public void setAge(int age) {
52 this.age = age;
53 }
54
55 public String toString() {
56 return "学号:" + this.getId() + " 姓名:" + this.getName() + " 性别:" + this.getGender() + " 年龄:" + this.getAge();
57 }
58
59 }
(4)Test.java:
1 package com.myutil.list.use;
2
3 import com.myutil.list.SequentialList;
4
5 public class Test {
6
7 /**
8 * @param args
9 */
10 public static void main(String[] args) {
11 SequentialList list = new SequentialList(100);
12
13 try {
14 list.insert(list.size(), new Students("S0001", "张三", "男", 18)); //第一个参数list.size代表的是:我每次都是在顺序表的最后一个位置(当前线性表的长度的位置)进行插入操作。这一行里,size是等于0
15 list.insert(new Students("S0002", "李四", "男", 19));
16 list.insert(list.size(), new Students("S0003", "王五", "女", 21));
17 list.insert(new Students("S0004","赵六","女",20));
18
19 for (int i = 0; i < list.size(); i++) {
20 System.out.println(list.get(i));
21 }
22
23 } catch (Exception ex) {
24 ex.printStackTrace();
25 }
26 }
27
28 }
注意第11行的注释:第一个参数list.size代表的是:我每次都是在顺序表的最后一个位置(当前线性表的长度的位置)进行插入操作;这样的话,遍历时才是按照张三、李四、王五的顺序进行输出的。
运行效果:
学号:S0001 姓名:张三 性别:男 年龄:18
学号:S0002 姓名:李四 性别:男 年龄:19
学号:S0003 姓名:王五 性别:女 年龄:21
学号:S0004 姓名:赵六 性别:女 年龄:20
本文参考博客:,并加入自己的一点改动,后续还会有优化改动,例如加入泛型等。。。。。。