在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

Java模拟单链表和双端链表数据结构的实例讲解

2016-7-29 15:41| 发布者: zhangjf| 查看: 573| 评论: 0

摘要: 模拟单链表 线性表: 线性表(亦作顺序表)是最基本、最简单、也是最常用的一种数据结构。 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。 线性表的 ...

模拟单链表

线性表:
线性表(亦作顺序表)是最基本、最简单、也是最常用的一种数据结构。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。
线性表的逻辑结构简单,便于实现和操作。
在实际应用中,线性表都是以栈、队列、字符串等特殊线性表的形式来使用的。
线性结构的基本特征为:
1.集合中必存在唯一的一个“第一元素”;
2.集合中必存在唯一的一个 “最后元素” ;
3.除最后一个元素之外,均有 唯一的后继(后件);
4.除第一个元素之外,均有 唯一的前驱(前件)。

链表:linked list
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
每个数据项都被包含在“链结点”(Link)中。
链结点是一个类的对象,这类可叫做Link。链表中有许多类似的链结点,每个Link中都中包含有一个对下一个链结点引用的字段next。
链表对象本身保存了一个指向第一个链结点的引用first。(若没有first,则无法定位)
链表不能像数组那样(利用下标)直接访问到数据项,而需要用数据间的关系来定位,即访问链结点所引用的下一个链结点,而后再下一个,直至访问到需要的数据
在链头插入和删除的时间复杂度为O(1),因为只需要改变引用的指向即可
而查找、删除指定结点、在指定结点后插入,这些操作都需要平均都需要搜索链表中的一半结点,效率为O(N)。
单链表:
以“结点的序列”表示线性表 称作线性链表(单链表)
是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。(这组存储单元既可以是连续的,也可以是不连续的)
链结点的结构:

20164884727592.png (180×69)

存放结点值的数据域data;存放结点的引用 的指针域(链域)next
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
每个结点只有一个链域的链表称为单链表(Single Linked List) , 一个方向, 只有后继结节的引用

  1. /**
  2. * 单链表:头插法 后进先出
  3. * 将链表的左边称为链头,右边称为链尾。
  4. * 头插法建单链表是将链表右端看成固定的,链表不断向左延伸而得到的。
  5. * 头插法最先得到的是尾结点
  6. * @author stone
  7. */
  8. public class SingleLinkedList<T> {
  9. private Link<T> first; //首结点
  10. public SingleLinkedList() {
  11. }
  12. public boolean isEmpty() {
  13. return first == null;
  14. }
  15. public void insertFirst(T data) {// 插入 到 链头
  16. Link<T> newLink = new Link<T>(data);
  17. newLink.next = first; //新结点的next指向上一结点
  18. first = newLink;
  19. }
  20. public Link<T> deleteFirst() {//删除 链头
  21. Link<T> temp = first;
  22. first = first.next; //变更首结点,为下一结点
  23. return temp;
  24. }
  25. public Link<T> find(T t) {
  26. Link<T> find = first;
  27. while (find != null) {
  28. if (!find.data.equals(t)) {
  29. find = find.next;
  30. } else {
  31. break;
  32. }
  33. }
  34. return find;
  35. }
  36. public Link<T> delete(T t) {
  37. if (isEmpty()) {
  38. return null;
  39. } else {
  40. if (first.data.equals(t)) {
  41. Link<T> temp = first;
  42. first = first.next; //变更首结点,为下一结点
  43. return temp;
  44. }
  45. }
  46. Link<T> p = first;
  47. Link<T> q = first;
  48. while (!p.data.equals(t)) {
  49. if (p.next == null) {//表示到链尾还没找到
  50. return null;
  51. } else {
  52. q = p;
  53. p = p.next;
  54. }
  55. }
  56. q.next = p.next;
  57. return p;
  58. }
  59. public void displayList() {//遍历
  60. System.out.println("List (first-->last):");
  61. Link<T> current = first;
  62. while (current != null) {
  63. current.displayLink();
  64. current = current.next;
  65. }
  66. }
  67. public void displayListReverse() {//反序遍历
  68. Link<T> p = first, q = first.next, t;
  69. while (q != null) {//指针反向,遍历的数据顺序向后
  70. t = q.next; //no3
  71. if (p == first) {// 当为原来的头时,头的.next应该置空
  72. p.next = null;
  73. }
  74. q.next = p;// no3 -> no1 pointer reverse
  75. p = q; //start is reverse
  76. q = t; //no3 start
  77. }
  78. //上面循环中的if里,把first.next 置空了, 而当q为null不执行循环时,p就为原来的最且一个数据项,反转后把p赋给first
  79. first = p;
  80. displayList();
  81. }
  82. class Link<T> {//链结点
  83. T data; //数据域
  84. Link<T> next; //后继指针,结点 链域
  85. Link(T data) {
  86. this.data = data;
  87. }
  88. void displayLink() {
  89. System.out.println("the data is " + data.toString());
  90. }
  91. }
  92. public static void main(String[] args) {
  93. SingleLinkedList<Integer> list = new SingleLinkedList<Integer>();
  94. list.insertFirst(33);
  95. list.insertFirst(78);
  96. list.insertFirst(24);
  97. list.insertFirst(22);
  98. list.insertFirst(56);
  99. list.displayList();
  100. list.deleteFirst();
  101. list.displayList();
  102. System.out.println("find:" + list.find(56));
  103. System.out.println("find:" + list.find(33));
  104. System.out.println("delete find:" + list.delete(99));
  105. System.out.println("delete find:" + list.delete(24));
  106. list.displayList();
  107. System.out.println("----reverse----");
  108. list.displayListReverse();
  109. }
  110. }
