当前位置:   article > 正文

【数据结构】之双向链表、双端(双向)链表、循环(双向)链表_双端链表和双向链表结构图

双端链表和双向链表结构图

双向链表、双端(双向)链表、循环(双向)链表示意(简)图

声明下面各图中,箭头指向的是整个节点,而不是指向节点中的prior或next。

双向链表:只有一个指针指向最开始的结点。

双端(双向)链表:有两个指针分别指向两端的节点。

循环(双向)链表:指向形成一个闭环。

双端(双向)链表的Java实现(简单示例)

注:双向链表、双端(双向)链表、循环(双向)链表的实现思路几乎一样,这里以实现双端(双向)链表示例。

  1. /**
  2. * 双端(双向)链表的(简单)实现
  3. *
  4. * @author JustryDeng
  5. * @date 2019/5/21 20:51
  6. */
  7. public class DoublesidedLinkedlistImpl<T> {
  8. /** 节点的个数 */
  9. private int size;
  10. /**
  11. * 尾节点
  12. * 注:本人新增节点时,从尾巴上新增
  13. * 注:也可以从头上进行新增节点
  14. */
  15. private Node rear;
  16. /**
  17. * 当前节点
  18. * 即:链表中最前面的节点,可理解为 队列中的头结点
  19. */
  20. private Node head;
  21. /**
  22. * 成员内部类, 节点
  23. */
  24. private class Node {
  25. /** 内部类的元素,Object类型 */
  26. private T data;
  27. /** 上一个节点(即:前驱节点)的地址 */
  28. private Node prior;
  29. /** 下一个节点(即:后继节点)的地址 */
  30. private Node next;
  31. private Node(T data) {
  32. this.data = data;
  33. }
  34. public T getData() {
  35. return data;
  36. }
  37. public void setData(T data) {
  38. this.data = data;
  39. }
  40. public Node getPrior() {
  41. return prior;
  42. }
  43. public void setPrior(Node prior) {
  44. this.prior = prior;
  45. }
  46. public Node getNext() {
  47. return next;
  48. }
  49. public void setNext(Node next) {
  50. this.next = next;
  51. }
  52. }
  53. /**
  54. * 新增节点
  55. *
  56. * @param obj
  57. * 添加的元素
  58. * @return 添加的元素对应的节点
  59. * @date 2019/5/20 20:48
  60. */
  61. public Node addNode(T obj) {
  62. // obj不能为null
  63. notNullCheck(obj);
  64. // 每次增加的元素,都作为新的尾节点
  65. Node newRear = new Node(obj);
  66. if (size == 0) {
  67. head = newRear;
  68. rear = newRear;
  69. } else {
  70. newRear.prior = rear;
  71. rear.next = newRear;
  72. // 更新尾节点
  73. rear = rear.next;
  74. }
  75. size++;
  76. return newRear;
  77. }
  78. /**
  79. * 删除当前(头)节点
  80. *
  81. * @return 被删除了的(头)节点
  82. * @date 2019/5/20 20:50
  83. */
  84. public Node deleteHead() {
  85. if(isEmpty()) {
  86. return null;
  87. }
  88. // targetObj存放原来的头节点数据
  89. Node targetNode = head;
  90. if (head.next != null) {
  91. // 断开后继节点对要删除的节点的引用
  92. head.next.prior = null;
  93. }
  94. // 更新头节点
  95. head = head.next;
  96. size--;
  97. return targetNode;
  98. }
  99. /**
  100. * 删除指定元素对应的第一个节点
  101. *
  102. * @return 删除成功与否(若元素不存在,则返回false)
  103. * @date 2019/5/20 20:50
  104. */
  105. public boolean deleteFirstNode(T obj) {
  106. // obj不能为null
  107. notNullCheck(obj);
  108. // 当head未null时,说明,当前为空链表
  109. if (size == 0) {
  110. return false;
  111. }
  112. // 当前节点
  113. Node current = head;
  114. // 前驱节点
  115. Node previous = head;
  116. // 判断条件,如果当前节点的值和待删除的值不一致,则继续
  117. while (!obj.equals(current.data)) {
  118. // 如果下一个节点不存在(即:当前节点是尾节点)
  119. // 则说明,不存在 元素值为obj的节点,直接返回false
  120. if (current.next == null) {
  121. return false;
  122. } else {
  123. // 更新前驱节点、当前节点
  124. previous = current;
  125. current = current.next;
  126. }
  127. }
  128. // 如果要删除的节点是第一个节点;
  129. if (head.equals(current)) {
  130. if (head.next != null) {
  131. // 断开后继节点对要删除的节点的引用
  132. head.next.prior = null;
  133. }
  134. // 更新头结点
  135. head = current.next;
  136. // 如果要删除的节点是尾节点
  137. } else if ((rear.equals(current))) {
  138. // 前驱节点断开对要删除的节点的应用
  139. previous.next = null;
  140. // 如果要删除的节点不是头结点(是中间的某个节点或尾节点)
  141. } else {
  142. // 将当前节点“抠除”,并将“两端”接上
  143. previous.next = current.next;
  144. current.prior = previous;
  145. }
  146. size--;
  147. return true;
  148. }
  149. /**
  150. * 删除指定元素对应的所有节点
  151. * 注:此方法性能较低,少用或不用
  152. *
  153. *
  154. * @return 删除了的节点数
  155. * @date 2019/5/20 20:50
  156. */
  157. public int deleteAllNode(T obj) {
  158. int count = 0;
  159. while(deleteFirstNode(obj)){
  160. count++;
  161. }
  162. return count;
  163. }
  164. /**
  165. * 查找元素的(第一个)节点
  166. *
  167. * @param obj
  168. * 待查找的元素对象
  169. * @return 元素的(第一个)节点, 无则返回null
  170. */
  171. public Node find(T obj) {
  172. // obj不能为null
  173. notNullCheck(obj);
  174. Node currNode = head;
  175. // 临时大小为size
  176. int tempSize = size;
  177. while (tempSize > 0) {
  178. if (obj.equals(currNode.data)) {
  179. return currNode;
  180. } else {
  181. currNode = currNode.next;
  182. }
  183. tempSize--;
  184. }
  185. return null;
  186. }
  187. /**
  188. * 非空检验
  189. *
  190. * @param obj
  191. * 被验证的对象
  192. * @throws NullPointerException 空指针异常(运行时异常)
  193. * @date 2019/5/20 21:00
  194. */
  195. private void notNullCheck(T obj){
  196. if(obj == null) {
  197. throw new NullPointerException("data must not be null!");
  198. }
  199. }
  200. /**
  201. * 判断链表是否为空
  202. *
  203. * @return 链表是否为空
  204. * @date 2019/5/20 20:49
  205. */
  206. public boolean isEmpty() {
  207. return size == 0;
  208. }
  209. @Override
  210. public String toString() {
  211. if (isEmpty()) {
  212. return "[]";
  213. }
  214. StringBuilder sb = new StringBuilder(16);
  215. sb.append("[");
  216. Node currentNode = head;
  217. for (int i = 0; i < size; i++) {
  218. sb.append(currentNode.data);
  219. sb.append(", ");
  220. currentNode = currentNode.next;
  221. }
  222. int length = sb.length();
  223. sb.delete(length - 2, length).append("]");
  224. return sb.toString();
  225. }
  226. public static void main(String[] args) {
  227. DoublesidedLinkedlistImpl<String> uli = new DoublesidedLinkedlistImpl<>();
  228. // 增
  229. uli.addNode("A");
  230. uli.addNode("B");
  231. uli.addNode("C");
  232. uli.addNode("C");
  233. uli.addNode("D");
  234. uli.addNode("C");
  235. uli.addNode("E");
  236. uli.addNode("E");
  237. uli.addNode("F");
  238. uli.addNode("E");
  239. // toString
  240. System.out.println("增加节点后的初始化链表:\t" + uli);
  241. // 找到C元素对应的第一个节点
  242. System.out.println("找到C元素对应的第一个节点:" + uli.find("C")
  243. + ",该节点的下下个节点的数据内容为:"
  244. + uli.find("C").getNext().getNext().getData());
  245. // 删除头结点
  246. uli.deleteHead();
  247. // toString
  248. System.out.println("删除头结点后:\t" + uli);
  249. // 删除C元素对应的第一个节点
  250. uli.deleteFirstNode("C");
  251. // toString
  252. System.out.println("删除C元素对应的第一个节点后:\t" +uli);
  253. // 删除E元素对应的所有节点
  254. uli.deleteAllNode("E");
  255. // toString
  256. System.out.println("删除E元素对应的所有节点后:\t" +uli);
  257. }
  258. }

测试一下

运行(写在实现类里面的)主方法,控制台输出:

由此可见:简单的双端(双向)链表实现完毕!

 

^_^ 如有不当之处,欢迎指正

^_^ 学习视频
               51CTO学院
《数据结构与算法》,讲师张晨光

^_^ 本文已被收录进《程序员成长笔记(五)》,笔者JustryDeng

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

闽ICP备14008679号