依旧先祭出这张图吧,链表也是一个线性表的结构,之前说过数组那就对比数组来说一下链表,有利于自己记。
1.首先说一下底层的存储结构,我们知道数组在内存中占用的是连续的空间,而链表就完全不需要只要还有内存,就能创建,并不需要是连续的空间。
举个栗子,比如内存中有150MB的空间,如果你要申请一个100MB的数组,但是在内存中是两块独立的75MB的空间碎片,那么数组肯定是创不成功的,因为数组需要连续的空间,而链表就可以创建成功,因为链表创建的空间不需要是连续的,是通过指针将一个个零散的内存块连接起来的。当然这里并不是说100MB链表存储的数据就比数组的多,正好相反,因为链表中每个结点至少需要存储下一个节点的地址,所以比数组占用的内存空间其实更大,相同数据类型,相同大小的内存空间其实数组比链表存储的数据要多。
2.说说链表是如何查找的
数组像是正规的军队,通过报数一个挨着一个,可以通过下标直接找到对应位置的数据,链表呢,就像抗战时期的地下党,每个节点知道的只有自己的上下级,你不可能知道你上级的上级是谁,除非你的上级牺牲了。
链表是存储数据的同时留出了一个存储地址的位置
单向链表:next中存放的就是下一个节点的地址
双向链表:prev中存放的是上一个节点的地址,next中依旧存放的是下一个节点的地址
相比于单向链表,双向链表的整体查找速度会加快,因为查找前面临近节点可以迅速查询,但是所谓能量守恒,内存的开销会增大,毕竟多了一个存放前一个节点的空间。
循环链表:之前的尾节点next存储的是空,而循环链表尾节点next节点指向的地址,是首节点的地址。
3.链表的增删查
1.首先说一下链表的查询,如果是要查询某个具体的值,无论是什么类型的链表都得从头几点一个个往下查找直到找到该节点,时间复杂度是O(n)
2.单纯的增加一个节点,当你知道你要给哪个位置插入时,时间复杂度是O(1)
下边放一下代码
// 头节点指针
2. private Node head;
3. // 尾节点指针
4. private Node last;
5. // 链表实际长度
6. private int size;
7.
8. /**
9. * 链表插入元素
10. * @param data 插入元素
11. * @param index 插入位置
12. */
13. public void insert(int data, int index) throws Exception {
14. if (index<0 || index>size) {
15. throw new IndexOutOfBoundsException(" 超出链表节点
范围!");
16. }
17. Node insertedNode = new Node(data);18. if(size == 0){
19. //空链表
20. head = insertedNode;
21. last = insertedNode;
22. } else if(index == 0){
23. //插入头部
24. insertedNode.next = head;
25. head = insertedNode;
26. }else if(size == index){
27. //插入尾部
28. last.next = insertedNode;
29. last = insertedNode;
30. }else {
31. //插入中间
32. Node prevNode = get(index-1);
33. insertedNode.next = prevNode.next;
34. prevNode.next = insertedNode;
35. }
36. size++;
37. }
38.
39. /**
40. * 链表删除元素
41. * @param index 删除的位置
42. */
43. public Node remove(int index) throws Exception {
44. if (index<0 || index>=size) {
45. throw new IndexOutOfBoundsException(" 超出链表节点
范围!");
46. }
47. Node removedNode = null;
48. if(index == 0){
49. //删除头节点
50. removedNode = head;
51. head = head.next;
52. }else if(index == size-1){
53. //删除尾节点
54. Node prevNode = get(index-1);
55. removedNode = prevNode.next;
56. prevNode.next = null;
57. last = prevNode;
58. }else {
59. //删除中间节点
60. Node prevNode = get(index-1);
61. Node nextNode = prevNode.next.next;
62. removedNode = prevNode.next;
63. prevNode.next = nextNode;
64. }65. size--;
66. return removedNode;
67. }
68.
69. /**
70. * 链表查找元素
71. * @param index 查找的位置
72. */
73. public Node get(int index) throws Exception {
74. if (index<0 || index>=size) {
75. throw new IndexOutOfBoundsException(" 超出链表节点
范围!");
76. }
77. Node temp = head;
78. for(int i=0; i<index; i++){
79. temp = temp.next;
80. }
81. return temp;
82. }
83.
84. /**
85. * 输出链表
86. */
87. public void output(){
88. Node temp = head;
89. while (temp!=null) {
90. System.out.println(temp.data);
91. temp = temp.next;
92. }
93. }
94.
95. /**
96. * 链表节点
97. */
98. private static class Node {
99. int data;
100. Node next;
101. Node(int data) {
102. this.data = data;
103. }
104.}
105.
106.public static void main(String[] args) throws Exception {
107. MyLinkedList myLinkedList = new MyLinkedList();
108. myLinkedList.insert(3,0);
109. myLinkedList.insert(7,1);
110. myLinkedList.insert(9,2);
111. myLinkedList.insert(5,3);112. myLinkedList.insert(6,1);
113. myLinkedList.remove(0);
114. myLinkedList.output();
115.}