复制代码

打印

  1. List (first-->last):
  2. the data is 56
  3. the data is 22
  4. the data is 24
  5. the data is 78
  6. the data is 33
  7. List (first-->last):
  8. the data is 22
  9. the data is 24
  10. the data is 78
  11. the data is 33
  12. find:null
  13. find:linked_list.SingleLinkedList$Link@4b71bbc9
  14. delete find:null
  15. delete find:linked_list.SingleLinkedList$Link@17dfafd1
  16. List (first-->last):
  17. the data is 22
  18. the data is 78
  19. the data is 33
  20. ----reverse----
  21. List (first-->last):
  22. the data is 33
  23. the data is 78
  24. the data is 22
复制代码

单链表:尾插法 、后进先出 ——若将链表的左端固定,链表不断向右延伸,这种建立链表的方法称为尾插法。
尾插法建立链表时,头指针固定不动,故必须设立一个尾部的指针,向链表右边延伸,
尾插法最先得到的是头结点。

  1. public class SingleLinkedList2<T> {
  2. private Link<T> head; //首结点
  3. public SingleLinkedList2() {
  4. }
  5. public boolean isEmpty() {
  6. return head == null;
  7. }
  8. public void insertLast(T data) {//在链尾 插入
  9. Link<T> newLink = new Link<T>(data);
  10. if (head != null) {
  11. Link<T> nextP = head.next;
  12. if (nextP == null) {
  13. head.next = newLink;
  14. } else {
  15. Link<T> rear = null;
  16. while (nextP != null) {
  17. rear = nextP;
  18. nextP = nextP.next;
  19. }
  20. rear.next = newLink;
  21. }
  22. } else {
  23. head = newLink;
  24. }
  25. }
  26. public Link<T> deleteLast() {//删除 链尾
  27. Link<T> p = head;
  28. Link<T> q = head;
  29. while (p.next != null) {// p的下一个结点不为空,q等于当前的p(即q是上一个,p是下一个) 循环结束时,q等于链尾倒数第二个
  30. q = p;
  31. p = p.next;
  32. }
  33. //delete
  34. q.next = null;
  35. return p;
  36. }
  37. public Link<T> find(T t) {
  38. Link<T> find = head;
  39. while (find != null) {
  40. if (!find.data.equals(t)) {
  41. find = find.next;
  42. } else {
  43. break;
  44. }
  45. }
  46. return find;
  47. }
  48. public Link<T> delete(T t) {
  49. if (isEmpty()) {
  50. return null;
  51. } else {
  52. if (head.data.equals(t)) {
  53. Link<T> temp = head;
  54. head = head.next; //变更首结点,为下一结点
  55. return temp;
  56. }
  57. }
  58. Link<T> p = head;
  59. Link<T> q = head;
  60. while (!p.data.equals(t)) {
  61. if (p.next == null) {//表示到链尾还没找到
  62. return null;
  63. } else {
  64. q = p;
  65. p = p.next;
  66. }
  67. }
  68. q.next = p.next;
  69. return p;
  70. }
  71. public void displayList() {//遍历
  72. System.out.println("List (head-->last):");
  73. Link<T> current = head;
  74. while (current != null) {
  75. current.displayLink();
  76. current = current.next;
  77. }
  78. }
  79. public void displayListReverse() {//反序遍历
  80. Link<T> p = head, q = head.next, t;
  81. while (q != null) {//指针反向,遍历的数据顺序向后
  82. t = q.next; //no3
  83. if (p == head) {// 当为原来的头时,头的.next应该置空
  84. p.next = null;
  85. }
  86. q.next = p;// no3 -> no1 pointer reverse
  87. p = q; //start is reverse
  88. q = t; //no3 start
  89. }
  90. //上面循环中的if里,把head.next 置空了, 而当q为null不执行循环时,p就为原来的最且一个数据项,反转后把p赋给head
  91. head = p;
  92. displayList();
  93. }
  94. class Link<T> {//链结点
  95. T data; //数据域
  96. Link<T> next; //后继指针,结点 链域
  97. Link(T data) {
  98. this.data = data;
  99. }
  100. void displayLink() {
  101. System.out.println("the data is " + data.toString());
  102. }
  103. }
  104. public static void main(String[] args) {
  105. SingleLinkedList2<Integer> list = new SingleLinkedList2<Integer>();
  106. list.insertLast(33);
  107. list.insertLast(78);
  108. list.insertLast(24);
  109. list.insertLast(22);
  110. list.insertLast(56);
  111. list.displayList();
  112. list.deleteLast();
  113. list.displayList();
  114. System.out.println("find:" + list.find(56));
  115. System.out.println("find:" + list.find(33));
  116. System.out.println("delete find:" + list.delete(99));
  117. System.out.println("delete find:" + list.delete(78));
  118. list.displayList();
  119. System.out.println("----reverse----");
  120. list.displayListReverse();
  121. }
  122. }
