avatar

数据结构(java实现)之顺序表和单链表

简介:

本文介绍了数据结构中线性表的两种实现方式:线性表和链表

  • 顺序表就是在内存中按顺序连续开辟一段空间来存储数据的结构,在java中就是数组,如a所示
  • 链表就是在内存中随机开辟内存一段段存储数据的结构,如图b所示

在这里插入图片描述

线性表的接口

使用一个接口表明基本操作的需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Ilist {
//清空线性表
public void clear();
//判断线性表是否为空
public boolean isEmpty();
//获取线性表的长度
public int length();
//获取线性表中的第i个值
public Object get(int i) throws Exception;
//往线性表中第i个位置插入一个值
public void insert(int i,Object x) throws Exception;
//移除掉线性表中的第i个值
public void remove(int i) throws Exception;
//找到线性表中第一次出现x的索引
public int indexOf(Object x);
//打印整个线性表
public void display();
}

顺序表基本操作代码实现

使用顺序表来实现Ilist接口,重写接口中所有的抽象类方法,注释就能看懂大部分,这里需要注意的是插入和删除两个操作,下面图解:

插入操作

从下图可以看出,想要在i-1出插入x,那么就要将该位置后面所有的元素向后移动,并且应该从an开始移动,再a(n-1),最后把ai从i-1移动到i位置,再插入x

在这里插入图片描述

删除操作

删除某个地方的元素后,该位置就会空出来,那么我们只需要把后面的元素依次往前移动补上去就行

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class SqList implements Ilist{
//对象数组,存放数据
private Object[] listElem;
//用于记录当前顺序表的长度
private int curLen;

//构造函数初始化,置长度为0,开辟一个maxSize大小的数组
public SqList(int maxSize) {
curLen = 0;
listElem = new Object[maxSize];
}

@Override
public void clear() {
//直接置当前数组长度为0即可,不用释放,下次用这一块内存会直接覆盖
curLen = 0;
}

@Override
public boolean isEmpty() {
return curLen == 0;
}

@Override
public int length() {
return curLen;
}

@Override
public Object get(int i) throws Exception{
if(i < 0 || i >= curLen){
throw new Exception("索引越界");
}
return listElem[i];
}

@Override
public void insert (int i, Object x) throws Exception{
if(curLen == listElem.length){
throw new Exception("数组满了,无法插入新元素");
}
//注意比较这里i > curLen,上面的get()方法是i >= curLen,因为这里是插入数据,可以在最后插入,因此可以等于curLen
if(i < 0 || i > curLen){
throw new ArrayIndexOutOfBoundsException("索引越界");
}
//要想在某个地方插入,必须先把该位置后面的数组往后移动一下,把该位置空出来才能插入数据
for (int j = curLen; j > i ; j--) {
listElem[j] = listElem[j-1];
}
listElem[i] = x;
curLen++;
}

@Override
public void remove(int i) throws Exception{
if(i < 0 || i >= curLen){
throw new Exception("索引错误");
}
//从i后面的数据一次往前补上来就行,注意这里j<curLen-1
for (int j = i; j < curLen-1; j++) {
listElem[i] = listElem[i+1];
}
curLen--;
}

@Override
public int indexOf(Object x) {
for (int i = 0; i < curLen; i++) {
if (listElem == x){
return i;
}
}
return -1;
}

@Override
public void display() {
for (int i = 0; i < curLen; i++) {
System.out.print(listElem[i]+"");
}
System.out.println();
}
}

单链表基本操作代码实现

链表需要先定义一个节点类Node用来存放数据以及指向下一个节点的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Node {
public Object data;
Node next;

public Node() {
//空参构造函数默认调用的是第三个构造函数,创建值域和指针域都为空的头节点
this(null,null);
}

public Node(Object data) {

this(data,null);
}

public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
}

这里链表的插入和删除操作需要单独进行图解:

插入

先使p指向head节点,往后移动到i-1的位置,第一步使得q.next指向p.next.next,再把p.next连接到q上。注意,这里顺序不能反了,如果反了,链表后半部分就找不到了

在这里插入图片描述

删除

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import java.util.Scanner;

public class LinkList implements Ilist{
//这里书上并没有定义一个记录长度的curLen,书上的链表的长度是从head遍历计算的,目前还没有想清楚
private int curLen;
public Node head;

public LinkList() {

curLen = 0;
head = new Node();
}

public LinkList(int n, boolean order) throws Exception {
//order为0时尾插,为1时头插
this();
System.out.println("请输入"+n+"个字符串,以回车分割");
if(!order){
creatTail(n);
}
else{
creatHead(n);
}
}

@Override
public void clear() {
head.data = null;
head.next = null;
curLen = 0;
}

@Override
public boolean isEmpty() {
return head.next == null;
}

@Override
public int length() {
return curLen;
}

@Override
public Object get(int i) throws Exception {
if (i < 0 || i >= curLen){
throw new Exception("索引错误");
}
Node p = head.next;
for (int j = 0; j < i; j++) {
p = p.next;
}
return p.data;
}

@Override
public void insert(int i, Object x) throws Exception {
if (i > curLen){
throw new Exception("索引错误");
}
Node p = head;
Node q = new Node(x);
//j设置为-1是因为p指向的是head,当i为0时他的前驱就是-1
//我想要设置头节点就是因为这个原因了,便于插入i=0的元素
for (int j = -1; j < i - 1; j++) {
p = p.next;
}
q.next = p.next;
p.next = q;
curLen++;
}

@Override
public void remove(int i) throws Exception {
if (i < 0 || i >= curLen){
throw new Exception("索引错误");
}
Node p = head.next;
for (int j = 0; j < i - 1; j++) {
p = p.next;
}
p.next = p.next.next;
curLen--;
}

@Override
public int indexOf(Object x) {
Node p = head.next;
int j = 0;
if (p != null){
if (p.data == x)
return j;
j++;
p = p.next;
}
return -1;
}

@Override
public void display() {
Node p = head.next;
while (p != null){
System.out.print(p.data+" ");
p = p.next;
}
System.out.println();
}

public void creatHead(int n) throws Exception {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
insert(0,sc.next());
}
}

public void creatTail(int n) throws Exception {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
insert(length(),sc.next());
}
}

}

链表和数组优缺点比较

  • 数组实现比较简单,查找效率高,插入和删除效率低,需要预分配空间,一旦分配尺寸就固定了

  • 链表比较灵活,插入和删除的效率比较高,可以动态分配空间,但是查找效率低

  • 所以红黑树就结合了两者的特点,使得查找效率十分高效,后面再学习红黑树

文章作者: Dxwell
文章链接: https://dxwell886.github.io/2020/07/16/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-java%E5%AE%9E%E7%8E%B0-%E4%B9%8B%E9%A1%BA%E5%BA%8F%E8%A1%A8%E5%92%8C%E5%8D%95%E9%93%BE%E8%A1%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dxwell的博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论