算法笔记

新题

61. 旋转链表 - 力扣(LeetCode)

这题我一开始的思路完全按照了旋转数组去做,实际上没有必要

链表的结构不需要像数组一样,没有数组的覆盖问题,只需要找到几个关键的节点,断开,在连接就好

class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null) return head;
ListNode cur = head;
int size = 0;
ListNode rEnd = null;
while (cur != null) {
if (cur.next == null) {
rEnd = cur;
}
cur = cur.next;
size++;
}
k %= size;
if (k == 0) return head;

ListNode lHead = head;
ListNode lEnd = lHead;
ListNode rightH = head;
for (int i = 0; i < size - k; i++) {
rightH = rightH.next;
if (i == size - k - 2) {
lEnd = rightH;
}
}
lEnd.next = null;
head = rightH;
rEnd.next = lHead;
return head;
}
}

92. 反转链表 II - 力扣(LeetCode)

核心问题就是找到4个关键节点,要注意节点的初始化,4个关键节点,找相邻中考前的两个,可以避免循环过程中因为i的原因导致的初始化失败

class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (left == right) return head;
ListNode nHead = new ListNode(-1, head);
ListNode cur = nHead;
head = nHead;
ListNode midBeginLeft = head;
ListNode midBegin = head;
ListNode midEnd = head;
ListNode midEndRight = head;
// 对于要移动的链表问题,都应该添加一个额外的头节点
// 这样链表末尾的null和头节点,形成了天然的slave
for (int i = 0; i < right; i++) {
cur = cur.next;
// 要借助链表连接的特性,而不是直接根据i的取值取获取,会跳过很大麻烦的边界
if (i == left - 2) {
midBeginLeft = cur;
} else if (i == right - 1) {
midEnd = cur;
}
}
midBegin = midBeginLeft.next;
midEndRight = midEnd.next;
ListNode pre = null;
cur = midBegin;
while (cur != null && cur != midEndRight) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
midBeginLeft.next = midEnd;
midBegin.next = midEndRight;
return head.next;
}
}

143. 重排链表 - 力扣(LeetCode)

比较暴力的做法,直接录下所有的节点,然后依次处理
class Solution {
public void reorderList(ListNode head) {
Map<Integer, ListNode> map = new HashMap<>();
int index = 0;
ListNode cur = head;
while (cur != null) {
map.put(index++, cur);
cur = cur.next;
}
int size = index;
int n = size - 1;
ListNode nHead = new ListNode(-1, head);
head = nHead;
cur = head;
for (int i = 0; i < size / 2; i++) {
cur.next = map.get(i);
cur.next.next = map.get(n - i);
cur = cur.next.next;
}
if (size % 2 != 0) {
cur.next = map.get(size / 2);
cur.next.next = null;
} else {
cur.next = null;
}
}
}
暴力做法优化 双指针求中间节点,反转后半段,合并两个链表
class Solution {
public void reorderList(ListNode head) {
ListNode mid = getMidAndSlice(head);
ListNode l1 = head;
ListNode l2 = mid;
l2 = reverse(l2);
mergeTwoList(l1, l2);

}
// 这里记得区分节点个数的奇偶,同时需要从中间截断
public ListNode getMidAndSlice(ListNode head) {
ListNode pre = null;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
if (fast == null) {
pre.next = null;
return slow;

} else {
pre = slow;
slow = slow.next;
pre.next = null;
return slow;
}
}

public ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}

public ListNode mergeTwoList(ListNode l1, ListNode l2) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while (l1 != null && l2 != null) {
cur.next = l1;
l1 = l1.next;
cur = cur.next;
cur.next = l2;
l2 = l2.next;
cur = cur.next;
}
// 按照getMid的截断法,l1.size >= l2.size()
while (l1 != null) {
cur.next = l1;
l1 = l1.next;
}
return head.next;
}
}

100. 相同的树 - 力扣(LeetCode)

class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
if (p.val != q.val) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}

103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)

这题不错,好好记一记

class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ans = new ArrayList<>();
if (root == null) return ans;
Deque<TreeNode> queue = new ArrayDeque<>();
queue.offerFirst(root);
boolean flag = true; // true 从左往右
while (!queue.isEmpty()) {
List<Integer> temp = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
if (flag) {
TreeNode cur = queue.pollLast();
if (cur.left != null) {
queue.offerFirst(cur.left);
}
if (cur.right != null) {
queue.offerFirst(cur.right);
}
temp.add(cur.val);
} else {
// 从右边往左,可以把它理解为与从左往右所有的操作都相反
TreeNode cur = queue.pollFirst();
if (cur.right != null) {
queue.offerLast(cur.right);
}
if (cur.left != null) {
queue.offerLast(cur.left);
}
temp.add(cur.val);
}
}
ans.add(new ArrayList<>(temp));
flag = !flag;
}
return ans;
}
}

