1. 链表

2. 单链表和双链表的反转

2.1. 单链表

  1. // 单链表
  2. public static class Node {
  3. int value;
  4. Node next;
  5. public Node(int value) {
  6. this.value = value;
  7. }
  8. }
  9. // 反转单链表
  10. public static Node reversalNode(Node head) {
  11. Node next = null;
  12. Node pre = null;
  13. while (head != null) {
  14. next = head.next; // 缓存下一个节点
  15. head.next = pre; // 更改当前节点的下一节点为反向节点
  16. pre = head; // 缓存当前节点为下一节点
  17. head = next; // 更新新的当前节点
  18. }
  19. return pre;
  20. }
  21. public static void main(String[] args) {
  22. // 单链表反转
  23. Node n1 = new Node(1);
  24. Node n2 = new Node(2);
  25. Node n3 = new Node(3);
  26. n1.next = n2;
  27. n2.next = n3;
  28. System.out.println(n1.value);
  29. System.out.println(n1.next.value);
  30. System.out.println(n1.next.next.value);
  31. System.out.println("=============");
  32. n1 = reversalNode(n1);
  33. System.out.println(n1.value);
  34. System.out.println(n1.next.value);
  35. System.out.println(n1.next.next.value);
  36. System.out.println("=============");
  37. }

2.2. 双链表

  1. // 双链表
  2. public static class Node1 {
  3. int value;
  4. Node1 last;
  5. Node1 next;
  6. public Node1(int value) {
  7. this.value = value;
  8. }
  9. }
  10. // 双链表反转
  11. public static Node1 reversalNode1(Node1 head) {
  12. Node1 next = null;
  13. Node1 pre = null;
  14. while (head != null) {
  15. next = head.next; //记录下一个节点
  16. head.next = pre; //修改当前节点的右节点 为上次记录的head节点
  17. head.last = next; //修改当前节点的左节点
  18. pre = head; //记录当前节点
  19. head = next; //修改当前节点位置
  20. }
  21. return pre; //返回当前节点 即头节点
  22. }
  23. public static void main(String[] args) {
  24. // 双链表反转
  25. Node1 nn1 = new Node1(1);
  26. Node1 nn2 = new Node1(2);
  27. Node1 nn3 = new Node1(3);
  28. nn1.next = nn2;
  29. nn2.last = nn1;
  30. nn2.next = nn3;
  31. nn3.last = nn2;
  32. System.out.println(nn1.value);
  33. System.out.println(nn1.next.value);
  34. System.out.println(nn1.next.next.value);
  35. nn1 = reversalNode1(nn1);
  36. System.out.println("=============");
  37. System.out.println(nn1.value);
  38. System.out.println(nn1.next.value);
  39. System.out.println(nn1.next.next.value);
  40. }

3. 单链表实现栈和队列

3.1. 队列

  1. public static class Node<V> {
  2. V value;
  3. Node<V> next;
  4. public Node(V value) {
  5. this.value = value;
  6. }
  7. }
  8. // 队列
  9. public static class MyQueue<V> {
  10. Node<V> head;
  11. Node<V> tail;
  12. int size;
  13. public MyQueue() {
  14. head = null;
  15. tail = null;
  16. size = 0;
  17. }
  18. public boolean isEmpty() {
  19. return size == 0;
  20. }
  21. public int size() {
  22. return size;
  23. }
  24. // 添加元素到队列中
  25. public void offer(V value) {
  26. Node<V> v = new Node<V>(value);
  27. if (tail == null) { // tail为null 则当前队列中没有元素 直接添加
  28. head = v; // 头尾同时指向 新添加元素
  29. tail = v;
  30. } else { // 当前队列有元素
  31. tail.next = v; // 更新尾元素的下一跳为添加的新元素地址
  32. tail = v; // 然后再更新尾元素 为添加元素地址
  33. }
  34. size++; // 长度+1
  35. }
  36. // 从队列中删除元素
  37. public V poll() {
  38. V ans = null;
  39. if (head != null) {
  40. ans = head.value; // 获取头部元素值
  41. head = head.next; // 将头部指向下一个元素
  42. size--;
  43. }
  44. if (head == null) { // 边界情况 当头部当前为null时 尾部也无元素
  45. tail = null;
  46. }
  47. return ans;
  48. }
  49. // 查看队头元素
  50. public V peek() {
  51. V ans = null;
  52. if (head != null) {
  53. ans = head.value;
  54. }
  55. return ans;
  56. }
  57. }

