单链表

介绍

链表是有序的列表,但是它在内存中是存储如下(物理结构)
image.png

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含data域,next域:指向下一个节点
  3. 如图:发现链表的各个节点不一定是连续存储
  4. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

逻辑结构
image.png

思路

实现单链表的增删改查
1.不需要构造器,用一个成员变量head指向第一个节点(一般不为空)
2.添加:从头开始遍历,找到最后一个元素,然后把元素挂在最后一个元素的next
3.顺序添加:从头开始遍历,找到值大于元素值的上一个节点,然后挂在这个节点和他next的中间
4.修改:遍历找到节点,然后赋值
5.删除:找到需要删除节点的前一个节点,然后node.next=node.next.next

代码实现

  1. package com.sgg.datastructure.linklist;
  2. public class SingleLinkList {
  3. public HeroNode head = new HeroNode(0, "", "");
  4. public void add(HeroNode heroNode) {
  5. HeroNode tmp = head;
  6. while (true) {
  7. if (tmp.next == null) {
  8. tmp.next = heroNode;
  9. break;
  10. }
  11. tmp = tmp.next;
  12. }
  13. }
  14. //按照编号no的顺序添加到指定位置
  15. //编号相同则提示
  16. public void addByOrder(HeroNode node) {
  17. boolean flag = false;
  18. HeroNode temp = head;
  19. while (true) {
  20. if (temp.next == null) {
  21. break;
  22. }
  23. if (temp.next.no == node.no) {
  24. flag = true;
  25. break;
  26. } else if (temp.next.no > node.no) {
  27. break;
  28. }
  29. temp = temp.next;
  30. }
  31. if (flag) {
  32. System.out.printf("英雄编号%d已经重复了,不允许重复添加\n", node.no);
  33. } else {
  34. node.next = temp.next;
  35. temp.next = node;
  36. }
  37. }
  38. //修改
  39. public void update(HeroNode heroNode) {
  40. boolean flag = false;
  41. if (head.next == null) {
  42. System.out.println("链表为空,无法操作");
  43. return;
  44. }
  45. HeroNode temp = head;
  46. while (true) {
  47. temp = temp.next;
  48. if (temp.no == heroNode.no) {
  49. flag = true;
  50. break;
  51. }
  52. if (temp.next == null) {
  53. break;
  54. }
  55. }
  56. if (flag) {
  57. temp.name = heroNode.name;
  58. temp.nickname = heroNode.nickname;
  59. }else {
  60. System.out.printf("链表里面没有编号为%d的英雄\n",heroNode.no);
  61. }
  62. }
  63. //删除:找到相等的那个节点的上一个节点,把他的next=next.next
  64. public void delete(int no) {
  65. boolean flag = false;
  66. if (head.next == null) {
  67. System.out.println("链表为空,无法操作");
  68. return;
  69. }
  70. HeroNode temp = head;
  71. while (true) {
  72. if (temp.next == null) {
  73. break;
  74. }
  75. if (temp.next.no == no) {
  76. flag = true;
  77. break;
  78. }
  79. temp = temp.next;
  80. }
  81. if (flag) {
  82. temp.next = temp.next.next;
  83. }else {
  84. System.out.printf("链表里面没有编号为%d的英雄\n",no);
  85. }
  86. }
  87. public void show() {
  88. if (head.next == null) {
  89. System.out.println("链表无数据");
  90. return;
  91. }
  92. HeroNode tmp = head.next;
  93. do {
  94. System.out.println(tmp);
  95. tmp = tmp.next;
  96. } while (tmp != null);
  97. }
  98. public static void main(String[] args) {
  99. SingleLinkList singleLinkList = new SingleLinkList();
  100. singleLinkList.addByOrder(new HeroNode(1, "宋江", "及时雨"));
  101. singleLinkList.addByOrder(new HeroNode(3, "吴勇", "智多星"));
  102. singleLinkList.addByOrder(new HeroNode(4, "aa", "dddd"));
  103. singleLinkList.addByOrder(new HeroNode(2, "曹盖", "牛逼"));
  104. singleLinkList.show();
  105. System.out.println();
  106. HeroNode update=new HeroNode(1, "宋江11", "及时雨11");
  107. singleLinkList.update(update);
  108. singleLinkList.show();
  109. System.out.println();
  110. singleLinkList.delete(3);
  111. singleLinkList.delete(1);
  112. singleLinkList.delete(9);
  113. singleLinkList.show();
  114. }
  115. }

