1024 极限挑战
-
-
- 挑战 Leetcode 24题
-
-
-
- 01 剑指 Offer 03. 数组中重复的数字
- 02 1020. 飞地的数量
- 03 1015. 可被 K 整除的最小整数
- 04 529. 扫雷游戏
- 05 501. 二叉搜索树中的众数
- 06 557. 反转字符串中的单词 III
- 07 1351. 统计有序矩阵中的负数
- 08 剑指 Offer 05. 替换空格
- 09 868. 二进制间距
- 10 LCP 07. 传递信息
- 11 137. 只出现一次的数字 II
- 12 1302. 层数最深叶子节点的和
- 13 6. Z 字形变换
- 14 104. 二叉树的最大深度
- 15 剑指 Offer 57 - II. 和为s的连续正数序列
- 16 397. 整数替换
-
-
- 挑战情况
-
作为一个大四老萌新,第一次过1024,一时不知道给这天涂抹啥样的颜色。贫穷(各种意义上)限制了我的操作空间(哭)。但毕竟是1024嘛,一年也就一次,我想尽可能让这 1 366 \frac{1}{366} 3661与众不同一些。于是给自己安排了一些 ⌈ \lceil ⌈全天挑战 ⌋ \rfloor ⌋:
★ 尽可能在 ⌈ \lceil ⌈ 0 ⌋ \rfloor ⌋ 次错误的前提下于leetcode上随机AC ⌈ \lceil ⌈ 24 ⌋ \rfloor ⌋ 题,然后根据完成的题目的难度折合成百分制进行评分(简单 → \rightarrow → 3,中等 → \rightarrow → 4, 困难 → \rightarrow → 5 ),满分120,每不通过一次扣1分(扣分无下限)。
★ 写 ⌈ \lceil ⌈ 1 ⌋ \rfloor ⌋ 篇博客(正如您现在所见)记录"今日的颜色"
挑战 Leetcode 24题
下面我将把今天AC过的题目列出来,问题详情可以点击题目标题链接,简要说下思路和算法运行时间。
01 剑指 Offer 03. 数组中重复的数字
【难度】简单
【得分】2/3
【题目翻译】找出含n个元素的数组中的任一重复数字,且数字范围是[0, n-1], 2 ≤ n ≤ 100000 2 \le n \le 100000 2≤n≤100000
【思路】用桶方法对号入座。
【时/空复杂度】 O ( N ) O(N) O(N), O ( N ) O(N) O(N), N N N表示所给容器大小。
【执行情况】
【代码】
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
vector<int> arr(nums.size(), 0);
for (int a: nums){
if(arr[a])
return a;
else
++arr[a];
}
return 0;
}
};
【失分原因】把循环体中的arr
错写成nums
了。
02 1020. 飞地的数量
【难度】中等
【得分】2/4
【题目翻译】找不与边界陆地相连的陆地数
【思路】预处理+BFS
【时/空复杂度】: O ( N M ) O(NM) O(NM), O ( N M ) O(NM) O(NM),其中 N N N、 M M M分别表示二维数组行、列。
【执行情况】
【代码】
class Solution {
public:
int n, m;
bool isOnBorder(int x, int y){
if(x>0 && x<n-1 && y>0 && y<m-1)
return false;
return true;
}
int numEnclaves(vector<vector<int>>& A) {
n = A.size();
m = A[0].size();
int total=0; //陆地总数
int ans=0; //与边界点相连的单元格数
queue<pair<int, int>> que;
vector<vector<bool>> mark(n, vector<bool>(m, 0));
for (int i=0; i<n; ++i){
for (int j=0; j<m; ++j){
if(A[i][j] == 1){
++total;
if(isOnBorder(i, j)){
//边界点
mark[i][j] = true;
que.push(make_pair(i, j));
++ans;
}
}
}
}
//cout<<ans<<endl;
const int dx[4]{
0, 1, 0, -1};
const int dy[4]{
1, 0, -1, 0};
while (!que.empty()){
auto pos = que.front();
que.pop();
int x=pos.first, y=pos.second;
for (int k=0; k<4; ++k){
int mx = x + dx[k];
int my = y + dy[k];
if(mx<0 || mx>=n || my<0 || my>=m
|| mark[mx][my] || A[mx][my] == 0)
continue;
if(!isOnBorder(mx, my)){
//边界点只算一次
mark[mx][my] = true;
que.push(make_pair(mx, my));
++ans; //与边界相连的点+1
}
}
}
//cout<<total<<endl;
//cout<<ans<<endl;
ans = total - ans;
return ans;
}
};
【失分原因】
- 把边界陆地重复算了几次,且mark进行标记的程序位置不对;
- 审题出错:错误认为为行、列数相同
03 1015. 可被 K 整除的最小整数
【难度】中等
【得分】3/4
【题目翻译】返回由n
个1组成的可以被K
整除的正整数的位数,这些正整数的集合为 { 1 , 11 , 111 , 1111 , . . . } \{1, 11, 111, 1111, ...\} {
1,11,111,1111,...}
【思路】找到前后两个数的关系:比如 1111 = 10 ∗ 111 + 1 1111= 10*111 + 1 1111=10∗111+1,然后考虑取余。注意不能被整除的条件判断:若余数之前出现过,则表示不可能被整除。
【时/空复杂度】: O ( K ) O(K) O(K), O ( K ) O(K) O(K),其中 K K K为除数,因为最坏情况为:每一轮出现不同的余数,而余数最多有 K K K个。
【执行情况】
【代码】
class Solution {
public:
int smallestRepunitDivByK(int K) {
int ans=1;
int rem=1 % K;
vector<bool> mark(K, 0);
// rem = (pre_remain*10 + 1) % K
//特例->6
while (rem){
mark[rem] = true;
int cur_rem = (10*rem+1) % K;
if(mark[cur_rem])
return -1;
rem = cur_rem;
++ans;
}
return ans;
}
};
【失分原因】不能被整除时的条件考虑错了,从而永远跳不出循环。
04 529. 扫雷游戏
【难度】中等
【得分】4/4
【题目翻译】模拟扫雷游戏,根据点击的块类型进行不同的操作。
【思路】与第2题思路差不多:BFS,只不过斜对角线也得考虑。虽然这题一遍过,但耗了大量时间,原因在于:①测试用例JSON解析有误(直接从题目示例复制粘贴的),花了大量时间debug,这告诉我们不要随意复制,手动输入最稳;②注意附近有雷的块不能继续递归下去,因此判断雷的数目需要在加入队列操作之前!
【时/空复杂度】: O ( N M ) O(NM) O(NM), O ( N M ) O(NM) O(NM),其中 N N N、 M M M分别表示二维数组行、列。
【执行情况】
【代码】
class Solution {
public:
int n, m;
vector<vector<char>> updateBoard(vector<vector<char>>& board,
vector<int>& click) {
if(board[click[0]][click[1]] == 'M'){
//直接踩雷
board[click[0]][click[1]] = 'X';
return board;
}
else{
//点击'E'处
n = board.size();
m = board[0].size();
queue<pair<int, int>> que;
vector<vector<bool>> mark(n, vector<bool>(m, 0));
que.push(make_pair(click[0], click[1]));
mark[click[0]][click[1]] = true;
const int dx[8]{
0, 1, 0, -1, 1, 1, -1, -1};
const int dy[8]{
1, 0, -1, 0, 1, -1, -1, 1};
while (!que.empty()){
auto pos = que.front();
que.pop();
int x = pos.first, y = pos.second;
int cnt=0; //周围炸弹数
for (int k=0; k<8; ++k){
int mx=x+dx[k], my=y+dy[k];
if(mx<0 || mx>=n || my<0 || my>=m)
continue;
if(board[mx][my] == 'M')
++cnt;
}
if(cnt)
board[x][y] = '0' + cnt;
else{
//'B'
//把一些节点加入队列
for (int k=0; k<8; ++k){
int mx=x+dx[k], my=y+dy[k];
if(mx<0 || mx>=n || my<0 || my>=m
|| mark[mx][my])
continue;
if(board[mx][my] == 'E'){
mark[mx][my] = true;
que.push(make_pair(mx, my));
}
}
board[x][y] = 'B';
}
}
return board;
}
}
};
05 501. 二叉搜索树中的众数
【难度】简单
【得分】1/3
【题目翻译】给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
【思路】中序遍历得到的序列为非降序序列,在遍历过程中记录当前数字出现次数和过程最大次数,并更新存储众数的容器。
【时/空复杂度】: O ( N ) O(N) O(N), O ( H ) O(H) O(H),其中 N N N为节点数目, H H H为递归深度,最坏情况下空间开销为 O ( N ) O(N) O(N)
【执行情况】
【代码】
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int pre;
int cnt, maxn;
void inorder(TreeNode* root, vector<int>& v){
if(!root)
return;
inorder(root->left, v);
if(pre == root->val){
++cnt;
if(cnt > maxn){
maxn = cnt;
v.clear();
v.push_back(pre);
}
else if(cnt == maxn){
v.push_back(pre);
}
}
else{
cnt = 1;
if(cnt >= maxn){
v.push_back(root->val);
}
}
pre = root->val;
inorder(root->right, v);
}
vector<int> findMode(TreeNode* root) {
vector<int> nums;
if(!root)
return nums;
if(root->val == INT_MIN)
pre = root->val + 1;
else
pre = root->val - 1;
cnt=0;
maxn = 0;
inorder(root, nums);
return nums;
}
};
【失分原因】
- 没考虑溢出的测试边界(pre可能溢出);
- 没考虑空树
06 557. 反转字符串中的单词 III
【难度】简单
【得分】3/3
【题目翻译】给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序,其中不存在连续的空格。
【思路】逐个单词翻转,原地修改
【时/空复杂度】: O ( N ) O(N) O(N), O ( 1 ) O(1) O(1),其中 N N N为字符串长度
【执行情况】
【代码】
class Solution {
public:
void revStr(string& s, int L, int R){
if(L>=R)
return;
int mid = (L+R) / 2;
for (int i=L; i<=mid; ++i){
char temp = s[i];
s[i] = s[R+L-i];
s[R+L-i] = temp;
}
}
string reverseWords(string s) {
int n = s.length();
int start=0, end=0;
while (end < n){
while (end<n && s[end] != ' ')
++end;
//cout<<end-1<<endl;
revStr(s, start, end-1);
end++;
start = end;
}
return s;
}
};
07 1351. 统计有序矩阵中的负数
【难度】简单
【得分】3/3
【题目翻译】给你一个 N * M
的矩阵 grid
,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。请你统计并返回 grid
中 负数 的数目。
【思路】对每行(列)二分查找到第一个负数,即可得到该行(列)负数个数,然后求和即可。
【时/空复杂度】: O ( N l o g M ) O(NlogM) O(NlogM), O ( 1 ) O(1) O(1),其中 N N N、 M M M分别为矩阵的行和列。
【执行情况】
【代码】
class Solution {
public:
int binarySearch(const vector<int>& v, int m){
int left = 0, right = m-1;
while (left <= right){
int mid = (left+right) / 2;
if(v[mid] < 0)
right = mid-1;
else
left = mid+1;
}
return right+1;
}
int countNegatives(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
int ans=0;
for (int i=0; i<n; ++i){
ans += m-binarySearch(grid[i], m);
}
return ans;
}
};
08 剑指 Offer 05. 替换空格
【难度】简单
【得分】3/3
【题目翻译】请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
【思路】建立一个返回串,搬运非空格字符,遇到空字符纳入"%20"即可。
【时/空复杂度】: O ( N ) O(N) O(N), O ( N ) O(N) O(N),其中 N N N为字符串长度。
【执行情况】
【代码】
class Solution {
public:
string replaceSpace(string s) {
string ans="";
for (auto& ch: s){
if(ch == ' '){
ans += "%20";
}
else{
char chs[] = {
ch, '\0'};
ans += chs;
}
}
return ans;
}
};
09 868. 二进制间距
【难度】简单
【得分】3/3
【题目翻译】求正整数N
二进制表示中两个1
的最远距离。
【思路】这题我小看了。可能是做了一天了脑袋炸了,以至于折腾了半天才想明白:一位位的处理,最好画个树状图把情况理清楚,否则大脑一团浆糊。
【时/空复杂度】: O ( C ) O(C) O(C), O ( C ) O(C) O(C),其中 C C C为正整数二进制最大位数,这里 C C C取32。
【执行情况】
【代码】
class Solution {
public:
int binaryGap(int n) {
int bin[32]{
0};
int p=0;
while (n){
bin[p] = (n&1);
++p;
n >>= 1;
}
int left = -1, right = -1, pre = bin[0];
if(bin[0]){
left = 0;
}
int maxn = 0;
for (int i=1; i<p; ++i){
if(pre == 0){
if(bin[i] == 0){
if(left != -1){
right = i;
}
}
else{
if(left == -1){
left = i;
}
else{
right = i;
maxn = max(maxn, right - left);
left = right;
}
}
}
else if(pre == 1){
if(bin[i] == 0){
right = i;
}
else{
maxn = max(maxn, 1);
left = i;
right = i;
}
}
pre = bin[i];
}
return maxn;
}
};
10 LCP 07. 传递信息
【难度】简单
【得分】3/3
【题目翻译】有向图中起、终点之间的路径长度恰好为 K K K的方案数(可以重复经过同一节点)。
【思路】建立有向图,然后不带访问标记的DFS。
【时/空复杂度】: O ( N K ) O(N^K) O(NK), O ( N 2 ) O(N^2) O(N2),其中 N N N为节点数, K K K为轮数,最坏情况为:有向完全图;空间开销主要为建立有向图。
【执行情况】
【代码】
class Solution {
public:
vector<vector<int>> graph;
int target;
int N;
void dfs(int k, int cur, int& ans){
if(k >= target){
if(cur == N-1){
++ans;
}
return;
}
for (auto& x: graph[cur]){
dfs(k+1, x, ans);
}
}
int numWays(int n, vector<vector<int>>& relation, int k) {
graph = vector<vector<int>>(n);
target = k;
N = n;
int ans=0;
for (vector<int>& v: relation){
graph[v[0]].push_back(v[1]);
}
for (auto& x: graph[0]){
dfs(1, x, ans);
}
return ans;
}
};
11 137. 只出现一次的数字 II
【难度】中等
【得分】4/4
【题目翻译】给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
【思路】哈希表。我想过位运算,但是实在不知道异或搭配啥运算才能区分出现1次和出现3次的元素,所以放弃了(如果用位运算的话空间开销为 O ( 1 ) O(1) O(1))。
【时/空复杂度】: O ( N ) O(N) O(N), O ( N ) O(N) O(N),其中 N N N为数组容器大小。
【执行情况】
【代码】
class Solution {
public:
int singleNumber(vector<int>& nums) {
map<int, int> hash;
for (int i=0; i<nums.size(); ++i){
hash[nums[i]]++;
}
for (auto& item: hash){
if(item.second == 1)
return item.first;
}
return -1;
}
};
12 1302. 层数最深叶子节点的和
【难度】中等
【得分】4/4
【题目翻译】给你一棵二叉树,请你返回层数最深的叶子节点的和。
【思路】两遍DFS:获得树高度;把节点深度为树高的节点值求和。不过还可以更简单,一次DFS就能搞定,之后再看看吧。
【时/空复杂度】: O ( N ) O(N) O(N), O ( H ) O(H) O(H),其中 N N N为树的节点总数, H H H为递归深度,即树的高度,最坏时空间开销趋于 O ( N ) O(N) O(N)。
【执行情况】
【代码】
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int level, maxn;
void dfs1(TreeNode* root){
if(!root)
return;
maxn = max(maxn, level);
level++;
dfs1(root->left);
dfs1(root->right);
level--;
}
void dfs2(TreeNode* root, int& ans){
if(!root)
return;
if(level == maxn){
ans += root->val;
}
++level;
dfs2(root->left, ans);
dfs2(root->right, ans);
--level;
}
int deepestLeavesSum(TreeNode* root) {
if(!root)
return 0;
level = 1;
maxn = 1;
dfs1(root);
int ans=0;
level = 1;
dfs2(root, ans);
return ans;
}
};
13 6. Z 字形变换
【难度】中等
【得分】3/4
【题目翻译】将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
【思路】找规律。过程中注意第一行和最后一行的处理;另外考虑只有1行的情况。
【时/空复杂度】: O ( N ) O(N) O(N), O ( 1 ) O(1) O(1),其中 N N N为字符串长度。
【执行情况】
【代码】
class Solution {
public:
string convert(string s, int numRows) {
if(numRows == 1)
return s;
string ret = "";
int n = s.length();
for (int i=0; i<numRows; ++i){
int k = 2*(numRows-1-i);
int idx = i;
while (idx<n){
ret.push_back(s[idx]);
if(i == numRows-1){
k = 2*(numRows-1);
idx += k;
}
else
idx += k;
if(i!= 0 && i!= numRows-1)
k = 2*(numRows-1)-k;
}
}
return ret;
}
};
【失分原因】没考虑只有1行的情况,陷入无限循环。。。
14 104. 二叉树的最大深度
【难度】简单
【得分】3/3
【题目翻译】给定一个二叉树,找出其最大深度。
【思路】半分钟搞定,递归即可。
【时/空复杂度】: O ( N ) O(N) O(N), O ( H ) O(H) O(H),其中 N N N为节点数目, H H H为树的高度,也即递归深度,空间开销最坏趋于 O ( N ) O(N) O(N)。
【执行情况】
【代码】
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if(!root)
return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return max(left, right) + 1;
}
};
15 剑指 Offer 57 - II. 和为s的连续正数序列
【难度】简单
【得分】3/3
【题目翻译】输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
【思路】只剩下1小时20分钟了。。。脑力用尽了,只能使用暴力DFS了。
【时/空复杂度】: O ( N N ) O(N\sqrt{N}) O(NN), O ( H ) O(H) O(H),其中 N N N即 t a r g e t target target, H H H为递归深度,空间开销最坏趋于 O ( N ) O(\sqrt{N}) O(N)。
【执行情况】
【代码】
class Solution {
public:
vector<int> temp;
vector<vector<int>> ret;
void dfs(int cur, int k){
temp.push_back(cur);
if(k == 0){
ret.push_back(temp);
temp.pop_back();
return;
}
else if(k<0)
return;
dfs(cur+1, k-cur-1);
}
vector<vector<int>> findContinuousSequence(int target) {
if(target == 1)
return ret;
for (int i=1; i <= target / 2; ++i){
temp.clear();
dfs(i, target-i);
}
return ret;
}
};
16 397. 整数替换
【难度】中等
【得分】-1/4
【题目翻译】给定一个正整数 n,你可以做如下操作:
- 如果 n 是偶数,则用 n / 2替换 n。
- 如果 n 是奇数,则可以用 n + 1或n - 1替换 n。
问:n 变为 1 所需的最小替换次数是多少?
【思路】递归解决,注意最大值、n为1等边界情况。另外,记忆化搜索会超时。
【时/空复杂度】: O ( l o g N ) O(logN) O(logN), O ( 1 ) O(1) O(1),其中 N N N即 n n n,这里的时间复杂度分析不太确定
【执行情况】
【代码】
class Solution {
public:
vector<int> temp;
vector<vector<int>> ret;
void dfs(int cur, int k){
temp.push_back(cur);
if(k == 0){
ret.push_back(temp);
temp.pop_back();
return;
}
else if(k<0)
return;
dfs(cur+1, k-cur-1);
}
vector<vector<int>> findContinuousSequence(int target) {
if(target == 1)
return ret;
for (int i=1; i <= target / 2; ++i){
temp.clear();
dfs(i, target-i);
}
return ret;
}
};
挑战情况
现在是23:30,目前总共AC了16道题,即题目完成度为16/24。做题数目上没有达成目标,当然有一部分原因是开始得晚:今日上午11点左右开始的,最主要原因还是历练不够,考虑问题不够全面。这个过程中除了以下题目没弄出来或不想做而跳过外,其余题目基本没跳(当然,困难题跳了)。
那么就做个简单的结算吧~
本次挑战一共完成了 9 道 ⌈ \lceil ⌈ 简单 ⌋ \rfloor ⌋题,7 道 ⌈ \lceil ⌈ 中等 ⌋ \rfloor ⌋题, 0 道 ⌈ \lceil ⌈ 困难 ⌋ \rfloor ⌋题
- 按题目完成率:16/24 → \rightarrow → 66.7 % 66.7\% 66.7%
- 按总分120,总共得分:43/120 → \rightarrow → 36/100。不及格嘿(哭)~
- 按照已做题目的得分:43/55 → \rightarrow → 78/100,看着舒服些了~
另外,过程中动手但未提交或者直接跳过的题目列表:
-
611 有效三角形
-
808 分汤
-
703 数据流中的第 K 大元素
-
669 修剪二叉搜索树
-
264 丑数II
-
1030 距离顺序排列矩阵单元格
总之,还有很多需要改进的地方,不论是解题思路还是经验积累~在今后的日子一定得再接再厉!!