复制代码

打印

  1. List (head-->last):
  2. the data is 33
  3. the data is 78
  4. the data is 24
  5. the data is 22
  6. the data is 56
  7. List (head-->last):
  8. the data is 33
  9. the data is 78
  10. the data is 24
  11. the data is 22
  12. find:null
  13. find:linked_list.SingleLinkedList2$Link@4b71bbc9
  14. delete find:null
  15. delete find:linked_list.SingleLinkedList2$Link@17dfafd1
  16. List (head-->last):
  17. the data is 33
  18. the data is 24
  19. the data is 22
  20. ----reverse----
  21. List (head-->last):
  22. the data is 22
  23. the data is 24
  24. the data is 33
复制代码

模拟双端链表,以链表实现栈和队列
双端链表:
双端链表与传统链表非常相似.只是新增了一个属性-即对最后一个链结点的引用rear
这样在链尾插入会变得非常容易,只需改变rear的next为新增的结点即可,而不需要循环搜索到最后一个节点
所以有insertFirst、insertLast
删除链头时,只需要改变引用指向即可;删除链尾时,需要将倒数第二个结点的next置空,
而没有一个引用是指向它的,所以还是需要循环来读取操作

  1. /**
  2. * 双端链表
  3. * @author stone
  4. */
  5. public class TwoEndpointList<T> {
  6. private Link<T> head; //首结点
  7. private Link<T> rear; //尾部指针
  8. public TwoEndpointList() {
  9. }
  10. public T peekHead() {
  11. if (head != null) {
  12. return head.data;
  13. }
  14. return null;
  15. }
  16. public boolean isEmpty() {
  17. return head == null;
  18. }
  19. public void insertFirst(T data) {// 插入 到 链头
  20. Link<T> newLink = new Link<T>(data);
  21. newLink.next = head; //新结点的next指向上一结点
  22. head = newLink;
  23. }
  24. public void insertLast(T data) {//在链尾 插入
  25. Link<T> newLink = new Link<T>(data);
  26. if (head == null) {
  27. rear = null;
  28. }
  29. if (rear != null) {
  30. rear.next = newLink;
  31. } else {
  32. head = newLink;
  33. head.next = rear;
  34. }
  35. rear = newLink; //下次插入时,从rear处插入
  36. }
  37. public T deleteHead() {//删除 链头
  38. if (isEmpty()) return null;
  39. Link<T> temp = head;
  40. head = head.next; //变更首结点,为下一结点
  41. if (head == null) {
  42. <span style="white-space:pre"> </span>rear = head;
  43. }
  44. return temp.data;
  45. }
  46. public T find(T t) {
  47. if (isEmpty()) {
  48. return null;
  49. }
  50. Link<T> find = head;
  51. while (find != null) {
  52. if (!find.data.equals(t)) {
  53. find = find.next;
  54. } else {
  55. break;
  56. }
  57. }
  58. if (find == null) {
  59. return null;
  60. }
  61. return find.data;
  62. }
  63. public T delete(T t) {
  64. if (isEmpty()) {
  65. return null;
  66. } else {
  67. if (head.data.equals(t)) {
  68. Link<T> temp = head;
  69. head = head.next; //变更首结点,为下一结点
  70. return temp.data;
  71. }
  72. }
  73. Link<T> p = head;
  74. Link<T> q = head;
  75. while (!p.data.equals(t)) {
  76. if (p.next == null) {//表示到链尾还没找到
  77. return null;
  78. } else {
  79. q = p;
  80. p = p.next;
  81. }
  82. }
  83. q.next = p.next;
  84. return p.data;
  85. }
  86. public void displayList() {//遍历
  87. System.out.println("List (head-->last):");
  88. Link<T> current = head;
  89. while (current != null) {
  90. current.displayLink();
  91. current = current.next;
  92. }
  93. }
  94. public void displayListReverse() {//反序遍历
  95. if (isEmpty()) {
  96. return;
  97. }
  98. Link<T> p = head, q = head.next, t;
  99. while (q != null) {//指针反向,遍历的数据顺序向后
  100. t = q.next; //no3
  101. if (p == head) {// 当为原来的头时,头的.next应该置空
  102. p.next = null;
  103. }
  104. q.next = p;// no3 -> no1 pointer reverse
  105. p = q; //start is reverse
  106. q = t; //no3 start
  107. }
  108. //上面循环中的if里,把head.next 置空了, 而当q为null不执行循环时,p就为原来的最且一个数据项,反转后把p赋给head
  109. head = p;
  110. displayList();
  111. }
  112. class Link<T> {//链结点
  113. T data; //数据域
  114. Link<T> next; //后继指针,结点 链域
  115. Link(T data) {
  116. this.data = data;
  117. }
  118. void displayLink() {
  119. System.out.println("the data is " + data.toString());
  120. }
  121. }
  122. public static void main(String[] args) {
  123. TwoEndpointList<Integer> list = new TwoEndpointList<Integer>();
  124. list.insertLast(1);
  125. list.insertFirst(2);
  126. list.insertLast(3);
  127. list.insertFirst(4);
  128. list.insertLast(5);
  129. list.displayList();
  130. list.deleteHead();
  131. list.displayList();
  132. System.out.println("find:" + list.find(6));
  133. System.out.println("find:" + list.find(3));
  134. System.out.println("delete find:" + list.delete(6));
  135. System.out.println("delete find:" + list.delete(5));
  136. list.displayList();
  137. System.out.println("----reverse----");
  138. list.displayListReverse();
  139. }
  140. }