旧题

2. 两数相加 - 力扣(LeetCode)

没什么好说的,最基本的模板题,注意循环的条件,注意addition的使用


class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int addition = 0;
ListNode head = new ListNode(-1);
ListNode cur = head;
while (l1 != null || l2 != null) {
if (l1 != null && l2 != null) {
int sum = (l1.val + l2.val + addition) % 10;
addition = (l1.val + l2.val + addition) / 10;
cur.next = new ListNode(sum);
l1 = l1.next;
l2 = l2.next;
}
else if (l1 == null && l2 != null) {
int sum = (l2.val + addition) % 10;
addition = (l2.val + addition) / 10;
cur.next = new ListNode(sum);
l2 = l2.next;

}
else if (l1 != null && l2 == null) {
int sum = (l1.val + addition) % 10;
addition = (l1.val + addition) / 10;
cur.next = new ListNode(sum);
l1 = l1.next;
}
cur = cur.next;
}
if (addition != 0) {
cur.next = new ListNode(addition);
}
return head.next;
}
}

82. 删除排序链表中的重复元素 II - 力扣(LeetCode)

美团面试考过,注意循环的条件,注意如果存在相同的元素,那么就需要消除,如果不存在,就else到下一个,到下一个节点的操作不是公共的操作

class Solution {
public ListNode deleteDuplicates(ListNode head) {
head = new ListNode(-1, head);
ListNode cur = head;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val == cur.next.next.val) {
int val = cur.next.val;
while (cur.next != null && cur.next.val == val) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return head.next;
}
}

24. 两两交换链表中的节点 - 力扣(LeetCode)

熟能生巧

class Solution {
public ListNode swapPairs(ListNode head) {
ListNode nHead = new ListNode(-1);
nHead.next = head;
head = nHead;
ListNode pre = head;
ListNode cur = head.next;
while (cur != null && cur.next != null) {
ListNode next = cur.next;
cur.next = next.next;
next.next = cur;
pre.next = next;
pre = cur;
cur = cur.next;
}
return head.next;
}
}

160. 相交链表 - 力扣(LeetCode)

模板题
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode cur = headA;
int sizeA = 0;
while (cur != null) {
sizeA++;
cur = cur.next;
}
cur = headB;
int sizeB = 0;
while (cur != null) {
sizeB++;
cur = cur.next;
}
if (sizeA > sizeB) {
for (int i = 0; i < sizeA - sizeB; i++) {
headA = headA.next;
}
} else {
for (int i = 0; i < sizeB - sizeA; i++) {
headB = headB.next;
}
}
while (headA != null && headB != null) {
if (headA == headB) return headA;
headA = headA.next;
headB = headB.next;
}
return null;
}
}

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

// 笔试遇到过类似的
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (p == root || q == root || root == null) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null && right != null) return right;
else if (right == null && left != null) return left;
if (left != null && right != null) return root;
return null;

}
}

230. 二叉搜索树中第K小的元素 - 力扣(LeetCode)

BST的中序遍历是从小到大的
class Solution {
int count;
int ans;
public int kthSmallest(TreeNode root, int k) {
count = k;
find(root);
return ans;
}

public void find(TreeNode root) {
if (root == null) return;
find(root.left);
count--;
if (count == 0) {
ans = root.val;
return;
}
find(root.right);
}
}

124. 二叉树中的最大路径和 - 力扣(LeetCode)

class Solution {
int ans = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if (root == null) return 0;
getMaxPath(root);
return ans;

}
public int getMaxPath(TreeNode root) {
if (root == null) return 0;
// 这里会遍历整个树,由下至上
int leftPath = getMaxPath(root.left);
int rightPath = getMaxPath(root.right);
leftPath = Math.max(0, leftPath);
rightPath = Math.max(0, rightPath);
// 最大值是在遍历的过程中产生的,在由下自上遍历的过程中,每个节点都会作为根节点,得到经过该节点的路径最大值
ans = Math.max(ans, leftPath + rightPath + root.val);
return Math.max(leftPath, rightPath) + root.val;
}
}