1、链表概念及应用

1.1、概念

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

1.2、链表应用-增删改查

使用带head头的单向链表实现 –水浒英雄排行榜管理
1、完成对英雄人物的增删改查操作, 注: 删除和修改,查找
可以考虑学员独立完成,也可带学员完成
2、第一种方法在添加英雄时,直接添加到链表的尾部
3、第二种方式在添加英雄时,根据排名将英雄插入到指定位置 (如果有这个排名,则添加失败,并给出提示)

1.2.1、data类

  1. public class Data {
  2. private int no;
  3. private String name;
  4. private String nickName;
  5. public Data(int no, String name, String nickName) {
  6. this.no = no;
  7. this.name = name;
  8. this.nickName = nickName;
  9. }
  10. public Data() {
  11. }
  12. public int getNo() {
  13. return no;
  14. }
  15. public void setNo(int no) {
  16. this.no = no;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public String getNickName() {
  25. return nickName;
  26. }
  27. public void setNickName(String nickName) {
  28. this.nickName = nickName;
  29. }
  30. @Override
  31. public String toString() {
  32. return "Data{" +
  33. "no=" + no +
  34. ", name='" + name + '\'' +
  35. ", nickName='" + nickName + '\'' +
  36. '}';
  37. }
  38. }

1.2.2、Node类

  1. public class Node {
  2. private Data data;
  3. private Node next;
  4. public Node() {
  5. }
  6. public Node(Data data) {
  7. this.data = data;
  8. }
  9. public Node(Data data,Node next) {
  10. this.data = data;
  11. this.next=next;
  12. }
  13. public Data getData() {
  14. return data;
  15. }
  16. public void setData(Data data) {
  17. this.data = data;
  18. }
  19. public Node getNext() {
  20. return next;
  21. }
  22. public void setNext(Node next) {
  23. this.next = next;
  24. }
  25. @Override
  26. public String toString() {
  27. return "Node{" +
  28. "data=" + data +
  29. '}';
  30. }
  31. }

1.2.3、SingleLinkedList类

  1. public class SingleLinkedList {
  2. private Node head=new Node(null,null);
  3. public void setHead(Node head){
  4. this.head=head;
  5. }
  6. public Node getHead(){
  7. return head;
  8. }
  9. //添加节点,不考虑排序
  10. public void insertNode(Node node){
  11. if(node.getData()==null) System.out.println("无效的节点");
  12. if(head.getNext()==null){
  13. head.setNext(node);
  14. System.out.println("添加结点成功");
  15. return;
  16. }
  17. Node temp=head;
  18. while (true){
  19. if(temp.getNext()==null){
  20. temp.setNext(node);
  21. System.out.println("添加结点成功");
  22. break;
  23. }
  24. temp=temp.getNext();
  25. }
  26. }
  27. //排序添加节点且不允许相同no的节点
  28. public void insertNodeByOrder(Node node){
  29. if(node.getData()==null) System.out.println("无效的节点");
  30. if(head.getNext()==null){
  31. head.setNext(node);
  32. return;
  33. }
  34. Node temp=head;
  35. while(true){
  36. if(temp.getNext()==null){
  37. temp.setNext(node);
  38. break;
  39. }else if(temp.getNext().getData().getNo()==node.getData().getNo()){
  40. System.out.println("存在相同编号的节点"+temp.getNext());
  41. break;
  42. }else if(temp.getNext().getData().getNo()>node.getData().getNo()){
  43. node.setNext(temp.getNext());
  44. temp.setNext(node);
  45. break;
  46. }
  47. temp=temp.getNext();
  48. }
  49. }
  50. //通过编号修改昵称
  51. public void updateNodeByNo(int no,String nickName){
  52. if(head.getNext()==null){
  53. System.out.println("链表为空");
  54. return;
  55. }
  56. Node temp=head;
  57. while(true){
  58. if(temp.getNext()==null){
  59. System.out.println("没有找到该编号的节点信息");
  60. break;
  61. }else if(temp.getNext().getData().getNo()==no){
  62. temp.getNext().getData().setNickName(nickName);
  63. break;
  64. }
  65. temp=temp.getNext();
  66. }
  67. }
  68. //通过编号删除节点
  69. public void deleteNode(int no){
  70. if(head.getNext()==null){
  71. System.out.println("链表为空");
  72. return;
  73. }
  74. Node temp=head;
  75. while(true){
  76. if(temp.getNext()==null){
  77. System.out.println("没有找到该编号的节点");
  78. break;
  79. }else if(temp.getNext().getData().getNo()==no) {
  80. temp.setNext(temp.getNext().getNext());
  81. break;
  82. }
  83. temp=temp.getNext();
  84. }
  85. }
  86. //获取链表的长度
  87. public int getLength(){
  88. if(head.getNext()==null){
  89. System.out.println("链表为空");
  90. System.exit(0);
  91. }
  92. Node temp=head;
  93. int result=0;
  94. while (true){
  95. if(temp.getNext()==null){
  96. break;
  97. }
  98. result++;
  99. temp=temp.getNext();
  100. }
  101. return result;
  102. }
  103. //打印链表节点
  104. public void printNode(){
  105. if(head.getNext()==null){
  106. System.out.println("链表为空");
  107. return;
  108. }
  109. Node temp=head;
  110. while (true){
  111. if(temp.getNext()==null){
  112. break;
  113. }
  114. temp=temp.getNext();
  115. System.out.println(temp);
  116. }
  117. }
  118. }

2、单链表面试题

单链表的常见面试题有如下:
1、求单链表中有效节点的个数
2、查找单链表中的倒数第k个结点 【新浪面试题】
3、单链表的反转【腾讯面试题,有点难度】
4、从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
5、合并两个有序的单链表,合并之后的链表依然有序【课后练习.】

2.1、新浪-找到单数第k个节点

  1. //查找链表倒数第index个元素
  2. public static Node getLastIndexNode(Node head, int index) {
  3. if (head.getNext() == null) {
  4. System.out.println("链表为空");
  5. return null;
  6. }
  7. SingleLinkedList singleLinkedList = new SingleLinkedList();
  8. singleLinkedList.setHead(head);
  9. int length = singleLinkedList.getLength();
  10. if (index <= 0 || index > length) return null;
  11. Node temp=head;
  12. int curNodeIndex=0;
  13. while(true){
  14. if(curNodeIndex==(length-index)+1){
  15. return temp;
  16. }
  17. temp=temp.getNext();
  18. curNodeIndex++;
  19. }
  20. }

2.2、腾讯-反转链表

  1. //反转链表,头插法
  2. public static void reverseLinkedList(Node head){
  3. if(head.getNext()==null){
  4. System.out.println("空链表");
  5. return;
  6. }
  7. if(head.getNext().getNext()==null){
  8. System.out.println("当前链表不需要进行反转");
  9. return;
  10. }
  11. Node curNode=head.getNext();
  12. Node nextNode=head.getNext().getNext();
  13. curNode.setNext(null);
  14. head.setNext(curNode);
  15. while(true){
  16. if(nextNode==null){
  17. return;
  18. }
  19. curNode=nextNode;
  20. nextNode=nextNode.getNext();
  21. curNode.setNext(head.getNext());
  22. head.setNext(curNode);
  23. }
  24. }

2.3、百度-从尾到头打印链表

  1. //从为到头打印链表
  2. public static void printNodeFromTailToHead(Node head){
  3. if(head.getNext()==null){
  4. System.out.println("空链表");
  5. return;
  6. }
  7. if(head.getNext().getNext()==null){
  8. System.out.println(head.getNext());
  9. return;
  10. }
  11. SingleLinkedList singleLinkedList = new SingleLinkedList();
  12. singleLinkedList.setHead(head);
  13. int length = singleLinkedList.getLength();
  14. Node[] nodes=new Node[length];
  15. int cur=length-1;
  16. Node temp=head;
  17. while(true){
  18. if (temp.getNext()==null){
  19. break;
  20. }
  21. temp=temp.getNext();
  22. nodes[cur--]=temp;
  23. }
  24. for (Node node : nodes) {
  25. System.out.println(node);
  26. }
  27. }

2.4、合并链表并保持有序

3、双向链表-增删改查

image.png

3.1、Node类

  1. public class Node {
  2. private Data data;
  3. private Node next;
  4. private Node pre;
  5. public Node() {
  6. }
  7. public Node(Data data) {
  8. this.data = data;
  9. }
  10. public Node(Data data,Node next) {
  11. this.data = data;
  12. this.next=next;
  13. }
  14. public Data getData() {
  15. return data;
  16. }
  17. public void setData(Data data) {
  18. this.data = data;
  19. }
  20. public Node getPre() {
  21. return pre;
  22. }
  23. public void setPre(Node pre) {
  24. this.pre = pre;
  25. }
  26. public Node getNext() {
  27. return next;
  28. }
  29. public void setNext(Node next) {
  30. this.next = next;
  31. }
  32. @Override
  33. public String toString() {
  34. return "Node{" +
  35. "data=" + data +
  36. '}';
  37. }
  38. }

3.2、DoubleLinkedList类-增删改查

  1. public class DoubleLinkedList {
  2. private Node head=new Node(null,null);
  3. private Node pre;
  4. private Node next;
  5. public Node getHead() {
  6. return head;
  7. }
  8. public void setHead(Node head) {
  9. this.head = head;
  10. }
  11. public Node getPre() {
  12. return pre;
  13. }
  14. public void setPre(Node pre) {
  15. this.pre = pre;
  16. }
  17. public Node getNext() {
  18. return next;
  19. }
  20. public void setNext(Node next) {
  21. this.next = next;
  22. }
  23. @Override
  24. public String toString() {
  25. return "DoubleLinkedList{" +
  26. "head=" + head +
  27. ", pre=" + pre +
  28. ", next=" + next +
  29. '}';
  30. }
  31. //插入节点,不允许相同节点且排序
  32. public void insertNode(Node node){
  33. if(node.getData()==null||node==null)return;
  34. if(head.getNext()==null){
  35. head.setNext(node);
  36. node.setPre(head);
  37. return;
  38. }
  39. Node temp=head;
  40. while(true){
  41. if(temp.getNext()==null){
  42. temp.setNext(node);
  43. node.setPre(temp);
  44. break;
  45. }else if(temp.getNext().getData().getNo()>node.getData().getNo()){
  46. node.setNext(temp.getNext());
  47. temp.getNext().setPre(node);
  48. temp.setNext(node);
  49. break;
  50. }else if(temp.getNext().getData().getNo()==node.getData().getNo()){
  51. System.out.println("存在相同编号的节点,无法添加"+temp.getNext());
  52. break;
  53. }
  54. temp=temp.getNext();
  55. }
  56. }
  57. //根据id更新昵称信息
  58. public void updateNodeByNo(int no,String nickName){
  59. if(head.getNext()==null){
  60. System.out.println("链表为空");
  61. return;
  62. }
  63. Node temp=head;
  64. while(true){
  65. if(temp.getNext()==null){
  66. System.out.println("没有找到该编号的节点信息");
  67. break;
  68. }else if(temp.getNext().getData().getNo()==no){
  69. temp.getNext().getData().setNickName(nickName);
  70. break;
  71. }
  72. temp=temp.getNext();
  73. }
  74. }
  75. //根据id删除节点
  76. public void deleteNode(int no){
  77. if(head.getNext()==null){
  78. System.out.println("链表为空");
  79. return;
  80. }
  81. Node temp=head;
  82. while(true){
  83. if(temp.getNext()==null){
  84. System.out.println("没有找到该编号的节点");
  85. break;
  86. }else if(temp.getNext().getData().getNo()==no) {
  87. temp.getNext().getNext().setPre(temp);
  88. temp.setNext(temp.getNext().getNext());
  89. break;
  90. }
  91. temp=temp.getNext();
  92. }
  93. }
  94. //打印双向链表
  95. public void printDoubleList(){
  96. if(head.getNext()==null){
  97. System.out.println("链表为空");
  98. return;
  99. }
  100. Node temp=head;
  101. while (true){
  102. if(temp.getNext()==null){
  103. break;
  104. }
  105. temp=temp.getNext();
  106. System.out.println(temp);
  107. }
  108. }
  109. }

4、环形链表-增删改查

image.png
Josephu 问题
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。