在上面删除的操作 java中有自己的回收器 因此我们无需手动回收无用对象 只需要把此对象改成不可达(即无法通过任何途径访问到此对象,则jvm会自动释放该对象)

3.2. 栈

  1. public static class Node<V> {
  2. V value;
  3. Node<V> next;
  4. public Node(V value) {
  5. this.value = value;
  6. }
  7. }
  8. //栈
  9. public static class MyStack<V>{
  10. int size;
  11. Node<V> head;
  12. public MyStack() {
  13. head = null;
  14. size =0;
  15. }
  16. public boolean isEmpty() {
  17. return size ==0;
  18. }
  19. public int size() {
  20. return size;
  21. }
  22. //入栈
  23. public void pusu(V value) {
  24. Node<V> v = new Node<V>(value);
  25. if(head == null) { //栈中无元素
  26. head = v; //添加元素
  27. } else { //栈中有元素
  28. head.next = v; //压栈底
  29. head = v;//更新栈顶
  30. }
  31. size++;
  32. }
  33. //出栈
  34. public V pop() {
  35. V ans = null;
  36. if (head !=null) {
  37. ans = head.value; //获取栈顶值
  38. head = head.next; //更新栈顶元素为下个元素
  39. size--;
  40. }
  41. return ans;
  42. }
  43. //查询栈顶元素
  44. public V peek() {
  45. V ans = null;
  46. if(head !=null) {
  47. ans = head.value;
  48. }
  49. return ans;
  50. }
  51. }

4. 双链表实现双端队列

为什么无法使用单链表实现双端队列?

  1. 单链表支持从队头中增删查元素
  2. 单链表只支持从队尾增查元素,无法删除元素
    因为无法从当前队尾中快速查找到上一跳的地址,只能遍历一次单链表。无法做到O(1),因为删除操作要遍历一次链表时间复杂程度为O(n)
  3. 所以我们可以使用双链表可以完成O(1)的双端队列的增删查
  1. public static class Node<V> {
  2. V value;
  3. Node<V> fist;
  4. Node<V> last;
  5. public Node(V value) {
  6. this.value = value;
  7. fist = null; // 上一跳
  8. last = null; // 下一跳
  9. }
  10. }
  11. public static class DoubleQueue<V> {
  12. Node<V> head;
  13. Node<V> tail;
  14. int size;
  15. public DoubleQueue() {
  16. head = null;
  17. tail = null;
  18. size = 0;
  19. }
  20. public boolean isEmpty() {
  21. return size == 0;
  22. }
  23. public int size() {
  24. return size;
  25. }
  26. // 从队列头添加指定元素
  27. public void addFist(V value) {
  28. Node<V> v = new Node<V>(value);
  29. if (head == null) { // 当前队列为空 直接添加
  30. head = v;
  31. tail = v;
  32. } else { // 从队头添加
  33. head.fist = v; // 原队头上一跳 标记为当前元素
  34. v.last = head; // 当前元素 下一跳为旧队头
  35. head = v; // 队头为当前元素
  36. }
  37. size++;
  38. }
  39. // 从队列尾添加指定元素
  40. public void addLast(V value) {
  41. Node<V> v = new Node<V>(value);
  42. if (head == null) {
  43. head = v;
  44. tail = v;
  45. } else {
  46. tail.last = v; // 旧队尾下一跳为 添加元素
  47. v.fist = tail; // 添加元素的上一跳 为旧队尾
  48. tail = v; // 更新队尾
  49. }
  50. size++;
  51. }
  52. // 删除队头元素
  53. public V pollFirst() {
  54. V ans = null;
  55. if (head != null) {
  56. ans = head.value;
  57. size--;
  58. }
  59. if (head == tail) { // 边界情况 只有一个元素
  60. head = null;
  61. tail = null;
  62. } else {
  63. head = head.fist;
  64. head.fist = null; // 将head上一跳标记为null
  65. }
  66. return ans;
  67. }
  68. // 删除队尾元素
  69. public V pollLast() {
  70. V ans = null;
  71. if (head != null) { // 有元素
  72. ans = tail.value; // 取当前队尾出值
  73. size--;
  74. }
  75. if (tail == tail) { // 边界情况 只有一个元素
  76. head = null;
  77. tail = null;
  78. } else {
  79. tail = tail.fist; // 更新队尾
  80. tail.last = null; // 当tail不为null 才将tall下一跳标记为null
  81. }
  82. return ans;
  83. }
  84. // 查看队头
  85. public V peekFist() {
  86. V ans = null;
  87. if (head != null) {
  88. ans = head.value;
  89. }
  90. return ans;
  91. }
  92. // 查看队尾
  93. public V peekLast() {
  94. V ans = null;
  95. if (tail != null) {
  96. ans = tail.value;
  97. }
  98. return ans;
  99. }
  100. }

