春招笔试手撕链表题没有做出来,我练我练我使劲练
[21. 合并两个有序链表] 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
方法一:迭代法
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode head = new ListNode(); ListNode res = head; while(list1 != null && list2 != null) { if(list1.val > list2.val) { res.next = list2; list2 = list2.next; } else{ res.next = list1; list1 = list1.next; } res = res.next; } // list1或list2可能没合并完 res.next = list1 != null ? list1 : list2; return head.next; } }
方法二:递归
class Solution { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null) { return l2; } else if (l2 == null) { return l1; } else if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l1, l2.next); return l2; } } }
解题思路:
由于两个链表都是有序的,因此可以新建一个链表来存放合并后的结果,通过依次对比两个链表的值,将较小的值直接加入新建的链表。迭代条件为list1或list2当前不为空。注意在跳出循环后判断list1或list2是否合并完。
复杂度分析:
- 时间复杂度: O(n+m),其中 n 和 m分别为两个链表的长度
- 空间复杂度:迭代O(1),只需要常数的空间存放若干变量。递归O(n+m)
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode detectCycle(ListNode head) { ListNode l1 = head; ListNode l2 = head; // 如果是换,则一定会相遇 while(l2 != null && l2.next != null) { l1 = l1.next; l2 = l2.next.next; if(l1 == l2) { // 让慢指针回到原点出发,快指针再相遇的点出发,两指针再次相遇的点就是入环的第一个节点 l1 = head; while(l1 != l2) { l1 = l1.next; l2 = l2.next; } return l1; } } return null; } }
解题思路:
代码随想录这篇讲解很好 . - 力扣(LeetCode)
这一次独立完成,主要还是要画图分析出快慢指针间的关系,总结来说,定义慢指针一次走一步,快指针一次走两步,如果链表是环形的,则两者一定会相遇;若链表不是环形的,则一定存在慢指针slow = null 或 slow.next = null。若判断链表是环形的,通过公式可以推断出,若将慢指针放回原点出发,快指针放在相遇点出发,步长为一步,它们再次相遇的点即为环形链表入口。
复杂度分析:
- 时间复杂度: O(n),其中 n 表示链表的长度
- 空间复杂度:O(1)
笨方法:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { // 遍历链表,获取长度 int len = 0; ListNode dummy = new ListNode(0); dummy.next = head; ListNode temp1 = head; while(temp1 != null) { len++; temp1 = temp1.next; } // 删除第正数len-n+1个 temp1 = dummy; for(int i = 0; i < len - n; i++) { temp1 = temp1.next; } temp1.next = temp1.next.next != null ? temp1.next.next : null; return dummy.next; } }
解题思路:
先遍历链表获取链表长度要删除的数的正索引,然后顺序遍历删除。
复杂度分析:
- 时间复杂度: O(n),其中 n 表示链表的长度
- 空间复杂度:O(1)
方法二:快慢指针
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNode pre = new ListNode(0); pre.next = head; ListNode start = pre, end = pre; while(n != 0) { start = start.next; n--; } while(start.next != null) { start = start.next; end = end.next; } end.next = end.next.next; return pre.next; } }
解题思路:. - 力扣(LeetCode)