【数据结构】顺序表和链表重点知识汇总(附有代码)

简介: 【数据结构】顺序表和链表重点知识汇总(附有代码)

思维导图

 

1.List的介绍和使用:

  1. List是一个接口不能直接实例化,List继承于Collection。
  2. List是一个线性表,是有相同类型元素的有限序列。
  3. ArrayList和LinkedList都实现了List接口

//只能访问 List 当中的方法

List<Integer> list1 = new ArrayList<>();

List<Integer> list2 = new LinkedList<>();

List<Integer> list3 = new Stack<>();


//可以访问接口中的方法,也可以访问自己类中的方法

ArrayList<Integer> list4 = new ArrayList<>();

LinkedList<Integer> list5 = new LinkedList<>();

Stack<Integer> list6 = new Stack<>();

2.线性表:

  1. 线性表是有多个相同特性元素的有限序列,在数据结构中广泛使用。常见的线性表有数组、顺序表、链表、栈和队列等
  2. 线性表的结构特点。从逻辑结构和物理结构上来说,在逻辑上是线性结构,类似于一条直线。而在物理上也可以称为内存上,确是不一定连续的,比如连续的有顺序表,链式结构的有链表。
  3. 线性表的存储方式有顺序存储和链式存储

3.ArrayList:

  1. ArrayList是一个类,实现了List接口。
  2. ArrayList是一个动态类型的顺序表,体现在增加元素时会自动发生扩容
  3. ArrayList底层是一块连续的内存空间,就是采用数组的存储形式,其背后就是一个数组
  4. 如果创建了一个ArrayList但是没有指定容量的话,这个顺序表默认的大小为0,当add ();元素时ArraysList就会扩容到10,以后每次按照1.5倍扩容。例如:原来大小是10,扩容后变成15。
  5. 检测到要扩容时会调用grow方法,源码中是调用copyOf进行扩容
  6. ArrayList有3种创建方法
3种创建方法 说明
List<Integer> list1 = new ArrayList<>(); 推荐写法
List<Integer> list2 = new ArrayList<>(66); 可以指定顺序表的容量
List<Integer> list3 = new ArrayList<>(list2); List3中的元素和list2一样

  1. ArrayList的模拟实现
1. public class MyArrayList {
2. 
3. public int[] elem;
4. public int usedSize;//当前顺序表中元素的个数
5. private static final int DEFAULT_CAPACITY = 10;//给定一个默认的容量
6. 
7. public MyArrayList(){
8.         elem = new int[DEFAULT_CAPACITY];
9.     }
10. 
11. //打印顺序表
12. public void diaplay(){
13. for (int i = 0; i < usedSize; i++) {
14.             System.out.print(elem[i]+" ");
15.         }
16.         System.out.println();
17.     }
18. //判断顺序表有没有满
19. private boolean isFull(){
20. return usedSize == elem.length;
21.     }
22. 
23. //增加元素时检查下标是否合法
24. private void checkPos1(int pos) {
25. if(pos < 0 || pos > usedSize){
26. return;
27.         }
28.     }
29. 
30. //修改查找元素时检查下标是否合法
31. private void checkPos2(int pos){
32. if(pos<0 || pos >= usedSize){
33. return;
34.         }
35.     }
36. //增加元素(尾插)
37. public void add(int data){
38. if(isFull()){
39.             elem = Arrays.copyOf(elem,elem.length*2);
40.         }
41.         elem[usedSize] = data;
42.         usedSize++;
43.     }
44. 
45. //在pos位置上增加元素
46. public void add(int pos,int data){
47.         checkPos1(pos);
48. if(isFull()){
49.             elem = Arrays.copyOf(elem,elem.length*2);
50.         }
51. //注意是 i>=pos
52. for (int i = usedSize-1; i >= pos; i--) {
53.             elem[i+1] = elem[i];
54.         }
55.         elem[pos] = data;
56.         usedSize++;
57.     }
58. 
59. //判断是否包含某个元素
60. public boolean contains(int data){
61. //i最多会走到usedSize的前一个元素,所以usedSize不用-1
62. for (int i = 0; i < usedSize; i++) {
63. if(data == elem[i]){
64. return true;
65.             }
66.         }
67. return false;
68.     }
69. 
70. //找到某个元素的下标
71. public int indexOf(int data){
72. for (int i = 0; i < usedSize; i++) {
73. if(data == elem[i]){
74. return i;
75.             }
76.         }
77. return -1;
78.     }
79. 
80. //获取pos下标的元素
81. public int get(int pos){
82.         checkPos2(pos);
83. return elem[pos];
84.     }
85. 
86. //将pos位置的值改成value
87. public void set(int pos,int value){
88.         checkPos2(pos);
89.         elem[pos] = value;
90.     }
91. 
92. //删除第一次出现的关键字key
93. public void remove(int key){
94. int index = indexOf(key);
95. if(index == -1){
96.             System.out.println("找不到这个数字");
97.         }
98.         checkPos2(index);
99. //这里注意一下:一定要-1
100. for (int i = index; i < usedSize-1; i++) {
101.             elem[i] = elem[i+1];
102.         }
103.         usedSize--;
104.     }
105. 
106. //获取顺序表的长度
107. public int size(){
108. /*int count = 0;
109.         for (int i = 0; i < usedSize; i++) {
110.             count++;
111.         }
112.         return count;*/
113. return usedSize;
114.     }
115. 
116. //清空顺序表
117. public void clear(){
118. /*for (int i = 0; i < usedSize; i++) {
119.             elem[i]=null;
120.         }*/
121.         usedSize = 0;
122.     }
123. 
124. }