5. leecode 链表题

5.1. K 个一组翻转链表

  1. public class ListNode {
  2. int val;
  3. ListNode next;
  4. ListNode() {
  5. }
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. ListNode(int val, ListNode next) {
  10. this.val = val;
  11. this.next = next;
  12. }
  13. }
  14. public class Solution {
  15. public ListNode reverseKGroup(ListNode head, int k) {
  16. ListNode start = head; // 缓存当前链表头
  17. ListNode end = getKEnd(start, k); // 获取第一组链表尾
  18. if (end == null) { // 如果k个元素内没有链尾 说明第一组链表<=k个元素 无法进行反转 直接返回当前链表即可
  19. return head;
  20. }
  21. // 第一组满足k个元素进行反转
  22. head = end; // 将当前链表头指向链表尾的元素
  23. reverse(start, end); // 进行反转
  24. ListNode lastEnd = start; // 上一组节点尾
  25. while (lastEnd.next != null) {
  26. start = lastEnd.next; // 缓存当前组的链表头
  27. end = getKEnd(start, k); // 获得当前组的链表尾
  28. if (end == null) {
  29. return head;
  30. }
  31. reverse(start, end); // 满足k个 反转当前组
  32. lastEnd.next = end; // 将上组的链尾 指向 当前组反转后的链表头
  33. lastEnd = start; // 更新lastEnd为当前组的链尾
  34. }
  35. return head;
  36. }
  37. public ListNode getKEnd(ListNode pre, int k) {
  38. while (--k != 0 && pre != null) { // 返回当前组的最后一个元素(即新的链头)
  39. pre = pre.next;
  40. }
  41. return pre;
  42. }
  43. public void reverse(ListNode start, ListNode end) {
  44. end = end.next; // 将当前end向后移一位(即下一组的链头,下一组未反转)
  45. ListNode cur = start; // 当前元素
  46. ListNode pre = null; // 缓存上次的修改过元素
  47. ListNode next = null; // 下一跳元素
  48. while (cur != end) {
  49. next = cur.next; // 缓存下一跳
  50. cur.next = pre; // 指向上次修改后的元素
  51. pre = cur; // 更新修改后的元素
  52. cur = next; // 更新当前元素
  53. }
  54. start.next = end; // 将当前组链尾 指向下一组的链头
  55. }
  56. }

