当前位置:   article > 正文

java实现链表

java实现链表

1、什么是链表

链表是一种数据结构,和数组同级别。Java中我们使用的LInkedList,其实现原理为链表,而ArrayList其实现原理为数组。链表在插入和删除时优势明显。

单链表是一种线性表,实际上由节点(Node)组成,可以把单链表想象成为一列火车,而组成单链表的每个节点想象成为一节车厢。节点中存储了节点的值(data)以及指向下一个节点的指针(Next)。

2、单链表的实现(增删查改)

  1. package LinkedList;
  2. // 车厢类
  3. class Node {
  4. // 存储具体元素
  5. int data;
  6. // 存储下一个结点地址(钩子)
  7. Node next;
  8. public Node(int data) {
  9. this.data = data;
  10. }
  11. public Node(int data, Node next) {
  12. this.data = data;
  13. this.next = next;
  14. }
  15. }
  16. // 火车
  17. public class SingleLinkedList {
  18. // 当前火车中车厢的个数(实际存储的元素个数)
  19. private int size;
  20. // 当前火车的第一个结点
  21. private Node head;
  22. // 在火车头部添加元素
  23. public void addFirst(int data) {
  24. // 要使用addFirst向火车中添加结点,要判断当前火车是否为空
  25. // -> 一个结点都没有
  26. // 要插入的结点就是第一个结点
  27. if(size == 0) {
  28. Node node = new Node(data);
  29. head = node;
  30. size ++;
  31. }else {
  32. // 当前火车已经存在结点了
  33. Node node = new Node(data);
  34. node.next = head;
  35. head = node;
  36. size ++;
  37. }
  38. }
  39. public void addIndex(int index,int data) {
  40. // 一定注意边界条件
  41. // 判断index的合法性
  42. if (index < 0 || index > size) {
  43. System.err.println("add index illegal!");
  44. return;
  45. }
  46. // 若index = 0;
  47. if (index == 0) {
  48. // 链表的头部节点插入
  49. addFirst(data);
  50. return;
  51. }
  52. // 说明此时index合法且在中间位置(包含最后节点)
  53. // 此时需要找到待插入位置index的前驱节点
  54. // 单链表中只能从前到后遍历
  55. Node node = new Node(data);
  56. Node prev = head;
  57. for (int i = 0; i < index - 1; i++) {
  58. prev = prev.next;
  59. }
  60. // prev引用指向当前插入index的前驱
  61. node.next = prev.next;
  62. prev.next = node;
  63. size ++;
  64. }
  65. public void addLast(int data) {
  66. addIndex(size,data);
  67. }
  68. /**
  69. * 查询index位置的节点数据
  70. * @param index
  71. * @return 节点数据
  72. */
  73. public int get(int index) {
  74. if (rangeCheck(index)) {
  75. Node node = head;
  76. for (int i = 0;i < index;i++) {
  77. node = node.next;
  78. }
  79. // 此时node指向待查找元素的索引
  80. int data = node.data;
  81. return data;
  82. }else {
  83. System.err.println("get index illegal!");
  84. return -1;
  85. }
  86. }
  87. /**
  88. * 修改单链表中index位置的元素
  89. * 返回修改前的元素值
  90. * @param index
  91. * @param data
  92. * @return
  93. */
  94. public int set(int index,int data) {
  95. if (rangeCheck(index)) {
  96. // 需要找到index位置的节点
  97. Node node = head;
  98. for (int i = 0; i < index; i++) {
  99. node = node.next;
  100. }
  101. int oldData = node.data;
  102. node.data = data;
  103. return oldData;
  104. }else {
  105. System.err.println("set index illegal!");
  106. return -1;
  107. }
  108. }
  109. private boolean rangeCheck(int index) {
  110. if (index < 0 || index >= size) {
  111. return false;
  112. }
  113. return true;
  114. }
  115. /**
  116. * 判断单链表中是否包含元素data
  117. * @param data
  118. * @return
  119. */
  120. public boolean contains(int data) {
  121. Node node = head;
  122. while (node != null) {
  123. if (node.data == data) {
  124. System.out.println("找到元素!");
  125. return true;
  126. }
  127. node = node.next;
  128. }
  129. System.out.println("没有找到该元素");
  130. return false;
  131. }
  132. public void removeFirst() {
  133. Node node = head;
  134. head = head.next;
  135. node.next = null;
  136. size --;
  137. }
  138. public void removeIndex(int index) {
  139. // 判断边界条件
  140. if (rangeCheck(index)) {
  141. if (index == 0) {
  142. // 此时删除头节点
  143. removeFirst();
  144. }else {
  145. // 此时index删除的中间位置节点
  146. Node prev = head;
  147. for (int i = 0; i < index - 1; i++) {
  148. prev = prev.next;
  149. }
  150. // prev指向待删除节点的前驱节点
  151. // node就是待删除的节点
  152. Node node = prev.next;
  153. // 链接前驱节点和后继节点
  154. prev.next = node.next;
  155. // 将当前节点的next引用置为空,脱钩操作
  156. node.next = null;
  157. size --;
  158. }
  159. }else {
  160. System.err.println("remove index illegal!");
  161. }
  162. }
  163. /**
  164. * 删除链表中指定元素的第一个节点
  165. * @param data
  166. */
  167. public void removeValueOnce(int data) {
  168. // 先要找到待删除的元素的节点
  169. // 先判断头节点的情况
  170. if (head.data == data) {
  171. // 此时头节点就是第一个待删除的节点
  172. removeFirst();
  173. }else {
  174. // 此时头节点一定不是要删除的节点
  175. // 从头节点开始找到待删除的节点前驱
  176. Node prev = head;
  177. // 此时要看下一个节点的情况
  178. while (prev.next != null) {
  179. // 找到待删除的节点
  180. if (prev.next.data == data) {
  181. // 此时perv就是待删除节点的前驱
  182. // node节点就是待删除的节点
  183. Node node = prev.next;
  184. // 将prev跳过当前node
  185. prev.next = node.next;
  186. node.next = null;
  187. size --;
  188. break;
  189. }else {
  190. // 此时prev的下一个节点不是待删除的节点
  191. // 继续向下一个节点前进
  192. prev = prev.next;
  193. }
  194. }
  195. }
  196. }
  197. public void removeAllValue(int data) {
  198. // 判断头节点的情况
  199. // 头节点以及之后出现了多个连续待删除的节点
  200. while (head != null && head.data == data) {
  201. Node node = head;
  202. head = head.next;
  203. node.next = null;
  204. size --;
  205. }
  206. // 此时head一定不是待删除的节点
  207. // head.data != data
  208. if (head == null) {
  209. // 此时链表全部是待删除的节点,全部删除完毕
  210. return;
  211. }else {
  212. // 此时头节点已经处理完毕,并且链表也不为空,向下一个节点开始判断
  213. Node prev = head;
  214. while (prev.next != null) {
  215. if (prev.next.data == data) {
  216. // node就是待删除的节点
  217. Node node = prev.next;
  218. prev.next = node.next;
  219. node.next = null;
  220. size --;
  221. }else {
  222. // 此时prev的下一个节点不是要删除的节点,prev继续向后走一个单位
  223. prev = prev.next;
  224. }
  225. }
  226. }
  227. }
  228. public static void main(String[] args) {
  229. SingleLinkedList singleLinkedList = new SingleLinkedList();
  230. singleLinkedList.addLast(2);
  231. singleLinkedList.addLast(2);
  232. singleLinkedList.addLast(2);
  233. singleLinkedList.addLast(3);
  234. singleLinkedList.addLast(2);
  235. System.out.println(singleLinkedList);
  236. singleLinkedList.removeAllValue(2);
  237. // 3
  238. System.out.println(singleLinkedList);
  239. }
  240. @Override
  241. public String toString() {
  242. String ret = "";
  243. // 为啥此时要有一个临时变量node来存储head的值?
  244. Node node = head;
  245. while (node != null) {
  246. ret += node.data + "->";
  247. // 继续访问下一节车厢
  248. node = node.next;
  249. }
  250. ret += "NULL";
  251. return ret;
  252. }
  253. }

3、链表顺序表(数组)的比较

        ①空间资源分配

                数组:数组又叫做顺序表,顺序表是在内存上开辟一段连续的空间来存储数据,数组不允许动态定义数组大大小,即只能在数组定义的时候确定数组的大小。而在实际的使用过程中,用户也无法确定数组的大小,往往出现两种情况:分配的空间太小,数组不够用;分配的空间太大,造成内存空间浪费。

                链表:链表可以看成一列小火车,其由一节节车厢相连而成,物理上是不连续的,逻辑上连续,链表进行动态内存分配,可以适应数据动态的增减的情况。需要新的数据,就new一个节点出来,与原链表相连,不需要时则进行删除,空间释放,不会造成空间的浪费。

        ②表操作时间复杂度

                数组:数组就像编了号站成一排的人,查找某个位置的人很容易,直接根据编号进行查找O(1),而若是进行删除和插入操作就很复杂,后面的人身上的编号都要发生变化O(n)。

                链表:链表就像一列火车,查找某节车厢,需要从前往后遍历O(n),比较复杂,而插入和删除则只需要进行脱钩和连钩操作O(1)。

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/777326
推荐阅读
相关标签
  

闽ICP备14008679号