复制代码

打印

  1. List (head-->last):
  2. the data is 4
  3. the data is 2
  4. the data is 1
  5. the data is 3
  6. the data is 5
  7. List (head-->last):
  8. the data is 2
  9. the data is 1
  10. the data is 3
  11. the data is 5
  12. find:null
  13. find:3
  14. delete find:null
  15. delete find:5
  16. List (head-->last):
  17. the data is 2
  18. the data is 1
  19. the data is 3
  20. ----reverse----
  21. List (head-->last):
  22. the data is 3
  23. the data is 1
  24. the data is 2
复制代码

使用链表实现栈 ,用前插 单链表就能实现,
本类采用双端链表实现:

  1. public class LinkStack<T> {
  2. private TwoEndpointList<T> datas;
  3. public LinkStack() {
  4. datas = new TwoEndpointList<T>();
  5. }
  6. // 入栈
  7. public void push(T data) {
  8. datas.insertFirst(data);
  9. }
  10. // 出栈
  11. public T pop() {
  12. return datas.deleteHead();
  13. }
  14. // 查看栈顶
  15. public T peek() {
  16. return datas.peekHead();
  17. }
  18. //栈是否为空
  19. public boolean isEmpty() {
  20. return datas.isEmpty();
  21. }
  22. public static void main(String[] args) {
  23. LinkStack<Integer> stack = new LinkStack<Integer>();
  24. for (int i = 0; i < 5; i++) {
  25. stack.push(i);
  26. }
  27. for (int i = 0; i < 5; i++) {
  28. Integer peek = stack.peek();
  29. System.out.println("peek:" + peek);
  30. }
  31. for (int i = 0; i < 6; i++) {
  32. Integer pop = stack.pop();
  33. System.out.println("pop:" + pop);
  34. }
  35. System.out.println("----");
  36. for (int i = 5; i > 0; i--) {
  37. stack.push(i);
  38. }
  39. for (int i = 5; i > 0; i--) {
  40. Integer peek = stack.peek();
  41. System.out.println("peek:" + peek);
  42. }
  43. for (int i = 5; i > 0; i--) {
  44. Integer pop = stack.pop();
  45. System.out.println("pop:" + pop);
  46. }
  47. }
  48. }