5.2. 两数相加

  1. public class ListNode {
  2. int val;
  3. ListNode next;
  4. ListNode() {
  5. }
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. ListNode(int val, ListNode next) {
  10. this.val = val;
  11. this.next = next;
  12. }
  13. }
  14. class Solution {
  15. public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
  16. ListNode l = getSize(l1) > getSize(l2) ? l1 : l2; // 最长的链表
  17. ListNode s = l == l1 ? l2 : l1; // 第二个链表 <= l的长度
  18. int temp = 0; // 进位标记 如果为1则需要进1 0则不进1
  19. int cou = 0; // 存储每次计算结果
  20. ListNode tl = l; // 缓存当前长节点 元素
  21. ListNode ts = s; // 缓存当前短节点 元素
  22. ListNode pre = null;
  23. // 处理短的链表
  24. while (ts != null) {
  25. cou = ts.val + tl.val + temp; // 相加结果
  26. temp = cou / 10; // 是否需要进1
  27. tl.val = cou % 10; // 存储到节点中
  28. pre = tl; // 缓存当前长的节点元素 因为最后进1 需要使用该缓存节点下一跳 指向新的节点
  29. // 为何要在长的和短的链表 中记录当前节点呢 因为有可能短和长的链表长度一致 短的跑完长也跑完 又需要进1 会发生空指针异常
  30. tl = tl.next; // 下一个节点
  31. ts = ts.next;
  32. }
  33. // 处理长的链表
  34. while (tl != null) {
  35. cou = tl.val + temp;
  36. temp = cou / 10;
  37. tl.val = cou % 10;
  38. pre = tl;
  39. tl = tl.next;
  40. }
  41. // 如果进位是1 则需要新创建节点
  42. if (temp != 0) {
  43. pre.next = new ListNode(1);
  44. }
  45. return l;
  46. }
  47. public int getSize(ListNode head) {
  48. int size = 0;
  49. while (head.next != null) {
  50. size++;
  51. head = head.next;
  52. }
  53. return size;
  54. }
  55. }

5.3. 合并两个有序链表

  1. public class ListNode {
  2. int val;
  3. ListNode next;
  4. ListNode() {
  5. }
  6. ListNode(int val) {
  7. this.val = val;
  8. }
  9. ListNode(int val, ListNode next) {
  10. this.val = val;
  11. this.next = next;
  12. }
  13. }
  14. class Solution {
  15. public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  16. if (list1 == null || list2 == null) { // 另外一个链表为空直接返回非空链表
  17. return list1 == null ? list2 : list1;
  18. }
  19. ListNode head = list1.val <= list2.val ? list1 : list2; // 头节点
  20. ListNode l = head.next; // 下一条
  21. ListNode s = head == list1 ? list2 : list1; // 另外一个链表
  22. ListNode pre = head;
  23. while (l != null && s != null) {
  24. if (l.val <= s.val) {
  25. pre.next = l;
  26. l = l.next;
  27. } else {
  28. pre.next = s;
  29. s = s.next;
  30. }
  31. pre = pre.next;
  32. }
  33. pre.next = l == null ? s : l;
  34. return head;
  35. }
  36. }

6. 单链表的相交节点

给定两个可能有环也可能无环的单链表,头节点head1和head2
请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交返回null
要求如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)

6.1. 如何判断单个链表是否有环

  1. 定义一个快指针步长为2,定义一个慢指针步长为1,两指针不断往下走除非遇到null(说明此链表无环)
  2. 两指针各走各的,直到两节点相遇,慢指针停下,快指针更变步长为1,并返回到head节点
  3. 两指针重新往下走,当两指针再次相遇则此为入环节点
  1. //找到链表入环头节点 如果不成环返回null
  2. public static Node getLoopNode(Node head) {
  3. if(head == null) {
  4. return null;
  5. }
  6. //定义一个快指针 步长为2
  7. Node fast = head.next.next;
  8. //定义一个慢指针 步长为1
  9. Node slow = head.next;
  10. //两指针相遇
  11. while(fast != slow) {
  12. if(fast.next.next == null || slow.next == null) {
  13. //其中一个指针到尾了 此链表不成环
  14. return null;
  15. }
  16. //往下走
  17. fast = head.next.next;
  18. slow = head.next;
  19. }
  20. //两指针相遇后 快指针返回原点 并且步长恢复为1
  21. fast = head;
  22. //此时两指针步长相等 两指针相遇时当前节点为 入环节点
  23. while(fast != slow) {
  24. fast = fast.next;
  25. slow = slow.next;
  26. }
  27. return fast; //返回任意一个指针都可以 此时两指针必定在同一节点上
  28. }

