简单记录一下做过的 LeetCode,不定期更新
3-无重复字符的最长子串
参考:二指针问题
#include <string.h>
int max(int a, int b) {
return a > b ? a : b;
}
int lengthOfLongestSubstring(char *s) {
bool vis[128] = {0};
int len = strlen(s);
int ans = 0;
for (int i = 0, j = 0; i < len; i++) {
while (j < len && !vis[s[j]]) {
vis[s[j]] = true;
j++;
}
ans = max(ans, j - i);
// abcdabc
if (len - i <= ans) {
break;
}
vis[s[i]] = false;
}
return ans;
}
5-最长回文子串
// 还有更优的算法
func longestPalindrome(s string) string {
if s == "" {
return s
}
maxL, maxR := 0, 0
for i := 0; i < len(s) - 1; i++ {
if l, r := test(s, i, i); r - l > maxR - maxL {
maxL, maxR = l, r
}
if l, r := test(s, i, i + 1); r - l > maxR - maxL {
maxL, maxR = l, r
}
}
return s[maxL: maxR + 1]
}
func test(s string, l, r int) (int, int) {
for l >=0 && r < len(s) && s[l] == s[r] {
l--
r++
}
return l + 1, r - 1
}
25-K 个一组翻转链表
详见:反转链表
34-在排序数组中查找元素的第一个和最后一个位置
参考:二分查找求上下界
35-搜索插入位置
参考:二分查找求上下界
46-全排列
如下
47-全排列 II
这里不贴原题代码了,按照下面的代码稍微改改就可以了
/*
* 全排列
*
* 方法1:每次递归选择一个数,将其标记并加入到答案数组中
* 由于选择时是按顺序选择的,所以输出也是有序的,不过前提是输入也是有序的
* 如:123 -> 123 -> 132 -> 213 -> 231 -> 312 -> 321
*
* 方法2:123 全排列 = (1) 1 + 23 全排列、(2) 2 + 13 全排列、(3) 3 + 12 全排列
* 可以看出,对于 123 全排列,需要进行三次子递归处理(三个数字各出现在首位一次)
* 为了方便,只需要将每个数字和当前首位数字交换一下,然后递归即可
* 但是由于上面的交换,会导致输出不是从小到大排列的
*
* 方法3:直接调用 next_permutation 函数,输出有序,自带去重(需要保证输入有序)
*/
#include <iostream>
using namespace std;
#define N 110
bool vis[N];
char path[N];
void perm1(char s[], int x, int len) {
if (x >= len) {
for (int i = 0; i < x; i++) {
cout << path[i];
}
cout << endl;
return;
}
for (int i = 0; i < len; i++) {
if (!vis[i]) {
// 去重检查,需要保证输入有序
// 如果当前元素和前一个元素相等,比如:12335,在遍历处理第二个 3 时重复了,跳过
// 其中 !vis[i - 1] 是必需的,表示 s[i - 1] 未被遍历处理的情况下遍历到了 s[i],跳过处理,如果 s[i - 1] 已经被遍历到是不能跳过的,否则就没有输出了
if (i > 0 && s[i] == s[i - 1] && !vis[i - 1]) continue;
vis[i] = 1;
path[x] = s[i]; // 选择一个数标记并加入数组
perm1(s, x + 1, len); // 递归
vis[i] = 0; // 还原
}
}
}
void perm2(char s[], int x, int len) {
if (x >= len) {
cout << s << endl;
return;
}
for (int i = x; i < len; i++) {
// 重复元素检测,由于当前需要将 s[x] 和 s[i] 交换然后递归,所以可以通过检测 s[i] 前面是否有和 s[i] 相同的元素
// 比如:12353,第一个 3 和 1 交换得到 3 2135 递归,随后第二个 3 和 1 交换同样得到 3 2351,这两种排列是重复的
bool dup = 0;
for (int j = i - 1; j >= x; j--) {
if (s[i] == s[j]) {
dup = 1;
break;
}
}
if (dup) continue;
swap(s[x], s[i]); // 将后面的数交换到首尾
perm2(s, x + 1, len); // 递归处理
swap(s[x], s[i]); // 还原
}
}
void perm3(char s[], int x, int len) {
do {
cout << s << endl;
} while (next_permutation(s, s + len));
}
int main() {
char s1[N] = "1234";
char s2[N] = "1233";
puts("(1) 无重复元素");
puts("-----");
perm1(s1, 0, 4);
puts("-----");
perm2(s1, 0, 4);
puts("-----");
perm3(s1, 0, 4);
puts("(2) 有重复元素");
puts("-----");
perm1(s2, 0, 4);
puts("-----");
perm2(s2, 0, 4);
puts("-----");
perm3(s2, 0, 4);
return 0;
}
50-Pow(x, n)
参考:乘法快速幂
double myPow(double x, long long n) {
double ret = 1;
long long nn = n < 0 ? -n : n;
while (nn) {
if (nn & 1) ret *= x;
x *= x;
nn >>= 1;
}
return n < 0 ? 1 / ret : ret;
}
53-最大子序和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if (nums.size() <= 0) return 0;
int ans = nums[0];
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
ans = max(ans, sum);
if (sum < 0) {
sum = 0;
}
}
return ans;
}
};
62-不同路径
最简单的动态规划了,$dp[i][j]=dp[i-1][j] + dp[i][j-1]$
#include <string.h>
#define MAXN 111
int dp[MAXN][MAXN];
int uniquePaths(int n, int m) {
memset(dp, 0, sizeof(dp));
dp[1][1] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i - 1 >= 1) {
dp[i][j] += dp[i - 1][j];
}
if (j - 1 >= 1) {
dp[i][j] += dp[i][j - 1];
}
}
}
return dp[n][m];
}
63-不同路径 II
func uniquePathsWithObstacles(input [][]int) int {
n, m := len(input), len(input[0])
cnt := make([][]int, n)
for i := 0; i < n; i++ {
cnt[i] = make([]int, m)
for j := 0; j < m; j++ {
if i == 0 && j == 0 && input[i][j] == 0{
cnt[i][j] = 1
continue
}
if i > 0 && input[i][j] == 0 {
cnt[i][j] += cnt[i - 1][j]
}
if j > 0 && input[i][j] == 0 {
cnt[i][j] += cnt[i][j - 1]
}
}
}
return cnt[n - 1][m - 1]
}
64-最小路径和
class Solution {
public int minPathSum(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int dp[][] = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
dp[i][j] = 0x3f3f3f3f;
if (i == 0 && j == 0) {
dp[i][j] = grid[i][j];
continue;
}
if (i - 1 >= 0) {
dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + grid[i][j]);
}
if (j - 1 >= 0) {
dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + grid[i][j]);
}
}
}
return dp[n - 1][m - 1];
}
}
69-x 的平方根
#define int long long
int mySqrt(int x) {
int ans = 0;
int l = 0, r = 0x7fffffff;
while (l < r) {
int m = l + ((r - l) >> 1);
if (m * m <= x) l = m + 1, ans = m;
else r = m;
}
return ans;
}
70-爬楼梯
class Solution {
public:
int climbStairs(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
int a = 1, b = 2;
for (int i = 3; i <= n; i++) {
int c = a + b;
a = b;
b = c;
}
return b;
}
};
72-编辑距离
动态规划 TODO
#include <string.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 1010
int dp[MAXN][MAXN];
int minDistance(char* s1, char* s2){
int l1 = strlen(s1);
int l2 = strlen(s2);
// 0~i
// 0~j
for (int i = 0; i <= l1; i++)
dp[i][0] = i;
for (int i = 0; i <= l2; i++)
dp[0][i] = i;
for (int i = 1; i <= l1; i++) {
for (int j = 1; j <= l2; j++) {
if (s1[i - 1] == s2[j - 1]) {
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1]) + 1);
} else {
dp[i][j] = min(dp[i - 1][j - 1] /* 替换 */, min(dp[i - 1][j], dp[i][j - 1]) /* 插入 | 删除 */) + 1;
}
}
}
return dp[l1][l2];
}
74-搜索二维矩阵
参考:二分查找
92-反转链表 II
详见:反转链表
98-验证二叉搜索树
中序遍历一下就行了,判断是否递增
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
long last;
// = = 故意的吧
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
last = Long.MIN_VALUE;
return dfs(root);
}
private boolean dfs(TreeNode node) {
if (node == null) {
return true;
}
boolean left = dfs(node.left);
if (!left) {
return false;
}
if (node.val > last) {
last = node.val;
} else {
return false;
}
return dfs(node.right);
}
}
110-平衡二叉树
public boolean isBalanced(TreeNode root) {
return checkBalanced(root) >= 0;
}
// 由于 Java 只能有一个返回值,无法同时返回高度和是否平衡
// 所以定义返回值为子树的高度,当为 -1 时表示不平衡
//
// 注意不能只看根节点的左右子树高度,必须判断每个子树
// 比如:
// 1
// / \
// 2 3
// / \
// 4 5
// / \
// 6 7
public int checkBalanced(TreeNode node) {
if (node == null) return 0;
int left = checkBalanced(node.left);
if (left < 0) {
return -1;
}
int right = checkBalanced(node.right);
if (right < 0) {
return -1;
}
return Math.abs(left - right) <= 1 ? Math.max(left, right) + 1 : -1;
}
124-二叉树中的最大路径和
递归,如果路径和小于0则丢弃。最大路径要么:
- 跨子树根节点:左子树 + 右子树 + 当前节点权值
- 不跨子树根节点:左子树 或 右子树的最大值 + 当前节点权值
#define INF 0x3f3f3f3f
int ans;
int max(int a, int b) {
return a > b ? a : b;
}
int dfs(struct TreeNode* root) {
int left = 0, right = 0;
if (root->left != NULL) {
left = max(dfs(root->left), 0);
}
if (root->right != NULL) {
right = max(dfs(root->right), 0);
}
ans = max(ans, left + right + root->val); // 跨子树根节点
return max(left, right) + root->val; // 取左右子树最大值向上回溯
}
// 5
// 4 8
// 11 13 4
// 7 2 1
int maxPathSum(struct TreeNode* root) {
ans = -INF;
dfs(root);
return ans;
}
129-求根到叶子节点数字之和
简单递归一下就行
class Solution {
int ans;
public int sumNumbers(TreeNode root) {
ans = 0;
if (root == null) return 0;
dfs(root, 0);
return ans;
}
void dfs(TreeNode node, int n) {
if (node.left == null && node.right == null) { // 叶子节点
ans += n * 10 + node.val;
return;
}
if (node.left != null)
dfs(node.left, n * 10 + node.val);
if (node.right != null)
dfs(node.right, n * 10 + node.val);
}
}
133-克隆图
递归遍历一下就行了,用一个 vis 标记是否节点是否已创建
func cloneGraph(node *Node) *Node {
vis := make(map[int]*Node)
return clone(node, vis)
}
// 返回克隆后的节点
func clone(node *Node, vis map[int]*Node) *Node {
if node == nil {
return nil
}
if existNode, exist := vis[node.Val]; exist { // 节点已经创建了
return existNode
}
newNode := &Node{ Val: node.Val }
vis[node.Val] = newNode
for _, nextNode := range node.Neighbors {
newNextNode := clone(nextNode, vis)
newNode.Neighbors = append(newNode.Neighbors, newNextNode)
}
return newNode
}
141-环形链表
同下
142-环形链表 II
/*
* 单链表判环,并找出起点
* 时间复杂度 O(n),空间复杂度 O(1)
*
* 分析:
*
* head -> node -> cycle_start -> node -> node -> node
* ^ |
* |--------------------------v
*
* 如图所示,使用快慢指针遍历链表,假设头节点 head 到 环起始节点 cycle_start 的长度为 m1,从环起始节点 cycle_start 到快慢指针相遇点的长度为 m2,环的长度为 cycle_length
* 相遇时,slow 指针所走长度为 m1 + m2,fast 指针比 slow 节点多走了一圈,其所走长度为 2 * (m1 + m2) = m1 + m2 + cycle_length,推导出 cycle_length = m1 + m2,即 slow 指针所走的长度
* 另外,由于 slow 指针到 cycle_start 节点的距离为 cycle_length - m2 = m1,head 指针到 cycle_start 节点的距离也为 m1,所以只需要从 相遇点 和 head 节点同时遍历直到相遇就能找到环起始节点了
* 为什么快指针和慢指针一定会相遇 且 相遇时前者比后者恰好多走一圈:https://www.zhihu.com/question/23208893/answer/117115415
*/
class ListNode {
int val;
ListNode next;
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
@Override
public String toString() {
return "ListNode [val=" + val + "]";
}
}
public class LinkedListCycle {
public static ListNode checkCycle(ListNode head) {
if (head == null) return null;
ListNode slow = head, fast = head;
do {
if (fast.next == null || fast.next.next == null) {
return null; // 不存在环
}
slow = slow.next;
fast = fast.next.next;
} while (slow != fast);
ListNode start = head;
while (start != slow) {
start = start.next;
slow = slow.next;
}
return start; // 环起点
}
// test
public static void main(String[] args) {
ListNode c7 = new ListNode(7, null);
ListNode c6 = new ListNode(6, c7);
ListNode c5 = new ListNode(5, c6);
ListNode c4 = new ListNode(4, c5);
ListNode c3 = new ListNode(3, c4);
ListNode c2 = new ListNode(2, c3);
ListNode c1 = new ListNode(1, c2);
System.out.println(checkCycle(c1));
c7.next = c3;
System.out.println(checkCycle(c1));
}
}
155-最小栈
/*
* 在普通的栈操作上添加了一个求最小值的操作
*
* 举个例子:
* push 4 [4] min=4
* push 2 [4, 2] min=2
* push 3 [4, 2, 3] min=2
* push 1 [4, 2, 3, 1] min=1
* pop [4, 2, 3] min=2
* pop [4, 2] min=2
* pop [4] min=4
*
* 很容易想到,如果要在 O(1) 时间内求栈中的最小元素,需要用额外的空间来换取时间
* 可以看出,如果能在栈操作过程中记录下最小值,当然只用单个变量记录当前的最小值肯定不行,因为还有出栈操作,出栈后最小值可能往回变大
* 所以可以,记录下每一个时刻栈中的最小值
* 即再用一个栈
* - 每次 push 后同步插入一个元素 (min(top(), x)),表示当前栈中的最小值
* - 每次 pop 后同步移除栈顶元素
*/
class MinStack {
private Stack<Integer> items = new Stack<>();
private Stack<Integer> minItems = new Stack<>();
public MinStack() {}
public void push(int x) {
int min = minItems.empty() ? x : Math.min(minItems.peek(), x);
items.push(x);
minItems.push(min);
}
public void pop() {
items.pop();
minItems.pop();
}
public int top() {
return items.peek();
}
public int getMin() {
return minItems.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
160-相交链表
/**
* 判断链表是否相交,并求出交点
* 时间复杂度 O(n),空间复杂度 O(1)
*/
class ListNode {
int val;
ListNode next;
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
@Override
public String toString() {
return "ListNode[ " + "val: " + val + " ]";
}
}
public class IntersectionLinkedList {
public static ListNode checkAcross(ListNode head1, ListNode head2) {
if (head1 == null || head2 == null) return null;
int length1 = 1, length2 = 1;
ListNode p = head1, q = head2;
while (p.next != null) {
p = p.next;
length1++;
}
while (q.next != null) {
q = q.next;
length2++;
}
if (p != q) return null;
int lengthc = Math.abs(length1 - length2);
p = head1;
q = head2;
if (length1 > length2) {
for (int i = 0; i < lengthc; i++) {
p = p.next;
}
} else {
for (int i = 0; i < lengthc; i++) {
q = q.next;
}
}
while (p != q) {
p = p.next;
q = q.next;
}
return p;
}
// test
public static void main(String[] args) {
ListNode p6 = new ListNode(6, null);
ListNode p5 = new ListNode(5, p6);
ListNode p4 = new ListNode(4, p5);
ListNode p3 = new ListNode(3, p4);
ListNode p2 = new ListNode(2, p3);
ListNode p1 = new ListNode(1, p2);
ListNode q2 = new ListNode(8, p5);
ListNode q1 = new ListNode(9, q2);
System.out.println(checkAcross(p1, q1));
}
}
162-寻找峰值
由于时间复杂度要求 logN,所以二分一下就行了
int findPeakElement(int* nums, int n) {
int l = 0, r = n;
while (l < r) {
int m = (l + r) >> 1;
// 满足条件:1 3 2
if ((m - 1 < 0 || nums[m] > nums[m - 1]) && (m + 1 >= n || nums[m] > nums[m + 1])) {
return m;
}
// 倒序:3 2 1,左边一定有峰值
if ((m - 1 < 0 || nums[m - 1] > nums[m]) && (m + 1 >= n || nums[m] > nums[m + 1])) {
r = m;
} else { // 非正序,右边一定有峰值:3 1 3 / 1 2 3
l = m + 1;
}
}
// 1 2 3 4 3
// m
// l r=5
// m
return -1; // un
}
179-最大数
排序处理一下就行了,排序按 ab 或 ba 取最大值排序
object Solution {
def largestNumber(nums: Array[Int]): String = {
val res = nums.map(_.toString).sortWith((a, b) => (a + b) > (b + a)).mkString
if (res.forall(_ == '0')) "0" else res
}
}
191-位1的个数
// 最简单的做法当然是不断除以 2,然后判断末尾是否是 1 即可,复杂度:O(logN),其实已经足够快了
// 不过还有优化的空间,如下所示,二进制中存在大量 0,参考 “231-2的幂”,通过 n & (n - 1) 不断“去掉”最后一个 1,从而跳过一些多余的判断,达到优化的目的
// 10010000001000010
// ^ 10010000001000001
// -------------------
// = 10010000001000000
// ^ 10000000000111111
// -------------------
// = 10010000000000000
// 10000000000000000
// 00000000000000000
public class Solution {
public int hammingWeight(int n) {
int ans = 0;
while (n != 0) {
ans ++;
n &= (n - 1);
}
return ans;
}
}
198-打家劫舍
#define N 111
int a[N][2]; // 0 yes 1 no
#define max(a, b) ((a) > (b) ? (a) : (b))
int rob(int* nums, int n) {
if (n <= 0) return 0;
a[0][0] = nums[0];
for (int i = 1; i < n; i++) {
a[i][0] = a[i - 1][1] + nums[i];
a[i][1] = max(a[i - 1][0], a[i - 1][1]);
}
return max(a[n - 1][0], a[n - 1][1]);
}
200-岛屿数量
简单搜索
int ans;
int n, m;
char **grid;
int dir[4][2] = {-1, 0, 1, 0, 0, 1, 0, -1};
void dfs(int x, int y) {
grid[x][y] = '0';
for (int i = 0; i < 4; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] != '1') {
continue;
}
dfs(tx, ty);
}
}
int numIslands(char** input, int gridSize, int* gridColSize) {
if (gridSize == 0) {
return 0;
}
n = gridSize;
m = gridColSize[0];
ans = 0;
grid = input;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '1') {
ans++;
dfs(i, j);
}
}
}
return ans;
}
204-计数质数
参考:素数筛
#include <stdio.h>
#include <string.h>
#define LL long long
#define MAXN 1500000
bool is_prime[MAXN + 1];
int prime[MAXN];
int tot;
int solve(int n) {
tot = 0;
for (int i = 2; i <= n; i++) is_prime[i] = true;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
prime[tot++] = i;
}
for (int j = 0; j < tot; j++) {
if ((LL) i * prime[j] > n) break;
is_prime[i * prime[j]] = false;
if (i % prime[j] == 0) {
break;
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (is_prime[i]) ans++;
}
return ans;
}
int countPrimes(int n) {
return solve(n - 1);
}
206-反转链表
/**
* 逆置单链表
* 时间复杂度 O(n),空间复杂度 O(1)
*/
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public class ReverseLinkedList {
/*
* LeetCode 206
*
* head 始终指向头结点,p 指向当前需要逆置到头结点的节点,q 是一个辅助指针,用于遍历过程中临时指向 p 的下一个节点
* 从第二个节点开始,依次将节点插入到头结点处
*
* h
* 1 2 3 4 5
* p
*
* h
* 3 2 1 4 5
* p q
*/
public static ListNode reverse(ListNode head) {
if (head == null) return null; // 0
if (head.next == null) return head; // 1
ListNode p = head.next;
ListNode q = null;
head.next = null; // 尾指针的下一个节点置为空
while (p != null) {
q = p.next;
p.next = head; // 插入到头结点
head = p;
p = q;
}
return head;
}
/*
* LeetCode 92
*
* 翻转 [l, r] 区间,下标从 1 开始
*
* 题目分为两种情况,一种是从第一个节点开始翻转,一种是中间节点开始翻转
* 为了简单起见,可以新建一个虚拟的头指针
*
* (1) 先找到翻转起点的前一个节点
* (2) 从翻转起点的后一个节点开始依次翻转,将其插入到起点前一个节点的后面
*
* revPre revHead p q
* 1 2 3 4 5
*
* 1 3 2 4 5
*
* p q
*
* 1 4 3 2 5
*
* revHeadCopy
* p q
*/
public ListNode reverseBetween(ListNode head, int l, int r) {
ListNode virtualHead = new ListNode(0);
virtualHead.next = head;
ListNode revPre = virtualHead;
for (int i = 1; i < l; i++) revPre = revPre.next;
ListNode revHead = revPre.next; // 翻转起点
ListNode revHeadCopy = revHead;
ListNode p = revHead.next; // 从翻转起点的下一个节点开始翻转
for (int i = l + 1; i <= r && p != null; i++) { // 依次翻转
ListNode q = p.next;
p.next = revHead; // 插入到头结点
revHead = p;
p = q;
}
revPre.next = revHead; // 原翻转起点前一个节点的 next 指针修改
revHeadCopy.next = p; // 原翻转起点的 next 指针修改
return virtualHead.next;
}
/*
* 每 K 个一组进行反转
*
* 1 2 3 4 5
* 3 2 1 5 4
*
* 分段处理一下就行了,同样地,为了方便,添加一下虚拟头结点
*/
public ListNode reverseKGroup1(ListNode head, int k) {
if (k == 1) return head;
ListNode virtualHead = new ListNode(0);
virtualHead.next = head;
ListNode c = virtualHead;
while (c.next != null) {
ListNode groupPre = c; // 每组翻转头结点的前一个节点
ListNode groupHead = groupPre.next; // 每组翻转的头结点
ListNode groupHeadCopy = groupHead;
ListNode p = groupHead.next; // 从头结点的后一个节点开始翻转
for (int i = 2; i <= k && p != null; i++) {
ListNode q = p.next;
p.next = groupHead; // 插入到头结点
groupHead = p;
p = q;
}
groupPre.next = groupHead; // 原翻转起点前一个节点的 next 指针修改
groupHeadCopy.next = p; // 原翻转起点的 next 指针修改
c = groupHeadCopy; // 修改 c 为下一组翻转区间头节点的前一个节点
}
return virtualHead.next;
}
/*
* LeetCode 25
*
* 每 K 个一组进行反转,如果不足 k 个保持不变,可以通过长度判断
*
* 1 2 3 4 5
* 3 2 1 4 5
*
* 分段处理一下就行了,同样地,为了方便,添加一下虚拟头结点
*/
public ListNode reverseKGroup2(ListNode head, int k) {
if (k == 1) return head;
int length = 0;
for (ListNode p = head; p != null; p = p.next) length++;
ListNode virtualHead = new ListNode(0);
virtualHead.next = head;
ListNode c = virtualHead;
while (c.next != null && length >= k) {
ListNode groupPre = c; // 每组翻转头结点的前一个节点
ListNode groupHead = groupPre.next; // 每组翻转的头结点
ListNode groupHeadCopy = groupHead;
ListNode p = groupHead.next; // 从头结点的后一个节点开始翻转
for (int i = 2; i <= k && p != null; i++) {
ListNode q = p.next;
p.next = groupHead; // 插入到头结点
groupHead = p;
p = q;
}
groupPre.next = groupHead; // 原翻转起点前一个节点的 next 指针修改
groupHeadCopy.next = p; // 原翻转起点的 next 指针修改
c = groupHeadCopy; // 修改 c 为下一组翻转区间头节点的前一个节点
length -= k;
}
return virtualHead.next;
}
// test
public static void main(String[] args) {
ListNode n5 = new ListNode(5, null);
ListNode n4 = new ListNode(4, n5);
ListNode n3 = new ListNode(3, n4);
ListNode n2 = new ListNode(2, n3);
ListNode n1 = new ListNode(1, n2);
ListNode p = new ReverseLinkedList().reverseKGroup2(n1, 3);
while (p != null) {
System.out.print(p.val + " ");
p = p.next;
}
}
}
207-课程表
拓扑排序有向图判环,参考:
class Solution {
private int n;
private List<Integer>[] edges;
private int[] de;
public boolean canFinish(int nn, int[][] input) {
n = nn;
edges = new ArrayList[n];
de = new int[n];
for (int i = 0; i < n; i++) {
edges[i] = new ArrayList<>();
}
for (int i = 0; i < input.length; i++) {
int u = input[i][0];
int v = input[i][1];
// 0,1 => 0>1
edges[u].add(v);
de[v]++;
}
return check();
}
private boolean check() {
Queue<Integer> q = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (de[i] == 0) {
q.add(i);
}
}
int cnt = 0;
while (!q.isEmpty()) {
int u = q.poll();
cnt++;
for (int i = 0; i < edges[u].size(); i++) {
int v = edges[u].get(i);
de[v]--;
if (de[v] == 0) {
q.add(v);
}
}
}
return cnt == n;
}
}
208-实现 Trie (前缀树)
字典树模板题,参考:
class Trie {
static int N = 26;
static class Node {
Node[] next = new Node[N];
int vis;
}
private Node root;
public Trie() {
root = new Node();
}
public void insert(String word) {
Node node = root;
Node next;
for (int i = 0; i < word.length(); i++) {
int pos = getPos(word.charAt(i));
next = node.next[pos];
if (next == null) {
next = new Node();
node.next[pos] = next;
}
node = next;
}
node.vis++;
}
public boolean search(String word) {
return searchInternal(word, false);
}
public boolean startsWith(String prefix) {
return searchInternal(prefix, true);
}
private boolean searchInternal(String word, boolean prefix) {
Node node = root;
Node next;
for (int i = 0; i < word.length(); i++) {
int pos = getPos(word.charAt(i));
next = node.next[pos];
if (next == null) {
return false;
}
node = next;
}
if (prefix) {
return true;
}
return node.vis > 0;
}
private int getPos(char c) {
return c - 'a';
}
}
209-长度最小的子数组
二指针简单题
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int ans = 0;
int sum = 0;
for (int i = 0, j = 0; i < nums.length; i++) {
while (j < nums.length && sum < s) {
sum += nums[j++];
}
if (sum >= s) {
if (ans == 0) {
ans = j - i;
} else {
ans = Math.min(ans, j - i);
}
}
sum -= nums[i];
}
return ans;
}
}
222-完全二叉树的节点个数
TODO
228-汇总区间
简单,记录一下有序区间左起点就行
class Solution {
public List<String> summaryRanges(int[] nums) {
List<String> ans = new ArrayList<>();
int l = -1;
for (int i = 0; i < nums.length; i++) {
if (i + 1 >= nums.length || nums[i] + 1 != nums[i + 1]) {
if (l == -1) {
ans.add(String.valueOf(nums[i]));
} else {
ans.add(String.format("%s->%s", l, nums[i]));
l = -1;
}
} else {
if (l == -1) {
l = nums[i];
}
}
}
return ans;
}
}
231-2的幂
// 2 的幂对应的二进制中一定只有一个 1
// 所以只需要 n & (n - 1),它的作用是去掉二进制中的最后一个 1,如果结果大于 0,说明不只一个 1,不是 2 的幂
// 00010000
// ^ 00011111
// ----------
// = 00000000
class Solution {
public boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
}
232-用栈实现队列
/**
* 两个栈实现队列
* 为了保持先进先出的特性,使用两个栈
*
* - 判空:如果栈 1 和栈 2 都为空则为空
* - 入队:直接进入栈 1 中
* - 出队:先判断栈 2 中是否存在元素
* --- 如果存在:直接出栈
* --- 如果不存在:将栈 1 中的元素倒入栈 2 中,再出栈
*/
public class DoubleStack2Queue<T> {
private Stack<T> s1 = new Stack<>();
private Stack<T> s2 = new Stack<>();
public DoubleStack2Queue() { }
// 判空
private boolean empty() {
return s1.empty() && s2.empty();
}
// 入队
public void push(T x) {
s1.push(x);
}
// 出队
public T pop() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.pop());
}
}
return s2.pop();
}
// test
public static void main(String[] args) {
DoubleStack2Queue<Integer> q = new DoubleStack2Queue<>();
q.push(1);
q.push(2);
System.out.println(q.pop());
System.out.println(q.pop());
q.push(2);
q.push(3);
q.push(4);
q.push(5);
while (!q.empty()) {
System.out.println(q.pop());
}
}
}
233-数字 1 的个数
数位 DP 模板题,参考:数位DP
#define N 100
int bit[N]; // 13 => 3 1
int dp[N][N];
int dfs(int pos, int num, bool limit, bool fzero) {
if (pos == -1) {
return num;
}
if (!limit && !fzero && dp[pos][num] != -1) {
return dp[pos][num];
}
int ans = 0;
int end = limit ? bit[pos] : 9;
for (int i = 0; i <= end; i++) {
int num1 = num + (i == 1);
ans += dfs(pos - 1, num1, limit && i == end, !i && fzero);
}
if (!limit && !fzero) dp[pos][num] = ans;
return ans;
}
int countDigitOne(int n) {
int l = 0;
while (n) {
bit[l++] = n % 10;
n /= 10;
}
memset(dp, -1, sizeof(dp));
return dfs(l - 1, 0, true, true);
}
236-二叉树的最近公共祖先
TODO
239-滑动窗口最大值
单调队列模板题,详情:单调队列-栈
func maxSlidingWindow(nums []int, k int) []int {
type Item struct {
id int
val int
}
res := make([]int, 0, len(nums));
queue := make([]Item, len(nums))
in, out := 0, 0
front, rear := 0, 0
for _, num := range nums {
for rear > front && num >= queue[rear - 1].val { // 递减
rear--
}
queue[rear] = Item{in, num} // 进队
rear++
in++
if in - out == k { // k个元素,出队
res = append(res, queue[front].val)
if out == queue[front].id {
front++
}
out++
}
}
return res;
}
279-完全平方数
简单 BFS 搜索,应该有更优解法
class Solution {
public int numSquares(int n) {
int maxNum = (int)Math.sqrt(n); // 25 1 4 9 16 25
int[] square = new int[maxNum];
for (int i = 0; i < maxNum; i++) {
square[i] = (i + 1) * (i + 1);
}
// 1 4 9 16
// ans[i] = min(ans[i], ans[i - squre[i]] + 1)
Queue<Node> q = new LinkedList<>();
q.add(new Node(0, 0));
while (!q.isEmpty()) {
Node u = q.poll();
if (u.val == n) {
return u.cnt;
}
for (int i = 0; i < maxNum; i++) {
int nextVal = u.val + square[i];
if (nextVal <= n) {
if (nextVal == n) {
return u.cnt + 1;
}
q.add(new Node(nextVal, u.cnt + 1));
}
}
}
return 0; // xx
}
static class Node {
int val;
int cnt;
Node() {}
Node(int val, int cnt) {
this.val = val;
this.cnt = cnt;
}
}
}
303-区域和检索 - 数组不可变
参考:一维前缀和
392-判断子序列
bool isSubsequence(char *s, char *t) {
int s_len = strlen(s);
int t_len = strlen(t);
int i = 0;
for (int j = 0; i < s_len && j < t_len; j++) {
if (t[j] == s[i]) i++;
}
return i >= s_len;
}
404-左叶子之和
简单题目,注意由于需要判断是否是左叶子
- 默认情况下不能遍历到叶子节点(叶子节点不知道自己是否是左叶子),所以需要在叶子节点的父节点进行判断
- 或者向下带上一个参数,表示是否是左儿子节点
class Solution(object):
def sumOfLeftLeaves(self, root):
"""
:type root: TreeNode
:rtype: int
"""
return self.cal(root)
return self.cal2(root, False)
def cal(self, node):
if node is None:
return 0
res = 0
if node.left is not None and node.left.left is None and node.left.right is None: # 左叶子
res = node.left.val
return self.cal(node.left) + self.cal(node.right) + res
def cal2(self, node, is_left):
if node is None:
return 0
if is_left and node.left is None and node.right is None:
return node.val
return self.cal2(node.left, True) + self.cal2(node.right, False)
429-N叉树的层序遍历
使用队列遍历一下就行了
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result = new ArrayList();
if (root == null) {
return result;
}
Queue<Node> q = new LinkedList<>();
q.add(root);
int nowLevelNum = 1;
int nextLevelNum = 0;
List<Integer> items = new ArrayList<>();
while (!q.isEmpty()) {
Node now = q.poll();
items.add(now.val); // 出队时添加到列表中
for (Node next : now.children) {
nextLevelNum++;
q.add(next);
}
nowLevelNum--;
if (nowLevelNum == 0) { // 这一层遍历完成,新建一个列表
result.add(items);
items = new ArrayList<>();
nowLevelNum = nextLevelNum;
nextLevelNum = 0;
}
}
return result;
}
}
421-数组中两个数的最大异或值
先把所有数字插入到 01 字典树中,然后遍历每个数,从树中找出与其异或最大的值即可
#define MAXN 500010
int sz;
int ch[MAXN][2];
int val[MAXN];
int get_id(int n, int m) {
return (int)(n & ((long long)1 << m)) > 0;
}
void build(int *nums, int n) {
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
val[0] = 0;
for (int i = 0; i < n; i++) {
int u = 0;
for (int j = 31; j >= 0; j--) {
int id = get_id(nums[i], j);
int v = ch[u][id];
if (!v) {
val[sz] = 0;
memset(ch[sz], 0, sizeof(ch[sz]));
ch[u][id] = sz++;
v = ch[u][id];
}
u = v;
}
val[u] = nums[i];
}
}
int find(int *nums, int n) {
int mx = 0;
for (int i = 0; i < n; i++) {
int u = 0;
for (int j = 31; j >= 0; j--) {
int id = get_id(nums[i], j);
if (ch[u][id ^ 1] != 0) {
u = ch[u][id ^ 1];
} else {
u = ch[u][id];
}
}
if ((val[u] ^ nums[i]) > mx) {
mx = (val[u] ^ nums[i]);
}
}
return mx;
}
int findMaximumXOR(int* nums, int n) {
build(nums, n);
return find(nums, n);
}
435-无重叠区间
基础的贪心题目,将区间按右端点排序,依次贪心,得到最多不重叠的区间数,最后用总数减去即可
class Solution {
public int eraseOverlapIntervals(int[][] input) {
// 1 2 1
// 1 3
// 2 3 2
// 3 4 3
if (input == null || input.length == 0) {
return 0;
}
int n = input.length;
Interval[] intervals = new Interval[n];
for (int i = 0; i < n; i++) {
intervals[i] = new Interval(input[i][0], input[i][1]);
}
Arrays.sort(intervals);
int cnt = 1;
Interval last = intervals[0];
for (int i = 1; i < n; i++) {
if (intervals[i].start >= last.end) {
last = intervals[i];
cnt++;
}
}
return n - cnt; // 2
}
class Interval implements Comparable<Interval> {
int start;
int end;
public Interval(int start, int end) {
this.start = start;
this.end = end;
}
public int compareTo(Interval to) {
return this.end - to.end;
}
}
}
448-找到所有数组中消失的数字
依次将每个元素标记为出现,然后从 1 遍历到 n 就能找出消失的数字
但是由于不能使用额外空间,所以比较 trick,比如出现了 4,即在原数组下标为 4 的元素置为相反数,利用原数组进行标记,如果是负数,即表示出现过
func findDisappearedNumbers(nums []int) []int {
for _, n := range nums {
idx := abs(n) - 1
if (nums[idx] > 0) {
nums[idx] = -nums[idx]
}
}
ret := make([]int, 0)
for i := range nums {
if nums[i] > 0 {
ret = append(ret, i + 1)
}
}
return ret
}
func abs(n int) int {
if n < 0 {
n = -n
}
return n
}
450-删除二叉搜索树中的节点
当被移除节点既有左儿子又有右儿子时,将其替换为左子树中的最大节点或者右子树中的最小节点即可
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode* leftMax(struct TreeNode* node) {
while (node->right != NULL) node = node->right;
return node;
}
// 删除当前子树中的 key,并返回子树根节点
struct TreeNode* deleteNode(struct TreeNode* node, int key) {
if (node == NULL) {
return node;
}
if (key < node->val) node->left = deleteNode(node->left, key); // 左边
else if (key > node->val) node->right = deleteNode(node->right, key); // 右边
else { // 等于
if (node->left == NULL) {
return node->right;
}
if (node->right == NULL) {
return node->left;
}
// 如果左右子树都不为空,则将当前节点替换为左子树中的最大节点,随后递归将左子树中的最大节点删除
node->val = leftMax(node->left)->val;
node->left = deleteNode(node->left, node->val);
}
return node;
}
494-目标和
由于数组和不大,所以可以使用一个二维数组表示前 i 个数字和为 j 的组合数。由于 j 可能有负数,所以 j 整体平移 2 * N
#define N 1000
int a[20][N * 4 + 1];
int pos(int j) {
return j + 2 * N;
}
int findTargetSumWays(int* nums, int n, int s) {
memset(a, 0, sizeof(a));
int sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
}
if (s > sum) {
return 0;
}
a[0][pos(nums[0])] = 1;
a[0][pos(-1 * nums[0])] += 1;
for (int i = 1; i < n; i++) {
for (int j = -N; j <= N; j++) {
int pj = pos(j);
a[i][pj] = a[i - 1][pj - nums[i]] /* + */ + a[i - 1][pj + nums[i]]; /* - */
}
}
return a[n - 1][pos(s)];
}
547-朋友圈
简单搜索一下就行,判断是否所有节点都可达,也可以使用并查集
class Solution {
public int findCircleNum(int[][] input) {
int n = input.length;
boolean vis[] = new boolean[n];
int ans = 0;
for (int i = 0; i < n; i++) {
if (!vis[i]) {
dfs(i, n, input, vis);
ans++;
}
}
return ans;
}
private void dfs(int s, int n, int[][] input, boolean vis[]) {
vis[s] = true;
for (int t = 0; t < n; t++) {
if (input[s][t] > 0 && !vis[t]) {
dfs(t, n, input, vis);
}
}
}
}
551-学生出勤记录 I
class Solution(object):
def checkRecord(self, s):
"""
:type s: str
:rtype: bool
"""
return s.count('A') <= 1 and 'LLL' not in s
557-反转字符串中的单词 III
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
fir = True
ss = ""
for part in s.split(" "):
if not fir:
ss += " "
ss += part[::-1]
fir = False
return ss
559-N叉树的最大深度
func maxDepth(root *Node) int {
if root == nil {
return 0
}
max := 0
dfs(root, 1, &max);
return max;
}
func dfs(rt *Node, depth int, maxDepth *int) {
if depth > *maxDepth {
*maxDepth = depth;
}
for _, child := range rt.Children {
dfs(child, depth + 1, maxDepth);
}
}
563-二叉树的坡度
func findTilt(root *TreeNode) int {
sum := 0
dfs(root, &sum)
return sum
}
func dfs(rt *TreeNode, sum *int) int {
if (rt == nil) {
return 0
}
left := dfs(rt.Left, sum)
right := dfs(rt.Right, sum)
if left > right {
*sum += left - right
} else {
*sum += right - left
}
return left + right + rt.Val
}
572-另一个树的子树
树不大的话简单递归一下就行了,
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if (s == null) return t == null;
return same(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
}
private boolean same(TreeNode s, TreeNode t) {
if (s == null && t == null) return true;
if (s != null && t != null && s.val == t.val) {
return same(s.left, t.left) && same(s.right, t.right);
}
return false;
}
}
605-种花问题
简单贪心问题,如果当前能种则种即可
class Solution(object):
def canPlaceFlowers(self, flowerbed, n):
"""
:type flowerbed: List[int]
:type n: int
:rtype: bool
"""
last = -2
count = 0
flowerbed.append(0)
for i, c in enumerate(flowerbed):
if c == 1:
last = i
if c == 0 and i - last > 1 and i + 1 < len(flowerbed) and flowerbed[i + 1] != 1:
count += 1
last = i
return count >= n
617-合并二叉树
class Solution {
// 返回值为合并后的子树根节点
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null) {
return t2;
}
if (t2 == null) {
return t1;
}
t1.val += t2.val;
// 合并左右子树
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
return t1;
}
}
647-回文子串
以每个节点为中心,向两边扩展即可,需要分为奇数位和偶数位两种情况
- 这里的解法有优化的空间
- 应该还有更优的解法
int count(char *s, int l, int r, int n) {
int c = 0;
while (l >= 0 && r < n && s[l] == s[r]) l--, r++, c++;
return c;
}
int countSubstrings(char * s) {
int n = strlen(s);
int ans = 0;
for (int i = 0; i < n; i++) {
ans += count(s, i, i, n); // 212
ans += count(s, i, i + 1, n); // 2112
}
return ans;
}
662-二叉树最大宽度
其实和节点的值没啥关系,根据二叉树的关系给每个节点编个号,左儿子 = 父节点 * 2,右儿子 = 父节点 * 2 + 1,然后分层遍历一下树,对于每一层,用最右边的节点序号减去最左边的节点序号,算一下最大值就行
class Solution {
public int widthOfBinaryTree(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> q = new LinkedList<>();
int ans = 1;
root.val = 1;
q.add(root);
while (!q.isEmpty()) {
int size = q.size();
int left = 0, right = 0;
for (int i = 0; i < size; i++) {
TreeNode rt = q.poll();
if (i == 0) {
left = rt.val;
} else if (i == size - 1) {
right = rt.val;
}
if (rt.left != null) {
rt.left.val = rt.val << 1;
q.add(rt.left);
}
if (rt.right != null) {
rt.right.val = rt.val << 1 | 1;
q.add(rt.right);
}
}
if (size >= 2) {
ans = Math.max(ans, right - left + 1);
}
}
return ans;
}
}
684-冗余连接
简单并查集一下就行了
func findRedundantConnection(edges [][]int) []int {
n := len(edges)
f := make([]int, n + 1)
for i := 0; i < n; i++ {
f[i] = i
}
for i := 0; i < n; i++ {
u, v := edges[i][0], edges[i][1]
if u > v {
u, v = v, u
}
if !add(f, u, v) {
return []int{u, v}
}
}
return nil
}
func add(f []int, u, v int) bool {
x := find(f, u)
y := find(f, v)
if x == y {
return false
}
f[x] = y
return true
}
func find(f []int, x int) int {
if f[x] == x {
return x
}
f[x] = find(f, f[x])
return f[x]
}
695-岛屿的最大面积
简单搜索一下就行了,取最大值
class Solution {
int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
public int maxAreaOfIsland(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
ans = Math.max(ans, dfs(i, j, n, m, grid));
}
}
}
return ans;
}
// 1 1 0 0 0
// 1 1 0 0 0
// 0 0 0 1 1
// 0 0 0 1 1
int dfs(int x, int y, int n, int m, int[][] grid) {
int area = 1;
grid[x][y] = 0;
for (int i = 0; i < dir.length; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] == 0) {
continue;
}
area += dfs(tx, ty, n, m, grid);
}
return area;
}
}
732-我的日程安排表 III
即求 [a, b) 区间内覆盖的最大次数,直接使用前缀和即可,由于数值比较离散,所以需要用一个 map 起到离散的作用。由于 golang 内置 map 无序,所以需要手动 sort 一下,如果是其他语言,直接使用 TreeMap 即可
type MyCalendarThree struct {
vis map[int]int
}
func Constructor() MyCalendarThree {
return MyCalendarThree{
vis: make(map[int]int, 400),
}
}
func (c *MyCalendarThree) Book(start int, end int) int {
c.vis[start]++
c.vis[end]--
ans := 0
sum := 0
var keys []int
for k := range c.vis {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
sum += c.vis[k]
if sum > ans {
ans = sum
}
}
return ans
}
746-使用最小花费爬楼梯
题意是真的难懂
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
if (n == 1) {
return cost[0];
}
vector<int> a(n);
a[0] = cost[0];
a[1] = cost[1];
for (int i = 2; i < n; i++) {
a[i] = min(a[i - 1], a[i - 2]) + cost[i];
}
return min(a[n - 1], a[n - 2]);
}
};
743-网络延迟时间
随表搜一搜就行了,判断是否可达,如果同一个节点有多个路径可达取最小值
class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
// build
List<Edge>[] g = new ArrayList[n + 1];
for (int i = 1; i <= n; i++) {
g[i] = new ArrayList<Edge>();
}
for (int i = 0; i < times.length; i++) {
int u = times[i][0];
int v = times[i][1];
int w = times[i][2];
g[u].add(new Edge(v, w));
}
// process
int dp[] = new int[n + 1];
for (int i = 1; i <= n; i++) {
dp[i] = -1; // unreachable
}
dfs(k, k, 0, g, dp);
int ans = 0;
for (int i = 1; i <= n; i++) {
if (dp[i] == -1) {
return -1;
}
ans = Math.max(ans, dp[i]);
}
return ans;
}
private void dfs(int u, int pre, int time, List<Edge>[] g, int dp[]) {
if (dp[u] == -1) {
dp[u] = time;
} else {
if (time >= dp[u]) {
return;
}
dp[u] = time;
}
for (int i = 0; i < g[u].size(); i++) {
int v = g[u].get(i).v;
int w = g[u].get(i).w;
if (v == pre) {
continue;
}
dfs(v, u, time + w, g, dp);
}
}
static class Edge {
public int v;
public int w;
public Edge(){}
public Edge(int v, int w) {
this.v = v;
this.w = w;
}
}
}
785-判断二分图
染色 dfs 遍历一下就行了
class Solution {
public boolean isBipartite(int[][] graph) {
int n = graph.length;
int color[] = new int[n];
for (int i = 0; i < n; i++) {
if (color[i] == 0) {
color[i] = 1;
boolean ok = dfs(graph, i, color);
if (!ok) {
return false;
}
}
}
return true;
}
private boolean dfs(int[][] graph, int u, int[] color) {
for (int i = 0; i < graph[u].length; i++) {
int v = graph[u][i];
if (color[v] == color[u]) {
return false;
}
if (color[v] == 0) {
color[v] = -color[u];
if (!dfs(graph, v, color)) {
return false;
}
}
}
return true;
}
}
795-区间子数组个数
TODO
827-最大人工岛
不难,先用 DFS 搜出所有的陆地块,同时对其编号并记录每块陆地所在的陆地块序号,然后枚举所有的海洋,判断其上下左右是否是陆地,如果是则把面积相加,最后取最大值即可
#define N 55
#define max(a, b) ((a) > (b) ? (a) : (b))
int tot;
int size[N * N];
int f[N][N];
int ans;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void cal_size(int **grid, int n, int m, int x, int y, int tot, int *size) {
(*size)++;
f[x][y] = tot;
grid[x][y] = -1;
for (int k = 0; k < 4; k++) {
int tx = x + dir[k][0];
int ty = y + dir[k][1];
if (tx >= 0 && tx < n && ty >= 0 && ty < m && grid[tx][ty] == 1) {
cal_size(grid, n, m, tx, ty, tot, size);
}
}
}
int largestIsland(int** grid, int n, int* gridColSize) {
tot = 0;
ans = 0;
memset(f, 0, sizeof(f));
memset(size, 0, sizeof(size));
int m = gridColSize[0];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
tot++;
cal_size(grid, n, m, i, j, tot, &size[tot]);
ans = max(ans, size[tot]);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 0) {
int sum = 1;
int vis[4] = {0}; // map?
int debug = 0;
for (int k = 0; k < 4; k++) {
int ti = i + dir[k][0];
int tj = j + dir[k][1];
if (ti >= 0 && ti < n && tj >= 0 && tj < m && grid[ti][tj] != 0) {
int flag = 1;
for (int x = 0; x < k; x++) {
if (vis[x] == f[ti][tj]) {
flag = 0;
break;
}
}
if (flag) {
debug++;
sum += size[f[ti][tj]];
vis[k] = f[ti][tj];
}
}
}
ans = max(ans, sum);
}
}
}
return ans;
}
// 1 0
// 0 1
// 1 1
// 1 0
// 1
// 1 0 1
// 1
834-树中距离之和
TODO
863-二叉树中所有距离为 K 的结点
将树转换一下,把 target 当做根节点,然后找出所有深度为 K 的节点即可
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
Map<Integer, List<Integer>> graph = new HashMap();
List<Integer> ans = new ArrayList<>();
convert(root, graph);
dfs(graph, target.val, target.val, k, 0, ans);
return ans;
}
private void dfs(Map<Integer, List<Integer>> graph, int u, int pre, int k, int dep, List<Integer> ans) {
if (dep == k) {
ans.add(u);
return;
}
List<Integer> to = graph.get(u);
if (to == null) {
return;
}
for (int i = 0; i < to.size(); i++) {
int v = to.get(i);
if (v != pre) {
dfs(graph, v, u, k, dep + 1, ans);
}
}
}
private void convert(TreeNode root, Map<Integer, List<Integer>> graph) {
if (root == null) {
return;
}
if (root.left != null) {
addEdge(root.val, root.left.val, graph);
convert(root.left, graph);
}
if (root.right != null) {
addEdge(root.val, root.right.val, graph);
convert(root.right, graph);
}
}
private void addEdge(int u, int v, Map<Integer, List<Integer>> graph) {
if (graph.get(u) == null) {
graph.put(u, new ArrayList<>());
}
if (graph.get(v) == null) {
graph.put(v, new ArrayList<>());
}
graph.get(u).add(v);
graph.get(v).add(u);
}
}
933-最近的请求次数
# -*- coding: UTF-8 -*-
class RecentCounter(object):
def __init__(self):
self.pings = []
self.left = 0
def ping(self, t):
"""
:type t: int
:rtype: int
"""
self.pings.append(t)
while t - self.pings[self.left] > 3000:
self.left += 1
return len(self.pings) - self.left
951-翻转等价二叉树
class Solution {
public boolean flipEquiv(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null) return true;
if (root1 != null && root2 != null && root1.val == root2.val) { // 根节点相等,比较左子树和右子树,分为两种情况
return flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)
|| flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left);
}
return false;
}
}
968-监控二叉树
TODO
991-坏了的计算器
贪心问题,从结果倒推,如果是奇数就加一,否则除以2,直到小于等于原始值,再补上一个差值即可
class Solution {
public int brokenCalc(int x, int y) {
// 5 => 8
// 5 => 4 => 8
// 5 => 7
// 5 => 4 => 8 => 7 7=8-1
// 5 => 10 => 9 => 8 => 7
// 3 => 10
// 3 => 6 => 5 => 10
int count = 0;
while (y > x) {
if (y % 2 == 1) {
y += 1;
} else {
y /= 2;
}
count++;
}
return count + (x - y);
}
}
1003-检查替换后的词是否有效
简单题目,不断删除abc,判断是否可以全部被删除
#define MAXN 20010
int top;
char stack[MAXN];
bool isValid(char *s) {
int n = strlen(s);
top = 0;
for (int i = 0; i < n; i++) {
stack[top++] = s[i];
// aabc bc
while (top >= 3
&& stack[top - 1] == 'c'
&& stack[top - 2] == 'b'
&& stack[top - 3] == 'a') {
top -= 3;
}
}
return top == 0;
}
1137-第 N 个泰波那契数
递推一下就行了
class Solution(object):
def tribonacci(self, n):
"""
:type n: int
:rtype: int
"""
ret = [0, 1, 1]
i = 3
while i <= n:
ret.append(ret[i - 1] + ret[i - 2] + ret[i - 3])
i += 1
return ret[n]
1161-最大层内元素和
class Solution {
public:
int maxLevelSum(TreeNode* root) {
if (root == NULL) return 0;
int max_sum = 0;
int max_dep = 0;
int dep = 0;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int size = q.size();
int sum = 0;
while (size--) {
TreeNode* u = q.front();
q.pop();
sum += u->val;
if (u->left != NULL) q.push(u->left);
if (u->right != NULL) q.push(u->right);
}
dep++;
if (sum > max_sum) {
max_dep = dep;
max_sum = sum;
}
}
return max_dep;
}
};
1219-黄金矿工
从每个点开始搜索一遍就行了
class Solution {
int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
public int getMaximumGold(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
ans = Math.max(ans, dfs(i, j, n, m, grid));
}
}
// ans = dfs(2, 1, n, m, grid);
return ans;
}
// 0 6 0
// 5 8 7
// 0 9 0
// 1, 0, 7
// 2, 0, 6
// 3, 4, 5
// 0, 3, 0
// 9, 0, 20
int dfs(int x, int y, int n, int m, int grid[][]) {
int t = grid[x][y];
int maxNext = 0;
grid[x][y] = -1;
for (int i = 0; i < dir.length; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] <= 0) {
continue;
}
maxNext = Math.max(maxNext, dfs(tx, ty, n, m, grid));
}
grid[x][y] = t;
return maxNext + t;
}
}
1249-移除无效的括号
TODO
1254-统计封闭岛屿的数目
简单的DFS搜一下就行,通过递归如果触及到边界则不是封闭岛屿
class Solution {
private int dir[][] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
public int closedIsland(int[][] grid) {
int ans = 0;
int n = grid.length;
int m = grid[0].length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 0) {
if (dfs(grid, n, m, i, j)) {
ans++;
}
}
}
}
return ans;
}
private boolean dfs(int[][] grid, int n, int m, int x, int y) {
boolean status = true;
if (x == 0 || y == 0 || x == n - 1 || y == m - 1) {
status = false;
}
for (int i = 0; i < dir.length; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] != 0) {
continue;
}
grid[tx][ty] = 1;
boolean ok = dfs(grid, n, m, tx, ty);
if (!ok) {
status = false;
}
}
return status;
}
}
1267-统计参与通信的服务器
简单题目,判断一下同一行和同一列的服务器数量是否大于2即可
class Solution(object):
def countServers(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
n, m = len(grid), len(grid[0])
row = [0 for i in range(n)]
col = [0 for i in range(m)]
for i in range(n):
for j in range(m):
if grid[i][j] > 0:
row[i] += 1
col[j] += 1
ans = 0
for i in range(n):
for j in range(m):
if grid[i][j] > 0:
if row[i] >= 2 or col[j] >= 2:
ans += 1
return ans
1291-顺次数
简单模拟了一下,其实完全可以打表的
class Solution {
public List<Integer> sequentialDigits(int low, int high) {
// 1000 130000
// 1234
// 2345
// 3456
// 4567
// 5678
// 6789
//
// 10000
//
// 12345
// 23456
// 2000 9000
// 2345
List<Integer> ans = new ArrayList<>();
int len_low = len(low);
int len_high = len(high);
for (int i = len_low; i <= len_high; i++) {
int x = i == len_low ? highBit(low, len_low) : 1;
for (int j = x; j <= 10 - i; j++) {
int n = gen(i, j);
if (n < low) {
continue;
}
if (n > high) {
break;
}
ans.add(gen(i, j));
}
}
return ans;
}
private int highBit(int n, int len) {
for (int i = 0; i < len - 1; i++) { // 1000 4
n = n / 10;
}
return n;
}
private int len(int n) {
// 101 => 3
// 10 => 2
// 8 => 1
return (int) (Math.log10(n)) + 1;
}
private int gen(int len, int start) {
// 4 + 2 => 2345
int n = 0;
for (int i = 0; i < len; i++) {
n = n * 10 + start + i; // 2 => 23 ...
}
return n;
}
}
1310-子数组异或查询
简单前缀和问题,只不过把和变成异或了
class Solution {
public int[] xorQueries(int[] arr, int[][] queries) {
int xor[] = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
if (i == 0) {
xor[i] = arr[i];
} else {
xor[i] = xor[i - 1] ^ arr[i];
}
}
int ans[] = new int[queries.length];
for (int i = 0; i < queries.length; i++) {
int l = queries[i][0];
int r = queries[i][1];
if (l - 1 >= 0)
ans[i] = xor[r] ^ xor[l - 1];
else
ans[i] = xor[r];
}
return ans;
}
}
1373-二叉搜索子树的最大键值和
递归一下,返回当前子树的最大值、最小值、节点和,回溯时判断如果满足二叉搜索树的条件,则累加返回,注意一下叶子节点的判断逻辑
func maxSumBST(root *TreeNode) int {
ans := 0
dfs(root, &ans)
return ans
}
type R struct {
min, max, sum int
empty bool
}
func dfs(rt *TreeNode, ans *int) R {
if rt == nil {
return R{0, 0, 0, true}
}
left := dfs(rt.Left, ans)
right := dfs(rt.Right, ans)
if (left.empty || left.max < rt.Val) && (right.empty || rt.Val < right.min) {
sum := left.sum + rt.Val + right.sum
if sum > *ans {
*ans = sum
}
if left.empty {
left.min = rt.Val
}
if right.empty {
right.max = rt.Val
}
return R{left.min, right.max, sum, false}
}
return R{Min, Max, 0, false}
}
1361-验证二叉树
class Solution {
// 树:n 个节点,n - 1 条边,连通 (根节点入度为 0,其他都是 1,同时如果 n > 1 时根节点出度需要大于 0)
// 二叉树:每个节点的出度不超过 2
public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
int edgeCount = 0;
int in[] = new int[n];
int out[] = new int[n];
for (int i = 0; i < leftChild.length; i++) { // i -> leftChild[i]
if (leftChild[i] >= 0) {
edgeCount++;
in[leftChild[i]]++;
out[i]++;
}
}
for (int i = 0; i < rightChild.length; i++) { // i -> rightChild[i]
if (rightChild[i] >= 0) {
edgeCount++;
in[rightChild[i]]++;
out[i]++;
}
}
if (edgeCount != n - 1) {
return false;
}
int root = -1;
for (int i = 0; i < n; i++) {
if (in[i] == 0) {
if (root != -1) {
return false;
}
root = i;
if (n > 1 && out[i] <= 0) {
return false;
}
}
if (out[i] > 2) {
return false;
}
}
return true;
}
}
1492-n 的第 k 个因子
class Solution(object):
def kthFactor(self, n, k):
"""
:type n: int
:type k: int
:rtype: int
"""
tot = 0
for i in range(1, n + 1):
if (n % i == 0):
tot += 1
if (tot == k):
return i
return -1
1513-仅含 1 的子串数
遍历一次,然后得到每段 1 的长度,加一下就行了
int numSub(char *s) {
int len = strlen(s);
int mod = 1e9 + 7;
int ans = 0;
int cnt = 0;
for (int i = 0; i < len; i++) {
if (s[i] == '1') {
cnt++;
}
// 1 + 2 + 3 + ... + n
if ((s[i] == '0' || i == len - 1) && cnt > 0) {
int t = (long long) cnt * (cnt + 1) / 2 % mod;
ans = (ans + t) % mod;
cnt = 0;
}
}
return ans;
}
1519-子树中标签相同的节点数
DFS 搜一遍就行,递归返回当前子树的所有字母出现的次数,回溯时累加即可
func countSubTrees(n int, edges [][]int, labels string) []int {
graph := build(n, edges)
ans := make([]int, n)
dfs(graph, labels, 0, 0, ans)
return ans
}
func dfs(graph map[int][]int, labels string, u, pre int, ans []int) []int {
cnt := make([]int, 26)
cnt[labels[u] - 'a']++
for _, v := range graph[u] {
if v != pre {
vcnt := dfs(graph, labels, v, u, ans)
for i := 0; i < 26; i++ {
cnt[i] += vcnt[i]
}
}
}
ans[u] = cnt[labels[u] - 'a']
return cnt
}
func build(n int, edges [][]int) map[int][]int {
graph := make(map[int][]int, n)
for i := 0; i < len(edges); i++ {
u := edges[i][0]
v := edges[i][1]
if _, ok := graph[u]; !ok {
graph[u] = make([]int, 0)
}
if _, ok := graph[v]; !ok {
graph[v] = make([]int, 0)
}
graph[u] = append(graph[u], v)
graph[v] = append(graph[v], u)
}
return graph
}
1557-可以到达所有点的最少点数目
统计一下入度为 0 的点的个数即可
class Solution {
public:
vector<int> findSmallestSetOfVertices(int n, vector<vector<int>>& edges) {
vector<int> d(n);
vector<int> ans;
for (int i = 0; i < edges.size(); i++) {
int u = edges[i][0];
int v = edges[i][1];
d[v]++;
}
for (int i = 0; i < n; i++) {
if (d[i] == 0) {
ans.push_back(i);
}
}
return ans;
}
};
1582-二进制矩阵中的特殊位置
class Solution {
public int numSpecial(int[][] mat) {
int n = mat.length;
if (n <= 0) {
return 0;
}
int m = mat[0].length;
int[] sumRow = new int[n];
int[] sumCol = new int[m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mat[i][j] == 1) {
sumRow[i] ++;
sumCol[j] ++;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mat[i][j] == 1 && sumRow[i] == 1 && sumCol[j] == 1) {
ans++;
}
}
}
return ans;
}
}
1583-统计不开心的朋友
两个 for 遍历对比一下就行了
class Solution {
public int unhappyFriends(int n, int[][] preferences, int[][] pairs) {
Map<Integer, Map<Integer, Integer>> mpt = new HashMap();
for (int i = 0; i < n; i++) {
Map<Integer, Integer> inner = new HashMap<>();
int k = 0;
for (int j = n - 2; j >= 0; j--) {
inner.put(preferences[i][j], k++);
}
mpt.put(i, inner);
}
boolean[] vis = new boolean[n];
int ans = 0;
int k = pairs.length;
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
if (i == j) continue;
// x y
// pair[i][0] pair[i][1]
// pair[j][0] pair[j][1]
// u v
int x = pairs[i][0];
int y = pairs[i][1];
int u = pairs[j][0];
int v = pairs[j][1];
// x-y u-v
if (mpt.get(x).get(u) > mpt.get(x).get(y) && mpt.get(u).get(x) > mpt.get(u).get(v) && !vis[x]) {
vis[x] = true;
ans++;
}
// x-y v-u
else if (mpt.get(x).get(v) > mpt.get(x).get(y) && mpt.get(v).get(x) > mpt.get(v).get(u) && !vis[x]) {
vis[x] = true;
ans++;
}
// y-x u-v
if (mpt.get(y).get(u) > mpt.get(y).get(x) && mpt.get(u).get(y) > mpt.get(u).get(v) && !vis[y]) {
vis[y] = true;
ans++;
}
// y-x v-u
else if (mpt.get(y).get(v) > mpt.get(y).get(x) && mpt.get(v).get(y) > mpt.get(v).get(u) && !vis[y]) {
vis[y] = true;
ans++;
}
}
}
return ans;
}
}
1584-连接所有点的最小费用
最小生成树模板题
class Solution {
static int n;
static int[][] mpt;
static int[] dis;
static int[] cls;
static int INF = 0x3f3f3f3f;
public int minCostConnectPoints(int[][] points) {
n = points.length;
dis = new int[n];
cls = new int[n];
mpt = new int[n][n];
for (int i = 0; i < n; i++) {
mpt[i] = new int[n];
for (int j = 0; j < n; j++) {
mpt[i][j] = INF;
}
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
mpt[i][j] = mpt[j][i] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
}
}
return prim(0);
}
int prim(int s) {
int ans = 0;
for (int j = 0; j < n; j++) {
cls[j] = s;
dis[j] = mpt[s][j];
}
dis[s] = -1;
for (int i = 1; i < n; i++) {
int Min = INF, k = 0;
for (int j = 0; j < n; j++) {
if (dis[j] != -1 && dis[j] < Min) {
Min = dis[j];
k = j;
}
}
ans += dis[k];
dis[k] = -1;
for (int j = 0; j < n; j++) {
if (dis[j] != -1 && mpt[k][j] < dis[j]) {
dis[j] = mpt[k][j];
cls[j] = k;
}
}
}
return ans;
}
}
面试题21-调整数组顺序使奇数位于偶数前面
二指针,类似于快排
class Solution {
public int[] exchange(int[] nums) {
// 1 4 2 4 13 9 8 11 20
// 1 11 2 4 13 9 8 4 20
// 1 11 9 4 13 2 8 4 20
// 1 11 9 13 4 2 8 4 20
int l = 0, r = nums.length - 1;
while (l < r) {
while (l < r && (nums[r] & 1) == 0) r--; // 找到第一个奇数
while (l < r && (nums[l] & 1) == 1) l++; // 找到第一个偶数
if (l != r) {
int t = nums[l];
nums[l] = nums[r];
nums[r] =t;
}
}
return nums;
}
}
面试题04.01-节点间通路
BFS 一下就完事了
class Solution {
public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
Map<Integer, List<Integer>> edges = new HashMap();
for (int i = 0; i < graph.length; i++) {
int u = graph[i][0];
int v = graph[i][1];
if (edges.get(u) == null) {
List<Integer> to = new ArrayList<>();
edges.put(u, to);
}
edges.get(u).add(v);
}
boolean vis[] = new boolean[n];
Queue<Integer> q = new LinkedList<>();
q.add(start);
while (!q.isEmpty()) {
int u = q.poll();
if (u == target) {
return true;
}
List<Integer> to = edges.get(u);
if (to == null) continue;
for (int i = 0; i < to.size(); i++) {
int v = to.get(i);
if (!vis[v]) {
vis[v] = true;
q.add(v);
}
}
}
return false;
}
}
面试题04.10-检查子树
由于树比较大,所以直接暴露匹配可能有问题,所以将树转化为先序序列,使用字符串匹配即可。需要注意一下对空节点的处理
// 1
// / \
// 2 3
// /
// 4
//
// 1 2 4 x x x 3 x x
// 2
// / \
// 4 3
//
// 2 4 x x 3 x x
class Solution {
public boolean checkSubTree(TreeNode t1, TreeNode t2) {
String s1 = convert(t1);
String s2 = convert(t2);
return s1.indexOf(s2) >= 0;
}
private String convert(TreeNode t) {
StringBuilder buf = new StringBuilder();
dfs(t, buf);
return buf.toString();
}
private void dfs(TreeNode t, StringBuilder buf) {
if (t == null) {
buf.append('x');
return;
}
buf.append(t.val);
buf.append(",");
dfs(t.left, buf);
dfs(t.right, buf);
}
}
面试题04.12-求和路径
由于不一定从根节点开始和到叶子节点结束,如果对每个节点暴力搜索肯定不行。所以从根节点向下 dfs,记录一下前缀和,同时使用一个 map 记录前缀和为 s 的前缀有多少个,当到达某个节点时,则前缀和为 S 的路径数为 map[S - s]
class Solution {
int ans;
Map<Integer, Integer> vis = new HashMap<>();
public int pathSum(TreeNode root, int sum) {
if (root == null) {
return 0;
}
ans = 0;
dfs(root, sum, root.val);
return ans;
}
private void dfs(TreeNode rt, int S, int s) {
if (s == S) ans++;
ans += vis.getOrDefault(s - S, 0);
if (rt == null) {
return;
}
incr(s);
if (rt.left != null) {
dfs(rt.left, S, s + rt.left.val);
}
if (rt.right != null) {
dfs(rt.right, S, s + rt.right.val);
}
decr(s);
}
private void incr(int n) {
Integer cnt = vis.getOrDefault(n, 0);
vis.put(n, cnt + 1);
}
private void decr(int n) {
Integer cnt = vis.get(n);
vis.put(n, cnt - 1);
}
}
面试题08.01-三步问题
#define N 1000000
#define MOD 1000000007
int a[N + 10] = {0, 1, 2, 4};
int waysToStep(int n) {
if (a[n] > 0) return a[n];
for (int i = 4; i <= N; i++) {
a[i] = ((long long) a[i - 1] + a[i - 2] + a[i - 3]) % MOD;
}
return a[n];
}