作业

比较简单直接写代码,放在上面的那个类里面

  1. /**
  2. * 得到链表的长度
  3. */
  4. public int getSize() {
  5. if (head.next == null) {
  6. return 0;
  7. }
  8. HeroNode temp = head.next;
  9. int count = 0;
  10. while (temp != null) {
  11. count++;
  12. temp = temp.next;
  13. }
  14. return count;
  15. }
  16. /**
  17. * 找到倒数第k个节点
  18. */
  19. public HeroNode findLastK(int k) {
  20. int size = getSize();
  21. if (k > size) {
  22. throw new RuntimeException("倒数的个数超过总容量,报错");
  23. }
  24. int index = size - k + 1;
  25. HeroNode temp = head;
  26. while (index > 0) {
  27. temp = temp.next;
  28. index--;
  29. }
  30. return temp;
  31. }

反转链表
思路:
1.新定义头结点new
2.遍历链表,取出,放在new的最前端
3.head.next=new
代码实现

  1. /**
  2. * 反转 自己写的
  3. */
  4. public void reverse() {
  5. //除了头结点以外没有节点或只有1个节点,就不用反转
  6. if (head.next == null || head.next.next == null) {
  7. return;
  8. }
  9. HeroNode reverseNode = new HeroNode();
  10. HeroNode temp = head.next;
  11. while (temp != null) {
  12. HeroNode heroNode = new HeroNode(temp.no, temp.name, temp.nickname);
  13. heroNode.next = reverseNode.next;
  14. reverseNode.next = heroNode;
  15. temp = temp.next;
  16. }
  17. head.next = reverseNode.next;
  18. }
  19. /**
  20. * 老师的思路
  21. */
  22. public void reverseTeacher() {
  23. //除了头结点以外没有节点或只有1个节点,就不用反转
  24. if (head.next == null || head.next.next == null) {
  25. return;
  26. }
  27. HeroNode reverseNode = new HeroNode();
  28. HeroNode temp = head.next;
  29. HeroNode next = null;//保留下一个节点,要不然去操作temp.next后就丢了
  30. while (temp != null) {
  31. next = temp.next;
  32. temp.next = reverseNode.next;
  33. reverseNode.next = temp;
  34. temp = next;
  35. }
  36. head.next = reverseNode.next;
  37. }

有点意思,老师写的比较巧妙,把丢的东西先放在兜里,似乎可以节约一些空间
逆序打印

  1. /**
  2. * 逆序打印单链表
  3. * 思路:不破坏原先的链表
  4. * 利用栈的后进先出的特性
  5. */
  6. public void reversePrint(){
  7. Stack<HeroNode> stack = new Stack<>();
  8. HeroNode temp = head.next;
  9. while (temp != null) {
  10. stack.add(temp);
  11. temp = temp.next;
  12. }
  13. while (stack.size() > 0) {
  14. System.out.println(stack.pop());
  15. }
  16. }

合并2个有序的链表,合并完也要有序的
暂时还不会做

双向链表

介绍

在单链表基础上增加个,其他不变

  1. public HeroNode prev;//相比单项链表,需要额外维护这个


单链表查找是单向的,这个可以是双向的
删除的时候不用辅助节点,自己就行了

思路

增加:多维护一个关系
修改不变
遍历展示不变
删除:改2个维护关系,后面的那个可能是空就不维护

