2021.01.03

今日题目:给你一个链表和一个特定值 x ,请你对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。
image.png
自己的思路:
自己的菜鸡代码🈶🈚

2021.01.04

今日题目:从尾到头打印单链表【简单】
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
image.png

  • 思路:
    1. 法一:因为要用数组返回每个结点的值,所以先获取整个单链表的长度( 时间复杂度为O(n) ),然后从头遍历单链表,同时从尾到头在数组中存放遍历结果,这里利用的是数组连续存放的特性。(这个算是暴力法了吧)

剑指中提到的另外两种解法——有利用题干中从头到尾遍历和从尾到头输出的特性。

  1. 法二:链表只能从头到尾遍历,而要求的是从尾到头输出数据,符合栈的特性(后进先出),故可以用一个栈保存遍历的结果,然后再输出结果。
  2. 法三:法二中提到用栈来解决,而由栈联想到递归,故也可以用递归进行解决。递归的代码很简单,但是若是链表的长度过长,则会导致函数递归调用的层级很深,从而可能导致函数调用栈溢出。(不提倡使用)
  • 自己的菜鸡代码: ```java // 法一的代码 /**
    • Definition for singly-linked list.
    • public class ListNode {
    • int val;
    • ListNode next;
    • ListNode(int x) { val = x; }
    • } */ class Solution { public int[] reversePrint(ListNode head) {
      1. ListNode p = head;
      2. int length = 0;
      3. while(p != null){
      4. ++length;
      5. p = p.next;
      6. }
      7. int result[] = new int[length];
      8. p = head;
      9. for(int i = length-1; i >= 0; i--){
      10. result[i] = p.val;
      11. p = p.next;
      12. }
      13. return result;
      } }

// 法三的代码 class Solution { public int[] reversePrint(ListNode head) { if(head != null){ if(head.next != null){ reversePrint(head.next); } System.out.println(head.val); } } }

  1. 运行结果:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609728345205-324e93e5-87bf-445c-9746-9a36fdffc293.png#align=left&display=inline&height=122&margin=%5Bobject%20Object%5D&name=image.png&originHeight=150&originWidth=752&size=8477&status=done&style=none&width=614)
  2. <a name="oKgok"></a>
  3. ### 2021.01.04
  4. 今日题目:链表中倒数第k个结点【简单】<br />输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是123456。这个链表的倒数第3个节点是值为4的节点。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609728542467-1a481369-21f7-475f-b6a0-f04d9effff7e.png#align=left&display=inline&height=167&margin=%5Bobject%20Object%5D&name=image.png&originHeight=167&originWidth=663&size=7282&status=done&style=none&width=663)
  5. - 思路(双指针):使用两个临时变量temptag,首先两个同时指向头结点,设定一个计数器,然后temp向后遍历至第k个结点停止遍历。查看计数器的值,若计数器的值大小于k则表示k的值大于链表的长度,为无效值;若此时计数器的值等于k,则temptag同时向后遍历,若temp指向最后一个结点,则表示tag所指结点即为倒数第k个结点。
  6. - 自己的菜鸡代码:
  7. ```java
  8. class Solution {
  9. public ListNode getKthFromEnd(ListNode head, int k) {
  10. ListNode temp,tag;
  11. tag = temp = head;
  12. int count = 1;
  13. for(; count < k && temp.next != null; count++,temp = temp.next) ;
  14. if(count < k) return null;// k大于链表长度,为无效值
  15. System.out.println(temp.val);
  16. while(temp.next != null){
  17. temp = temp.next;
  18. tag = tag.next;
  19. }
  20. return tag;
  21. }
  22. }

运行结果:
image.png

2021.01.06 合并两个排序的链表