4.ArrayList的常见操作:

  1. 增加元素
增加元素 说明
add(x); 尾插元素x
add(index,x); 将元素x插入到下标为index的位置
addAll(c); 将c中的元素插入到顺序表的末尾

  1. 删除元素
删除元素 说明
remove(index); 删除index处的下标
remove(x); 删除第一个元素为x
clear(); 清空顺序表

  1. 查找元素
查找元素 说明
get(index); 获取下标为index的元素
contains(x); 判断元素x是否在顺序表中
indexOf(x); 返回第一个元素为x的下标
lastIndexOf(x); 返回最后一个元素为x的下标

  1. 修改元素
修改元素 说明
set(index,x); 将下标为index的元素改为x
subList(first,last); 截取下标从first到last的部分顺序表

  1. 对subList();截取的部分的值修改,也会影响到原顺序表的值。因为subList();截取后返回的是该起始点的地址。

5.ArrayList的遍历:

  1. 有4种遍历方法//直接打印
System.out.println(list);
//for循环+下标
for( int i = 0; i<list.size() ; i++ ) {
    System.out,println( list.get(i) );
}
//foreach实现
for( Integer integer : list ) {
    System.out, println(integer + " ");
}
//使用迭代器1
Iterator<Integer> it = list.listIterator();
while (it.hasNext()) {
    System.out.println(it.next() + " ");
}
//使用迭代器2
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
    System.out.println(it.next());
}
  1. 只要实现了Iterable接口的,就可以使用迭代器来打印。

6.顺序表的缺陷:

  1. 增删查改,使用顺序表进行数据的插入、查找和删除其时间复杂度都是O(n),效率太低!例如:插入和删除某个元素,需要移动其他元素。
  2. 顺序表的开辟需要一块连续的空间,空间利用率较低
  3. 扩容问题,扩容一般是1.5倍增长,容易造成一定空间的浪费。例如:当前容量100,扩容到了150,但是我只存了9个元素,剩下的41个数据空间就浪费了......

(下面是链表部分)

7.链表的结构:

  1. 物理结构上看(内存上),链表是非连续的结构;从逻辑结构上看链表又是连续的。
  2. 链表分为单向、双向、带头、不带头、循环和非循环。
  3. 组合起来一共就有8种链表结构。
  4. 最难掌握也是最重要的是单向不带头非循环链表

8.单链表

  1. 单链表的结构特点,单向链表存放当前元素值val和下一个元素的地址next,最后一个元素的next为null。
  2. 无头单向非循环链表的模拟实现
1. public class MySingleList {
2. 
3. static class ListNode{
4. public int val;
5. public ListNode next;
6. 
7. public ListNode(int val) {
8. this.val = val;
9.         }
10.     }
11. public ListNode head;
12. 
13. //头插法
14. public void addFirst(int data){
15. ListNode node = new ListNode(data);
16.         node.next = head;
17.         head = node;
18.     }
19. 
20. //尾插法
21. public void addLast(int data){
22. ListNode node = new ListNode(data);
23. if(head == null){
24.             head = node;
25.         }else{
26. ListNode cur = head;
27. while(cur.next != null){
28.                 cur = cur.next;
29.             }
30.             cur.next = node;
31.         }
32.     }
33. 
34. //打印链表
35. public void display(){
36. ListNode cur = head;
37. while(cur != null){
38.             System.out.print(cur.val+" ");
39.             cur = cur.next;
40.         }
41.         System.out.println();
42.     }
43. 
44. //从指定节点打印
45. public void display(ListNode newHead){
46. ListNode cur = newHead;
47. while(cur != null){
48.             System.out.print(cur.val+" ");
49.             cur = cur.next;
50.         }
51.         System.out.println();
52.     }
53. 
54. //查找元素key是否在单链表中
55. public boolean contains(int key){
56. ListNode cur = head;
57. while(cur != null){
58. if(cur.val == key){
59. return true;
60.             }
61.             cur = cur.next;
62.         }
63. return false;
64.     }
65. 
66. //得到链表的长度
67. public int size(){
68. ListNode cur = head;
69. int count = 0;
70. while(cur != null){
71.             count++;
72.             cur = cur.next;
73.         }
74. return count;
75.     }
76. 
77. //插入元素时检查下标合法性
78. private void checkIndex(int index) {
79. if(index < 0 || index > size()){
80. return;
81.         }
82.     }
83. 
84. //任意位置插入元素,首元素若为0下标
85. public void addIndex(int index,int data){
86.         checkIndex(index);
87. if(index == 0){
88.             addFirst(data);
89. return;
90.         } else if (index == size()) {
91.             addLast(data);
92. return;
93.         }else {
94. ListNode node = new ListNode(data);
95. ListNode cur = head;
96. while (index - 1 != 0) {
97.                 cur = cur.next;
98.                 index--;
99.             }
100.             node.next = cur.next;
101.             cur.next = node;
102.         }
103. 
104.     }
105. 
106. //删除第一次出现的元素key
107. public void remove(int key){
108. if(head.val == key){
109.             head = head.next;
110. return;
111.         }
112. ListNode cur = head;
113. //循环中时cur.next,如果是cur的话会空指针异常
114. while(cur.next != null){
115. //这里处理不了头节点
116. if(cur.next.val == key){
117.                 cur.next = cur.next.next;
118. return;
119.             }
120.             cur = cur.next;
121.         }
122.     }
123. 
124. //删除所有值为key的元素。只遍历了一遍!!
125. public void removeAll(int key){
126. if(head == null){
127. return;
128.         }
129. ListNode cur = head;
130. ListNode prev = head;
131. 
132. while(cur != null){
133. if(cur.val == key){
134.                 prev.next = cur.next;
135.                 cur = cur.next;
136.             }else{
137.                 prev = cur;
138.                 cur = cur.next;
139.             }
140.         }
141. if(head.val == key){
142.             head = head.next;
143.         }
144.     }
145. 
146. //清空链表
147. public void clear(){
148. ListNode cur = head;
149. while(cur != null){
150. ListNode curNext = cur.next;
151.             cur.next = null;
152.             cur = curNext;
153.         }
154.         head = null;
155.     }
156. 
157. }