代码实现

  1. package com.sgg.datastructure.doubleLinkedLis;
  2. public class DoubleLinkList {
  3. public HeroNode head = new HeroNode(0, "", "");
  4. /**
  5. * 默认加到最后
  6. *
  7. * @param heroNode
  8. */
  9. public void add(HeroNode heroNode) {
  10. HeroNode tmp = head;
  11. while (true) {
  12. if (tmp.next == null) {
  13. tmp.next = heroNode;
  14. heroNode.prev = tmp;//多维护个
  15. break;
  16. }
  17. tmp = tmp.next;
  18. }
  19. }
  20. //作业
  21. //按照编号no的顺序添加到指定位置
  22. //编号相同则提示
  23. public void addByOrder(HeroNode node) {
  24. boolean flag = false;
  25. HeroNode temp = head;
  26. while (true) {
  27. if (temp.next == null) {
  28. break;
  29. }
  30. if (temp.next.no == node.no) {
  31. flag = true;
  32. break;
  33. } else if (temp.next.no > node.no) {
  34. break;
  35. }
  36. temp = temp.next;
  37. }
  38. if (flag) {
  39. System.out.printf("英雄编号%d已经重复了,不允许重复添加\n", node.no);
  40. } else {
  41. if (temp.next != null) {
  42. temp.next.prev = node;
  43. }
  44. node.next = temp.next;
  45. temp.next = node;
  46. node.prev = temp;
  47. }
  48. }
  49. //修改,不变
  50. public void update(HeroNode heroNode) {
  51. boolean flag = false;
  52. if (head.next == null) {
  53. System.out.println("链表为空,无法操作");
  54. return;
  55. }
  56. HeroNode temp = head;
  57. while (true) {
  58. temp = temp.next;
  59. if (temp.no == heroNode.no) {
  60. flag = true;
  61. break;
  62. }
  63. if (temp.next == null) {
  64. break;
  65. }
  66. }
  67. if (flag) {
  68. temp.name = heroNode.name;
  69. temp.nickname = heroNode.nickname;
  70. } else {
  71. System.out.printf("链表里面没有编号为%d的英雄\n", heroNode.no);
  72. }
  73. }
  74. //删除:找到相等的那个节点的 他的上个节点指向他的下个节点,如果有下个节点,那么下个节点指向上个节点
  75. public void delete(int no) {
  76. boolean flag = false;
  77. if (head.next == null) {
  78. System.out.println("链表为空,无法操作");
  79. return;
  80. }
  81. HeroNode temp = head.next;
  82. while (true) {
  83. if (temp == null) {
  84. break;
  85. }
  86. if (temp.no == no) {
  87. flag = true;
  88. break;
  89. }
  90. temp = temp.next;
  91. }
  92. if (flag) {
  93. temp.prev.next = temp.next;
  94. if (temp.next != null) {
  95. temp.next.prev = temp.prev;
  96. }
  97. } else {
  98. System.out.printf("链表里面没有编号为%d的英雄\n", no);
  99. }
  100. }
  101. //不变
  102. public void show() {
  103. if (head.next == null) {
  104. System.out.println("链表无数据");
  105. return;
  106. }
  107. HeroNode tmp = head.next;
  108. do {
  109. System.out.println(tmp);
  110. tmp = tmp.next;
  111. } while (tmp != null);
  112. }
  113. public int getSize() {
  114. if (head.next == null) {
  115. return 0;
  116. }
  117. HeroNode temp = head.next;
  118. int count = 0;
  119. while (temp != null) {
  120. count++;
  121. temp = temp.next;
  122. }
  123. return count;
  124. }
  125. public static void main(String[] args) {
  126. DoubleLinkList list = new DoubleLinkList();
  127. // list.add(new HeroNode(1, "宋江", "及时雨"));
  128. // list.add(new HeroNode(2, "22", "及时雨"));
  129. // list.add(new HeroNode(3, "吴勇", "智多星"));
  130. // list.add(new HeroNode(4, "aa", "dddd"));
  131. // list.show();
  132. // list.update(new HeroNode(4, "aa4", "dddd4"));
  133. // System.out.println();
  134. // list.show();
  135. // list.delete(4);
  136. // System.out.println();
  137. // list.show();
  138. list.addByOrder(new HeroNode(2, "22", "及时雨"));
  139. list.addByOrder(new HeroNode(1, "宋江", "及时雨"));
  140. list.addByOrder(new HeroNode(4, "aa", "dddd"));
  141. list.addByOrder(new HeroNode(9, "吴勇9", "智多星"));
  142. list.addByOrder(new HeroNode(8, "吴勇8", "智多星"));
  143. list.addByOrder(new HeroNode(6, "吴勇6", "智多星"));
  144. list.show();
  145. }
  146. }