今日题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
image.png
提示:0 <= 链表长度 <= 1000

  • 思路:因为L1和L2都是不带头节点的单链表,所以先判断两个单链表是否都不为空,在都不为空的情况下,比较两个头结点的大小,选择值较小的结点作为新链表的头结点(例如 L1.val 较小),同时用一个head指针指向头结点,用一个尾指针rear记录新链表的最后一个结点,便于插入,使L1指向下一个结点,然后以L1不为空且L2不为空为循环条件,比较L1与L2值的大小,rear结点的next值指向较小值的结点,该较小值结点要从旧链表中剥离。最后若L1单链表中还有值,直接把剩下部分加入新链表的末尾;若L2单链表中还有值,则直接把剩下的部分加入新链表的末尾。时间复杂度为 O(min{n,m});
  • 自己的菜鸡代码:

    1. /**
    2. * Definition for singly-linked list.
    3. * public class ListNode {
    4. * int val;
    5. * ListNode next;
    6. * ListNode(int x) { val = x; }
    7. * }
    8. */
    9. class Solution {
    10. public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    11. if(l1 == null) return l2;
    12. if(l2 == null) return l1;
    13. ListNode rear, head;
    14. if(l1.val < l2.val) {
    15. rear = l1;
    16. l1 = l1.next;
    17. }else{
    18. rear = l2;
    19. l2 = l2.next;
    20. }
    21. head = rear;
    22. rear.next = null;
    23. while(l1 != null && l2 != null){
    24. if(l1.val < l2.val){
    25. rear.next = l1;
    26. l1 = l1.next;
    27. }else{
    28. rear.next = l2;
    29. l2 = l2.next;
    30. }
    31. rear = rear.next;
    32. }
    33. if(l1 != null){
    34. rear.next = l1;
    35. l1 = l1.next;
    36. }else{
    37. rear.next = l2;
    38. l2 = l2.next;
    39. }
    40. return head;
    41. }
    42. }
  • 运行结果:

image.png

2021.01.06 两个链表的第一个公共结点

今日题目:输入两个链表,找出它们的第一个公共节点。
如下面的两个链表
链表 - 图6
在节点 c1 开始相交。
image.pngimage.png
image.png
注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

  • 思路:

    1. 法一:暴力法。遍历一个单链表A,然后在B中逐个查找是否有公共结点。时间复杂度为O(n*m);空间复杂度为O(1);
    2. 法二:使用两个栈,然后保存遍历的结果,判断栈顶元素的值是否相等,若是不等直接返回null表示没有交点,若是相等,直至找到不相等的结点。时间复杂度为O(n);空间复杂度为O(n);以空间换时间。(这个方法也蛮恶心的)
    3. 法三:(努力找寻时间复杂度为O(n),空间复杂度为O(1)的算法)看评论区大佬的思路,用双指针ptrA与ptrB分别指向两个链表的头结点,同时向后遍历,若ptrA或ptrB遍历结束,则继续从另一条链表的头结点出发继续遍历,两个指针会在两个链表第一个公共结点处相遇。时间复杂度为O(m+n),空间复杂度为O(1)。大佬示意图如下:

链表 - 图10链表 - 图11链表 - 图12
链表 - 图13链表 - 图14链表 - 图15
链表 - 图16链表 - 图17链表 - 图18

  • 自己的菜鸡代码: ```java /**
    • Definition for singly-linked list.
    • public class ListNode {
    • int val;
    • ListNode next;
    • ListNode(int x) {
    • val = x;
    • next = null;
    • }
    • } */

// 法一:暴力美学 public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if(headA == null || headB == null) return null; ListNode ptrB; for(ListNode ptrA = headA; ptrA != null; ptrA = ptrA.next){ for(ptrB = headB; ptrB != null; ptrB = ptrB.next){ if(ptrB == ptrA) return ptrA; } } return null; } }

public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { // 法二(也很恶心) if(headA == null || headB == null) return null; ListNode ptr1 = headA, ptr2 = headB; Stack stackA = new Stack(); Stack stackB = new Stack(); while(ptr1 != null){ stackA.push(ptr1); ptr1 = ptr1.next; } while(ptr2 != null){ stackB.push(ptr2); ptr2 = ptr2.next; } while(!stackA.isEmpty() && !stackB.isEmpty() && stackA.peek() == stackB.peek()){ ptr1 = stackA.peek(); ptr2 = stackB.peek(); stackA.pop(); stackB.pop(); }

  1. if(ptr1 != ptr2) return null;
  2. return ptr1;
  3. }

}

// 法三,太强了 public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { // 法三(震撼我妈) if(headA == null || headB == null) return null; ListNode ptrA = headA, ptrB = headB; while(ptrA != ptrB){ ptrA = ptrA != null? ptrA.next : headB; ptrB = ptrB != null? ptrB.next : headA; } return ptrA; } }

  1. - 运行结果:
  2. 法一(左,惨不忍睹):<br />法二(右,空间复杂度很不行)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609924519261-e162fbbe-ce04-4513-b3ab-99b13d5f0c80.png#align=left&display=inline&height=335&margin=%5Bobject%20Object%5D&name=image.png&originHeight=460&originWidth=631&size=28872&status=done&style=none&width=460)![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609926130130-0577d1d4-957d-43b0-9562-ad6629e307a1.png#align=left&display=inline&height=332&margin=%5Bobject%20Object%5D&name=image.png&originHeight=443&originWidth=648&size=28350&status=done&style=none&width=486)<br />法三(这就是算法的魔力吗,祝天下有情人终成眷属):<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609987216001-0d655b59-5be5-460a-8177-8709fd20fc92.png#align=left&display=inline&height=340&margin=%5Bobject%20Object%5D&name=image.png&originHeight=452&originWidth=633&size=28561&status=done&style=none&width=476)
  3. <a name="XheIW"></a>
  4. ### 2021.01.07
  5. 今日题目:反转链表<br />定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1584832/1609989101342-b312c621-5cfe-4176-8651-4c655bfb9818.png#align=left&display=inline&height=119&margin=%5Bobject%20Object%5D&name=image.png&originHeight=142&originWidth=622&size=5503&status=done&style=none&width=523)<br />提示:0 <= 节点个数 <= 5000
  6. - 思路:
  7. 1. 法一:遍历一遍链表,同时采用头插法重建链表,遍历结束即成功反转该链表。时间复杂度O(n),空间复杂度O(1)。
  8. 1. 法二:看到评论区有大佬说可以用递归的方式处理(zhwm)。假设链表的其余部分已经反转,那么要如何反转前面的部分。假设列表为
  9. n →…→n nn→…→n →∅<br />若从节点 nn已经被反转,而我们正处于nkn→…→nnn←…←n<br />我们希望n的下一个节点指向n。所以n.next.next=n要小心的是n的下一个必须指向∅ 。<br />如果忽略了这一点,链表中可能会产生循环。如果使用大小为 2 的链表测试代码,则可能会捕获此错误。时间复杂度O(n),空间复杂度O(n)。<br />递归的方式很不好理解。(偷大佬的示意图)(这里再加个自己理解的图)
  10. ![](https://cdn.nlark.com/yuque/0/2021/gif/1584832/1610003589454-0225763b-2ede-466d-bee9-23ba85940ef8.gif#align=left&display=inline&height=480&margin=%5Bobject%20Object%5D&originHeight=480&originWidth=853&size=0&status=done&style=none&width=853)
  11. 1. 法三:妖魔化的双指针。时间复杂度O(n),空间复杂度O(1)。
  12. - 自己的菜鸡代码:
  13. ```java
  14. /**
  15. * Definition for singly-linked list.
  16. * public class ListNode {
  17. * int val;
  18. * ListNode next;
  19. * ListNode(int x) { val = x; }
  20. * }
  21. */
  22. // 法一
  23. class Solution {
  24. public ListNode reverseList(ListNode head) {
  25. if(head == null) return head;
  26. ListNode p = head.next, q;
  27. head.next = null;
  28. while(p != null){
  29. q = p.next;
  30. p.next = head;
  31. head = p;
  32. p = q;
  33. }
  34. return head;
  35. }
  36. }
  37. // 法二(递归实现,不好理解)
  38. class Solution {
  39. public ListNode reverseList(ListNode head) {
  40. // 前一个判断单链表是否为空,后一个判断是否已经递归到最后一个结点
  41. if(head == null || head.next == null) return head;
  42. ListNode ret = reverseList(head.next);
  43. head.next.next = head;
  44. head.next = null;
  45. return ret;
  46. }
  47. }
  48. // 法三(妖魔化双指针即不用头插法的方式)
  49. class Solution {
  50. public ListNode reverseList(ListNode head) {
  51. // 不同的双指针方法
  52. if(head == null) return null;
  53. ListNode p = head, q;
  54. while(head.next != null){
  55. q = head.next.next;
  56. head.next.next = p;
  57. p = head.next;
  58. head.next = q;
  59. }
  60. return p;
  61. }
  62. }
  • 运行结果:

法一、法二、法三
image.pngimage.png

2021.01.07

今日题目:删除链表的结点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
image.png
说明:题目保证链表中节点的值互不相同,若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点。

  • 思路:首先判断链表是否为空。在不为空的情况下,判断头结点是否为待删除结点,若是则直接返回 head.next ,否则遍历单链表,直到找到待删除结点,将其从单链表中剥离即可。
  • 自己的菜鸡代码:

    1. /**
    2. * Definition for singly-linked list.
    3. * public class ListNode {
    4. * int val;
    5. * ListNode next;
    6. * ListNode(int x) { val = x; }
    7. * }
    8. */
    9. class Solution {
    10. public ListNode deleteNode(ListNode head, int val) {
    11. // 要注意删除的值是否为头结点的情况
    12. if(head == null) return null;
    13. if(head.val == val) return head.next;
    14. ListNode p = head;
    15. while(p.next != null){
    16. if(p.next.val == val){
    17. p.next = p.next.next;
    18. break;
    19. }
    20. p = p.next;
    21. }
    22. return head;
    23. }
    24. }
  • 运行结果:

image.png