复制代码

打印

  1. peek:4
  2. peek:4
  3. peek:4
  4. peek:4
  5. peek:4
  6. pop:4
  7. pop:3
  8. pop:2
  9. pop:1
  10. pop:0
  11. pop:null
  12. ----
  13. peek:1
  14. peek:1
  15. peek:1
  16. peek:1
  17. peek:1
  18. pop:1
  19. pop:2
  20. pop:3
  21. pop:4
  22. pop:5
复制代码

链表实现 队列 用双端链表实现:

  1. public class LinkQueue<T> {
  2. private TwoEndpointList<T> list;
  3. public LinkQueue() {
  4. list = new TwoEndpointList<T>();
  5. }
  6. //插入队尾
  7. public void insert(T data) {
  8. list.insertLast(data);
  9. }
  10. //移除队头
  11. public T remove() {
  12. return list.deleteHead();
  13. }
  14. //查看队头
  15. public T peek() {
  16. return list.peekHead();
  17. }
  18. public boolean isEmpty() {
  19. return list.isEmpty();
  20. }
  21. public static void main(String[] args) {
  22. LinkQueue<Integer> queue = new LinkQueue<Integer>();
  23. for (int i = 1; i < 5; i++) {
  24. queue.insert(i);
  25. }
  26. for (int i = 1; i < 5; i++) {
  27. Integer peek = queue.peek();
  28. System.out.println("peek:" + peek);
  29. }
  30. for (int i = 1; i < 5; i++) {
  31. Integer remove = queue.remove();
  32. System.out.println("remove:" + remove);
  33. }
  34. System.out.println("----");
  35. for (int i = 5; i > 0; i--) {
  36. queue.insert(i);
  37. }
  38. for (int i = 5; i > 0; i--) {
  39. Integer peek = queue.peek();
  40. System.out.println("peek2:" + peek);
  41. }
  42. for (int i = 5; i > 0; i--) {
  43. Integer remove = queue.remove();
  44. System.out.println("remove:" + remove);
  45. }
  46. }
  47. }
复制代码

打印

  1. peek:1
  2. peek:1
  3. peek:1
  4. peek:1
  5. remove:1
  6. remove:2
  7. remove:3
  8. remove:4
  9. ----
  10. peek2:5
  11. peek2:5
  12. peek2:5
  13. peek2:5
  14. peek2:5
  15. remove:5
  16. remove:4
  17. remove:3
  18. remove:2
  19. remove:1
复制代码

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-5-6 13:25

Copyright 2015-2025 djqfx

返回顶部