作业

顺序添加,有点难。要维护4个指针关系

环形链表

介绍

Josephu问题为:设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
image.png

思路

构建(批量添加)

临时指针指向最后一个
第一次:首节点next指向自己,临时指针指向first
>=2次:临时指针next=新节点,新节点next=首节点,临时执政指向新的

遍历

遍历,直到next是首节点

出圈(形成队列)

/*
描述:n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列
出圈的思路
1.创建个辅助指针,指向队列的最后一个位置(已经有了1个指向第一个位置的)
2.辅助指针和头指针先向前移动k-1个位置
3.辅助指针和头指针向前移动m-1个位置
4.出列的做法:头指针移动下一个位置,辅助指针指向新头指针位置
5.最终尾指针和头指针指向同一个位置,结束
*/

代码实现

  1. package com.sgg.datastructure.circleLinkedList;
  2. public class CircleLinkedList {
  3. private Boy first = new Boy(0);
  4. public CircleLinkedList() {
  5. }
  6. public void add(int num) {
  7. if (num < 2) {
  8. System.out.println("数量必须大于等于2");
  9. return;
  10. }
  11. Boy temp = null;
  12. for (int i = 1; i <= num; i++) {
  13. if (i == 1) {
  14. first = new Boy(1);
  15. temp = first;
  16. first.setNext(first);
  17. } else {
  18. Boy boy = new Boy(i);
  19. temp.setNext(boy);
  20. boy.setNext(first);
  21. temp = boy;
  22. }
  23. }
  24. }
  25. /**
  26. * 不断遍历,如果指针下一个是头结点,打印下就退出
  27. */
  28. public void show() {
  29. Boy temp = first;
  30. while (true) {
  31. System.out.println(temp.getNo());
  32. if (temp.getNext() == first) {
  33. break;
  34. }
  35. temp = temp.getNext();
  36. }
  37. }
  38. public int size() {
  39. int size = 1;
  40. Boy temp = first;
  41. while (temp.getNext() != first) {
  42. size++;
  43. temp = temp.getNext();
  44. }
  45. return size;
  46. }
  47. /**
  48. * 描述:n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列
  49. * 出圈的思路
  50. * 1.创建个辅助指针,指向队列的最后一个位置(已经有了1个指向第一个位置的)
  51. * 2.辅助指针和头指针先向前移动k-1个位置
  52. * 3.辅助指针和头指针向前移动m-1个位置
  53. * 4.出列的做法:头指针移动下一个位置,辅助指针指向新头指针位置
  54. * 5.最终尾指针和头指针指向同一个位置,结束
  55. */
  56. public int finalPerson(int k, int m) {
  57. if (k > size()) {
  58. throw new RuntimeException("k必须小于小朋友的总数");
  59. }
  60. if (m < 1) {
  61. throw new RuntimeException("m必须大于2");
  62. }
  63. Boy temp = first;
  64. while (true) {
  65. if (temp.getNext() == first) {
  66. break;
  67. }
  68. temp = temp.getNext();
  69. }
  70. //5
  71. while (temp != first) {
  72. //2
  73. for (int i = 0; i < k - 1; i++) {
  74. first = first.getNext();
  75. temp = temp.getNext();
  76. }
  77. //3
  78. for (int i = 0; i < m - 1; i++) {
  79. first = first.getNext();
  80. temp = temp.getNext();
  81. }
  82. System.out.printf("小朋友%d出列\n", first.getNo());
  83. //4
  84. first = first.getNext();
  85. temp.setNext(first);
  86. }
  87. System.out.println("final winner:"+first.getNo());
  88. return first.getNo();
  89. }
  90. public static void main(String[] args) {
  91. CircleLinkedList circleLinkedList = new CircleLinkedList();
  92. int n = 5;
  93. int k =1;
  94. int m = 2;
  95. circleLinkedList.add(n);
  96. circleLinkedList.show();
  97. System.out.println("size:" + circleLinkedList.size());
  98. int result1 = circleLinkedList.finalPerson(k, m);
  99. }
  100. }

结果

image.png