本文中的链表节点定义如下

  1. @Data
  2. class Node {
  3. private int data;
  4. private Node next;
  5. public Node(int data) {
  6. this.data = data;
  7. }
  8. // 省略 getter/setter 方法
  9. }

寻找单向链表倒数第N个节点信息

  1. 暴力法,通过判断剩余节点的长度判断所处的位置,通过下图可知,后续节点的个数+1 就是其倒数节点的索引位置

image.png

  1. @Test
  2. /* 寻找倒数第N个节点的信息 */
  3. public void testFindIndexFromLast() {
  4. // 初始化一个链表,其值为0->1->2->3 .... 7 -> 8 -> 9 这里因为篇幅问题暂略
  5. Node preLink = init();
  6. // 寻找倒数第3个节点,断言其Data = 7
  7. Node data = LinkUtils.findIndexFromLast(preLink, 3);
  8. assert Objects.equals(Objects.requireNonNull(data).getData(), 7);
  9. }
  10. /* 暴力法寻找倒数第N个节点 */
  11. public static Node findIndexFromLast(Node t, int index) {
  12. int length = length(t);
  13. if (index > length) {
  14. throw new RuntimeException("节点数据不足");
  15. }
  16. Node tmp = t;
  17. while (tmp.getNext() != null) {
  18. if (length(tmp) == index - 1) {
  19. return tmp;
  20. }
  21. tmp = tmp.getNext();
  22. }
  23. return null;
  24. }
  1. 双指针法

通过定义两个指针的方式,假设总链表长度为S,需要查询倒数第L节点的信息,那么A指针指向头结点,B指针指向第L个节点,然后A指针和B指针一直向后移动,直到B指针指向尾结点,那么B指针总共走了S-A步,同样A指针则走了S-L步,也就是说当B节点指向尾结点的时候,A的Next节点就是倒数第L个节点。

  1. @Test
  2. /* 寻找倒数第N个节点的信息 */
  3. public void testFindIndexFromLast() {
  4. // 初始化一个链表,其值为0->1->2->3 .... 7 -> 8 -> 9 这里因为篇幅问题暂略
  5. Node preLink = init();
  6. // 寻找倒数第3个节点,断言其Data = 7
  7. Node data = LinkUtils.findIndexFromLast(preLink, 3);
  8. assert Objects.equals(Objects.requireNonNull(data).getData(), 7);
  9. }
  10. public static Node findIndexFromLatestWithDoublePoint(Node t, int index) {
  11. int length = length(t);
  12. if (index > length) {
  13. throw new RuntimeException("");
  14. }
  15. Node pointNode1 = t;
  16. Node pointNode2 = t;
  17. while (index > 0) {
  18. index--;
  19. pointNode1 = pointNode1.getNext();
  20. }
  21. while (pointNode1.getNext() != null) {
  22. pointNode1 = pointNode1.getNext();
  23. pointNode2 = pointNode2.getNext();
  24. }
  25. return pointNode2.getNext();
  26. }

判断链表内是够有环

image.png

  1. Floyd环判定算法 又称之为龟兔算法,通过设定不同的移动速度来分别移动两个指针,当两个指针再次相遇的时候,则说明存在环状链表,否则则不存在。
    1. public static boolean hasRingLinked(Node t) {
    2. if (t == null || t.getNext() == null) {
    3. return false;
    4. }
    5. Node fPoint = t, lPoint = t;
    6. while (fPoint.getNext() != null && fPoint.getNext().getNext() != null) {
    7. fPoint = fPoint.getNext().getNext();
    8. lPoint = lPoint.getNext();
    9. if (fPoint == lPoint) {
    10. return true;
    11. }
    12. }
    13. return false;
    14. }

获取环状链表中环的长度

这其实是两个问题,分别是判断是否有环,但有环的时候,固定快节点,让慢节点一步一步走,每走一步直接加一,直到和快节点再次相遇。

  1. public static int getRingLength(Node t) {
  2. Node fPoint = t, lPoint = t;
  3. while (fPoint.getNext() != null && fPoint.getNext().getNext() != null) {
  4. fPoint = fPoint.getNext().getNext();
  5. lPoint = lPoint.getNext();
  6. if (fPoint == lPoint) {
  7. // 如果是环状链表,则移动慢节点直到和快节点再次相遇
  8. int ringLength = 1;
  9. lPoint = lPoint.getNext();
  10. while (lPoint != fPoint) {
  11. ringLength++;
  12. lPoint = lPoint.getNext();
  13. }
  14. return ringLength;
  15. }
  16. }
  17. return 0;
  18. }