6.2. 两链表无环 获取两链表有相交头节点

  1. 判断两链表的最后一个节点是否相等,不相等则两链表无相交(各走各的),如相交必定尾节点相等
  2. 获取两个链表的各自长度,长的链表先让 head1.length - head2.length 步,然后两指针并行走,直到两节点相等,则此节点为相交节点
  1. // 两链表无环 返回两链表相交头节点 如无返回null
  2. public static Node noLoop(Node head1, Node head2) {
  3. if (head1 == null || head2 == null) {
  4. return null;
  5. }
  6. // 两节点各走各的末尾 并记录长度
  7. Node cur1 = head1;
  8. Node cur2 = head2;
  9. int n = 0; // 此为记录长度的
  10. while (cur1 != null) {
  11. cur1 = cur1.next;
  12. n++;
  13. }
  14. while (cur2 != null) {
  15. cur2 = cur2.next;
  16. n--;
  17. }
  18. if (cur1 != cur2) { // 两链表走到尾 查看两节点是否相等 不相等则无相交
  19. return null;
  20. }
  21. //
  22. cur1 = n > 0 ? head1 : head2; // 大于0则说明head1长 小于0则说明head2长
  23. cur2 = cur1 == head1 ? head2 : head1;
  24. n = Math.abs(n); // 变成绝对值 防止负数情况
  25. // 先让短链表 n步
  26. while (n != 0) {
  27. n--;
  28. cur1 = cur1.next;
  29. }
  30. // 两链表并行走 直到相遇
  31. while (cur1 != cur2) {
  32. cur1 = cur1.next;
  33. cur2 = cur2.next;
  34. }
  35. return cur1;
  36. }

6.3. 其中一个链表有环 另外一个链表无环 两链表不可能相交

此情况直接返回null

6.4. 两链表都有环 获取两链表相交头节点

此情况有三种分支

  1. 两链表没有相交节点,两个环各走各的

    1. 此分支下无相交节点 返回null
  2. 两链表相交节点在环之前,环为同一个(即loop1 == loop2)

    1. 此分支下,把入环节点视为尾结点去除掉,与两链表无环 获取两链表有相交头节点处理一致
  3. 两链表相交节点在环内部,但相交头节点有两个,此时返回head1或head2都对

    1. loop1 != loop2 ,loop1不断往下走,如果遇到自身则为分支1返回null,如果遇到loop2则为分支3,返回loop1即可
  1. // 两链表为环链表 返回两链表相交头节点 如无返回null
  2. public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
  3. Node cur1 = null;
  4. Node cur2 = null;
  5. //两相交节点 在环之前 先判断两环头是否相等
  6. if(loop1 == loop2) {
  7. // 两节点各走各的末尾 并记录长度
  8. cur1 = head1;
  9. cur2 = head2;
  10. int n = 0; // 此为记录长度的
  11. while (cur1 != loop1) {
  12. cur1 = cur1.next;
  13. n++;
  14. }
  15. while (cur2 != loop2) {
  16. cur2 = cur2.next;
  17. n--;
  18. }
  19. if (cur1 != cur2) { // 两链表走到尾 查看两节点是否相等 不相等则无相交
  20. return null;
  21. }
  22. //
  23. cur1 = n > 0 ? head1 : head2; // 大于0则说明head1长 小于0则说明head2长
  24. cur2 = cur1 == head1 ? head2 : head1;
  25. n = Math.abs(n); // 变成绝对值 防止负数情况
  26. // 先让短链表 n步
  27. while (n != 0) {
  28. n--;
  29. cur1 = cur1.next;
  30. }
  31. // 两链表并行走 直到相遇
  32. while (cur1 != cur2) {
  33. cur1 = cur1.next;
  34. cur2 = cur2.next;
  35. }
  36. return cur1;
  37. } else {
  38. //两链表 相交节点可能在环内 判断环内部是否有loop2(即链表2的入环头节点)
  39. cur1 = loop1.next;
  40. while(cur1 != loop1) {
  41. if(cur1 == loop2) {
  42. return loop1;
  43. }
  44. cur1 = cur1.next; //往下走
  45. }
  46. //环内走完 说明两环并不相交 返回null
  47. return null;
  48. }
  49. }