9.LinkedList:

  1. LinkedList底层是一个双向链表,可以当作栈和队列来使用。
  2. LinkedList的特点,LinkedList没有实现RandomAccess接口因此不支持随机访问,而顺序表ArrayList实现了接口因此支持随机访问

10.LinkedList常见的操作方法:

  1. 增加元素
增加元素 说明
add(x); 尾插元素 x
add(index,x); 将x插入到 index下标
addAll(c); 将c中的元素全部尾插

  1. 删除元素
删除元素 说明
remove(index); 删除index下标元素
remove(x); 删除遇到的第一个x元素
clear(); 清空链表

  1. 查找元素
查找元素 说明
get(index); 获取index下标元素
contains(x); 判断链表中是否包含x
indexOf(x); 返回遇到的第一个x元素下标
lastIndexOf(x); 返回最后一个值为x的元素的下标

  1. 修改元素
修改元素 说明
set(index,x); 将index下标元素修改为x
subList(first,last); 截取first下标到last下标之间的元素

11.LinkedList的遍历:

  1. 有4种打印方法//直接打印
System.out.println(linkedlist);
//for循环+下标
for( int i = 0; i < linkedlist.size(); i++ ) {
    System.out,println( linkedlist.get(i) );
}
//foreach实现
for( Integer integer : linkedlist ) {
    System.out, println(integer + " ");
}
//使用迭代器
ListIterator<Integer> it = linkedlist.listIterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

12.ArrayList和LinkedList异同:

  1. 结构上,ArrayList在物理结构和逻辑结构上是连续的,而LinkedList在逻辑结构上连续,物理结构上不一定连续
  2. 因为ArrayList实现了RandomAccess接口所以支持随机访问,而LinkedList不可以。知道元素下标时ArrayList可以更快访问,时间复杂度为O(1);LinkedList查找访问元素时效率低,时间复杂度为O(n)。
  3. 修改、删除和插入元素时,ArrayList的效率很低,有一种“牵一发而动全身”的感觉,因此时间复杂度为O(n)。LinkedList在处理增删改的时候只需要改动部分的next即可,效率很高。
  4. ArrayList还有一个特点,他是动态类型的顺序表,其容量和扩容都是固定的操作,很浪费空间,空间利用率也不高。而LinkedList没有扩容一说,如果需要增加元素只需要插入并且修改next的地址即可,空间利用率很高。
  5. 如果需要频繁的查找访问元素,可以使用ArrayList来完成。如果需要大量的插入删除元素,那么使用LinkedList会更好


如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

相关文章
|
28天前
|
存储 编译器 C语言
【数据结构】C语言实现链队列(附完整运行代码)
【数据结构】C语言实现链队列(附完整运行代码)
36 0
|
2天前
|
存储 C语言
数据结构基础:双链表结构、实现
数据结构基础:双链表结构、实现
|
2天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
12天前
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
|
15天前
数据结构--链表刷题(一)快慢指针(上)
数据结构--链表刷题(一)快慢指针
16 0
|
24天前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
28天前
|
存储 编译器 C语言
【数据结构】深入浅出理解链表中二级指针的应用
【数据结构】深入浅出理解链表中二级指针的应用
28 0
|
19天前
|
消息中间件 存储 搜索推荐
深入理解栈和队列(二):队列
深入理解栈和队列(二):队列
34 0
|
1月前
【栈】数据结构栈的实现
【栈】数据结构栈的实现