1-leetcode160. 相交链表
注意:√
- 这道题有思路做对很简单,自己思考的问题很有意思:为什么
while
循环里面不可以是p1.next== null
的时候直接跳转去headB
- 答案如下面图片所示,之前一直好奇这个代码怎么解决压根没有焦点的解决办法,现在这个问题解决了
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA;
ListNode p2 = headB;
while (p1 != p2){
if (p1 == null){
p1 = headB;
}else {
p1 = p1.next;
}
if (p2 == null){
p2 = headA;
}else {
p2 = p2.next;
}
}
return p1;
}
2-leetcode206. 反转链表
注意:×
- 递归和迭代的逻辑转换都需要注意
- 指针的转换容易出错,需要注意
- ListNode的代码可以放在内部类,也可以单独开一个class
// 1.递归
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
// 2.迭代
public ListNode reverseList1(ListNode head) {
ListNode prev = null;
ListNode current = head;
while (current != null){
ListNode next = current.next;
current.next = prev;
// 切换指针到新的状态
prev = current;
current = next;
}
return prev;
}
L补充-leetcode92. 反转链表 II
注意:×
- 绕死我了,核心的点在于反转前n个 和 反转整体链表 的
last
都是一个意思,都是为了把真正的头返回出来 - 第二个相同点在于
head.next.next = head;
- 不同的地方在于,反转整体链表的
head.next = null;
而反转前N个节点是head.next = successor;
- 不要在意可能提前让
head.next
发生变化,如果这个节点不是最后一个,在head.next.next = head;
就会被覆盖的
public ListNode reverseBetween(ListNode head, int left, int right) {
if (left == 1){
return reverseList(head,right);
}
ListNode last = reverseBetween(head.next, left-1, right-1);
head.next = last;
return head;
}
private ListNode successor = null;
private ListNode reverseList(ListNode head, int right) {
if (right == 1){
successor = head.next;
return head;
}
ListNode last = reverseList(head.next,right-1);
head.next.next = head;
head.next = successor;
return last;
}
3-leetcode234. 回文链表
注意:√
- 没有那么多花里胡哨的,直接转到
ArrayList
里面就完事了
public boolean isPalindrome(ListNode head) {
ListNode p = head;
ArrayList<Integer> arr = new ArrayList<>();
while (p != null){
arr.add(p.val);
p = p.next;
}
int n = arr.size();
int left = 0;
int right = n-1;
while (left<right){
if (arr.get(left).equals(arr.get(right))){
left++;
right--;
}else {
return false;
}
}
return true;
}
4-leetcode141. 环形链表
注意:×
- 这题不要轻视!!注意为了防止链表只有一个节点导致的错误,代码的判断流程最开始是
fast != null && fast.next != null
- 然后要注意先移动位置,这样是防止刚开始的时候
slow fast
的值都是head - 快慢指针赋值以后再进行比较
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if (slow == fast){
return true;
}
}
return false;
}
5-leetcode142. 环形链表 II
注意:√
- 知道思路很快就能写出来
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if (slow == fast){
// 这时候就发现有环了
slow = head;
while (fast != slow){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
6-leetcode21. 合并两个有序链表
注意:√
- 注意不只是一个虚拟头节点
dummy
,除了dummy
以外还需要一个指针在dummy
后面跑,让另外两个链表的值一点点的接进来 - 另外两个链表的结点是可以直接使用
list1
和list2
的,只是现在刷链表的题发现默认这些都是对应的头节点,移动遍历的时候都会选择再创建一个节点来完成功能
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1);
ListNode p = dummy;
ListNode p1 = list1, p2 = list2;
while (p1 != null && p2!= null){
if (p1.val >= p2.val){
p.next = p2;
p = p.next;
p2 = p2.next;
}else {
p.next = p1;
p = p.next;
p1 = p1.next;
}
}
if (p1 == null){
p.next = p2;
}
if (p2 == null){
p.next = p1;
}
return dummy.next;
}
7-leetcode2. 两数相加
注意:×
- 思路其实好理解,要注意里面对进位的处理
- 注意
while (p1 != null || p2 != null || carry > 0)
,注意是 或 判断
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 虚拟头节点
ListNode dummy = new ListNode(-1);
// 三个指针
ListNode p1 = l1, p2 = l2, p = dummy;
// 记录进位
int carry = 0;
int val = 0;
while (p1 != null || p2 != null || carry > 0) {
val = carry;
if (p1 != null) {
val = val + p1.val;
p1 = p1.next;
}
if (p2 != null) {
val = val + p2.val;
p2 = p2.next;
}
carry = val / 10;
val = val % 10;
ListNode cur = new ListNode(val);
p.next = cur;
p = p.next;
}
return dummy.next;
}
8-leetcode19. 删除链表的倒数第 N 个结点
注意:×
- 知道思路也写错了,因为没法很好的处理删除第一个节点的事情,例如
[1] 1
和[1,2] 2
类似这种删除第一个点的操作 - 解决办法是用一个虚拟节点指向头节点,然后
slow
节点指向的是虚拟节点,这样后面判断的时候直接判断fast != null
,这样删除slow后面的节点就可以去除首个节点被删除造成的问题
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
ListNode slow = dummy, fast = head;
for (int i = 0; i < n; i++) {
fast = fast.next;
}
while (fast != null){
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummy.next;
}
9-leetcode24. 两两交换链表中的节点
注意:×
- 看完思路以后隔了4小时才写,写对了
- 跟反转链表的操作很相似,递归的思路
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode pre = head, cur = head.next, next = head.next.next;
cur.next = pre;
pre.next = swapPairs(next);
return cur;
}
10-leetcode25. K 个一组翻转链表
注意:×
- 注意
reverse
函数中是判断cur != b
- 注意
reverseKGroup
的顺序,先拿到reverse(head, b)
,然后再改head.next = reverseKGroup(b, k)
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null){
return null;
}
ListNode b = head;
for (int i = 0; i < k; i++) {
if (b != null){
b = b.next;
}else {
return head;
}
}
ListNode reverseHead = reverse(head, b);
head.next = reverseKGroup(b, k);
return reverseHead;
}
private ListNode reverse(ListNode head, ListNode b){
ListNode pre = null, cur = head, next = head;
while (cur != b){
next = cur.next;
cur.next = pre;
// 移动位置
pre = cur;
cur = next;
}
return pre;
}
11-leetcode138. 随机链表的复制
注意:×
- 同样是一道刷了就不会忘的类型
- 对于数据结构复制,甭管他怎么变,你就记住最简单的方式:一个哈希表 + 两次遍历。
- 第一次遍历专门克隆节点,借助哈希表把原始节点和克隆节点的映射存储起来;第二次专门组装节点,照着原数据结构的样子,把克隆节点的指针组装起来。
public Node copyRandomList(Node head) {
HashMap<Node, Node> hashMap = new HashMap<>();
for (Node p = head; p != null; p = p.next){
if (!hashMap.containsKey(p)){
hashMap.put(p, new Node(p.val));
}
}
for (Node p = head; p != null; p = p.next){
if (p.next != null){
hashMap.get(p).next = hashMap.get(p.next);
}
if (p.random != null){
hashMap.get(p).random = hashMap.get(p.random);
}
}
return hashMap.get(head);
}
12-leetcode148. 排序链表
注意:×√
- 看了一下递归的写法,结合
合并两个有序链表
一趟过 - 注意一开始就要给
ListNode fast = head.next.next;
- 注意找中点的while循环是
while (fast != null && fast.next !=null)
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode slow = head;
ListNode fast = head.next.next;
while (fast != null && fast.next !=null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode nextHead = slow.next;
slow.next = null;
ListNode left = sortList(head);
ListNode right = sortList(nextHead);
ListNode dummy = new ListNode(-1);
// 三个活动的指针
ListNode p = dummy, p1 = left, p2 = right;
while (p1 != null && p2 != null){
if (p1.val <= p2.val){
p.next = p1;
// 移动
p = p.next;
p1 = p1.next;
}else {
p.next = p2;
// 移动
p = p.next;
p2 = p2.next;
}
}
if (p1 == null){
p.next = p2;
}
if (p2 == null){
p.next = p1;
}
return dummy.next;
}
13-leetcode23. 合并 K 个升序链表
注意:×
PriorityQueue
的使用方法,注意(a,b)是默认的a小b大- 一定要注意题目中给的
lists
结构,在后面while
循环里面PriorityQueue
取出来的元素要判断有没有后续的节点,有的话要加入道PriorityQueue
中
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0){
return null;
}
PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>((a, b) -> a.val - b.val);
ListNode dummy = new ListNode(-1);
ListNode p = dummy;
for (ListNode list : lists) {
if (list != null){
priorityQueue.add(list);
}
}
while (!priorityQueue.isEmpty()){
ListNode node = priorityQueue.poll();
p.next = node;
if (node.next != null){
priorityQueue.add(node.next);
}
p = p.next;
}
return dummy.next;
}
14-leetcode146. LRU 缓存
注意:×
- 第一次写LRU,确实蛮有意思的
- 注意
makeRecently(key)
是自己写的 - 注意
put
函数的逻辑(如下图所示,来自Labuladong的算法小抄-已购买), 一定要注意这里是两条路线!!!!,所以如果LinkedHashMap
里面含有这个key
的话,要注意return'
class LRUCache {
int cap;
LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<Integer, Integer>();
public LRUCache(int capacity) {
this.cap = capacity;
}
public int get(int key) {
if (!cache.containsKey(key)){
return -1;
}
makeRecently(key);
return cache.get(key);
}
private void makeRecently(int key) {
if (!cache.containsKey(key)){
return;
}
Integer val = cache.get(key);
cache.remove(key);
cache.put(key,val);
}
public void put(int key, int value) {
if (cache.containsKey(key)){
makeRecently(key);
cache.put(key, value);
return;
}
if (cache.size() >= cap){
Integer deleteKey = cache.keySet().iterator().next();
cache.remove(deleteKey);
}
cache.put(key, value);
}
}