6.5. 完整代码

  1. public static class Node {
  2. public int value;
  3. public Node next;
  4. public Node(int data) {
  5. this.value = data;
  6. }
  7. }
  8. // 找到链表入环头节点 如果不成环返回null
  9. public static Node getLoopNode(Node head) {
  10. if (head == null) {
  11. return null;
  12. }
  13. // 定义一个快指针 步长为2
  14. Node fast = head.next.next;
  15. // 定义一个慢指针 步长为1
  16. Node slow = head.next;
  17. // 两指针相遇
  18. while (fast != slow) {
  19. if (fast.next == null || fast.next.next == null) {
  20. // 其中一个指针到尾了 此链表不成环
  21. return null;
  22. }
  23. // 往下走
  24. fast = fast.next.next;
  25. slow = slow.next;
  26. }
  27. // 两指针相遇后 快指针返回原点 并且步长恢复为1
  28. fast = head;
  29. // 此时两指针步长相等 两指针相遇时当前节点为 入环节点
  30. while (fast != slow) {
  31. fast = fast.next;
  32. slow = slow.next;
  33. }
  34. return fast; // 返回任意一个指针都可以 此时两指针必定在同一节点上
  35. }
  36. // 两链表无环 返回两链表相交头节点 如无返回null
  37. public static Node noLoop(Node head1, Node head2) {
  38. if (head1 == null || head2 == null) {
  39. return null;
  40. }
  41. // 两节点各走各的末尾 并记录长度
  42. Node cur1 = head1;
  43. Node cur2 = head2;
  44. int n = 0; // 此为记录长度的
  45. while (cur1 != null) {
  46. cur1 = cur1.next;
  47. n++;
  48. }
  49. while (cur2 != null) {
  50. cur2 = cur2.next;
  51. n--;
  52. }
  53. if (cur1 != cur2) { // 两链表走到尾 查看两节点是否相等 不相等则无相交
  54. return null;
  55. }
  56. //
  57. cur1 = n > 0 ? head1 : head2; // 大于0则说明head1长 小于0则说明head2长
  58. cur2 = cur1 == head1 ? head2 : head1;
  59. n = Math.abs(n); // 变成绝对值 防止负数情况
  60. // 先让短链表 n步
  61. while (n != 0) {
  62. n--;
  63. cur1 = cur1.next;
  64. }
  65. // 两链表并行走 直到相遇
  66. while (cur1 != cur2) {
  67. cur1 = cur1.next;
  68. cur2 = cur2.next;
  69. }
  70. return cur1;
  71. }
  72. // 两链表为环链表 返回两链表相交头节点 如无返回null
  73. public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
  74. Node cur1 = null;
  75. Node cur2 = null;
  76. //两相交节点 在环之前 先判断两环头是否相等
  77. if(loop1 == loop2) {
  78. // 两节点各走各的末尾 并记录长度
  79. cur1 = head1;
  80. cur2 = head2;
  81. int n = 0; // 此为记录长度的
  82. while (cur1 != loop1) {
  83. cur1 = cur1.next;
  84. n++;
  85. }
  86. while (cur2 != loop2) {
  87. cur2 = cur2.next;
  88. n--;
  89. }
  90. if (cur1 != cur2) { // 两链表走到尾 查看两节点是否相等 不相等则无相交
  91. return null;
  92. }
  93. //
  94. cur1 = n > 0 ? head1 : head2; // 大于0则说明head1长 小于0则说明head2长
  95. cur2 = cur1 == head1 ? head2 : head1;
  96. n = Math.abs(n); // 变成绝对值 防止负数情况
  97. // 先让短链表 n步
  98. while (n != 0) {
  99. n--;
  100. cur1 = cur1.next;
  101. }
  102. // 两链表并行走 直到相遇
  103. while (cur1 != cur2) {
  104. cur1 = cur1.next;
  105. cur2 = cur2.next;
  106. }
  107. return cur1;
  108. } else {
  109. //两链表 相交节点可能在环内 判断环内部是否有loop2(即链表2的入环头节点)
  110. cur1 = loop1.next;
  111. while(cur1 != loop1) {
  112. if(cur1 == loop2) {
  113. return loop1;
  114. }
  115. cur1 = cur1.next; //往下走
  116. }
  117. //环内走完 说明两环并不相交 返回null
  118. return null;
  119. }
  120. }
  121. public static Node getIntersectNode(Node head1, Node head2) {
  122. if(head1 == null || head2 == null ) {
  123. return null;
  124. }
  125. //找出两链表是否有环
  126. Node loop1 = getLoopNode(head1);
  127. Node loop2 = getLoopNode(head2);
  128. //两节点无环情况
  129. if(loop1 == null && loop2 == null) {
  130. return noLoop(head1,head2);
  131. }
  132. //两节点有环情况
  133. if(loop1 != null && loop2 !=null) {
  134. return bothLoop(head1, loop1, head2, loop2);
  135. }
  136. //两节点 其中一个有环情况 必定没有相交点
  137. return null;
  138. }
  139. public static void main(String[] args) {
  140. // 1->2->3->4->5->6->7->null
  141. Node head1 = new Node(1);
  142. head1.next = new Node(2);
  143. head1.next.next = new Node(3);
  144. head1.next.next.next = new Node(4);
  145. head1.next.next.next.next = new Node(5);
  146. head1.next.next.next.next.next = new Node(6);
  147. head1.next.next.next.next.next.next = new Node(7);
  148. // 0->9->8->6->7->null
  149. Node head2 = new Node(0);
  150. head2.next = new Node(9);
  151. head2.next.next = new Node(8);
  152. head2.next.next.next = head1.next.next.next.next.next; // 8->6
  153. System.out.println(getIntersectNode(head1, head2).value);
  154. // 1->2->3->4->5->6->7->4...
  155. head1 = new Node(1);
  156. head1.next = new Node(2);
  157. head1.next.next = new Node(3);
  158. head1.next.next.next = new Node(4);
  159. head1.next.next.next.next = new Node(5);
  160. head1.next.next.next.next.next = new Node(6);
  161. head1.next.next.next.next.next.next = new Node(7);
  162. head1.next.next.next.next.next.next = head1.next.next.next; // 7->4
  163. // 0->9->8->2...
  164. head2 = new Node(0);
  165. head2.next = new Node(9);
  166. head2.next.next = new Node(8);
  167. head2.next.next.next = head1.next; // 8->2
  168. System.out.println(getIntersectNode(head1, head2).value);
  169. // 0->9->8->6->4->5->6..
  170. head2 = new Node(0);
  171. head2.next = new Node(9);
  172. head2.next.next = new Node(8);
  173. head2.next.next.next = head1.next.next.next.next.next; // 8->6
  174. System.out.println(getIntersectNode(head1, head2).value);
  175. }