LeetCode Weekly Contest 47解题思路
来源:互联网 发布:阿里巴巴淘宝城地址 编辑:程序博客网 时间:2024/05/22 14:21
LeetCode Weekly Contest 47解题思路
详细代码可以fork下Github上leetcode项目,不定期更新。
赛题
本次周赛主要分为以下4道题:
- Leetcode 665. Non-decreasing Array
- Leetcode 666. Path Sum IV
- Leetcode 667. Beautiful Arrangement II
- Leetcode 668. Kth Smallest Number in Multiplication Table
Leetcode 665. Non-decreasing Array
Problem:
Given an array with n integers, your task is to check if it could become non-decreasing by modifying at most 1 element.
We define an array is non-decreasing if array[i] <= array[i + 1] holds for every i (1 <= i < n).
Example 1:
Input: [4,2,3]
Output: True
Explanation: You could modify the first 4 to 1 to get a non-decreasing array.
Example 2:
Input: [4,2,1]
Output: False
Explanation: You can’t get a non-decreasing array by modify at most one element.
Note:
The n belongs to [1, 10,000].
思路:直接检测出合法状态(说白了,就是找到合法状态的充分必要条件),开始构思。
第一:可以找寻所有arra[i] < arra[i - 1] 的状态,对其计数,所以很简单,如果非法状态>=2,直接return false.
可以想象,cnt = 0 ,说明arra是非递减的,一定合法,直接return true。
那么cnt = 1的情况呢?我是提交时才发现的问题,主要是因为单纯的计数cnt,有一种状态检测不出,即在检测到非法状态的下标j后,前数组满足非递减,后数组满足非递减,而删除j后,这两种状态不能合法叠加。
删除即合并,想复杂了,合并的情况直接考察是否满足即可,那么删除j还是删除j-1?如果删除j,这意味着j+1的元素和j-1的元素符合非递减,需要检测一波。如果删除j-1,需要检测j元素和j-2的元素是否符合非递减。
所以该问题就是两个步骤:找非法状态,非法状态太多,不符合,非法状态在指定范围内,检测能否合并,是个模拟啊!
代码如下:
public boolean checkPossibility(int[] nums) { int n = nums.length; int cnt = 0; int j = -1; for (int i = 1; i < n; ++i){ if (nums[i] < nums[i - 1]){ cnt ++; j = i; } } if (cnt == 0) return true; if (cnt == 1){ if (j == 1 || (j - 2 >= 0) && nums[j] >= nums[j - 2]) return true; if (j == n - 1 || (j + 1 < n) && nums[j + 1] >= nums[j - 1]) return true; } return false; }
discuss还有一种做法,代码简单,思路还是一个,只不过更有技巧性,巧妙的改变值,来替代合并操作。
也分为两种情况,要么i-2的元素比i小,要么比i大,比i小的将i-1改为i,比i大的将i改为i-1,接着再来检测。
代码如下:
public boolean checkPossibility(int[] nums) { int n = nums.length; int cnt = 0; for (int i = 1; i < n; ++i){ if (nums[i] < nums[i - 1]){ cnt ++; if (i - 2 >= 0 && nums[i - 2] > nums[i]) nums[i] = nums[i - 1]; else nums[i - 1] = nums[i]; } } return cnt <= 1; }
Leetcode 666. Path Sum IV
Problem:
If the depth of a tree is smaller than 5, then this tree can be represented by a list of three-digits integers.
For each integer in this list:
- The hundreds digit represents the depth D of this node, 1 <= D <= 4.
- The tens digit represents the position P of this node in the level it belongs to, 1 <= P <= 8. The position is the same as that in a full binary tree.
- The units digit represents the value V of this node, 0 <= V <= 9.
Given a list of ascending three-digits integers representing a binary with the depth smaller than 5. You need to return the sum of all paths from the root towards the leaves.
Example 1:
Input: [113, 215, 221]
Output: 12
Explanation:
The tree that the list represents is:
3
/ \
5 1The path sum is (3 + 5) + (3 + 1) = 12.
Example 2:
Input: [113, 221]
Output: 4
Explanation:
The tree that the list represents is:
3
\
1The path sum is (3 + 1) = 4.
思路:
这道题我的思路很暴力,从叶子结点开始搜索一条有效路径,并且累加,当然搜索路径的过程中,被访问过的结点一定不属于叶子结点,用一个visited数组记录,接着直到所有的叶子结点被访问过,程序结束。
至于如何访问叶子结点到根结点的路径,可以直接借用堆的建树规则。
我的初级代码如下:
public int pathSum(int[] nums) { int sum = 0; int i = nums.length - 1; boolean[] leaf = new boolean[nums.length]; Arrays.fill(leaf, true); while (i >= 0){ if (leaf[i]){ int val = nums[i] % 10; int pos = nums[i] / 10 % 10; int dep = nums[i] / 100; int layer = dep; sum += val; for (int k = 1; k < dep; ++k){ for (int j = i - 1; j >= 0; --j){ int pp = nums[j] / 10 % 10; int vl = nums[j] % 10; int ll = nums[j] / 100; if ((pos + 1) / 2== pp && ll < layer){ leaf[j] = false; sum += vl; pos = pp; layer = ll; break; } } } i--; } else i--; } return sum; }
当然你可以用for循环,因为if和else都需要i–,所以代码如下:
public int pathSum(int[] nums) { int sum = 0; int n = nums.length; boolean[] leaf = new boolean[nums.length]; Arrays.fill(leaf, true); for (int i = n - 1; i >= 0; --i){ if (leaf[i]){ int val = nums[i] % 10; int pos = nums[i] / 10 % 10; int dep = nums[i] / 100; int layer = dep; sum += val; for (int k = 1; k < dep; ++k){ for (int j = i - 1; j >= 0; --j){ int pp = nums[j] / 10 % 10; int vl = nums[j] % 10; int ll = nums[j] / 100; if ((pos + 1) / 2== pp && ll < layer){ leaf[j] = false; sum += vl; pos = pp; layer = ll; break; } } } } } return sum; }
当然,如果面对一棵大树,上述代码理论上还不够快,因为我们可以直接定位每一层的结点,思路还是一个,只是先用map记录层数和位置,接着拿到叶子结点后, 直接定位,代码如下:
public int pathSum(int[] nums) { int sum = 0; int n = nums.length; boolean[][] nleaf = new boolean[5][12]; Map<Integer, Integer> map = new HashMap<>(); for (int num : nums){ int val = num % 10; int pos = num / 10 % 10; int dep = num / 100; map.put(dep * 8 + pos, val); } for (int i = n - 1; i >= 0; --i){ int val = nums[i] % 10; int pos = nums[i] / 10 % 10; int dep = nums[i] / 100; if (!nleaf[dep][pos]){ sum += val; for (int k = 1; k < dep; ++k){ pos = (pos + 1 ) / 2; int key = (dep - k) * 8 + pos; nleaf[dep - k][pos] = true; sum += map.get(key); } } } return sum; }
当然map你也可以用一个二维数组做,这里就不写了,map更加直观。上述是迭代版本,你也可以用递归哟,这样就不需要记录是否为叶子结点咯。
Leetcode 667. Beautiful Arrangement II
Problem:
Given two integers n and k, you need to construct a list which contains n different positive integers ranging from 1 to n and obeys the following requirement:
Suppose this list is [a1, a2, a3, … , an], then the list [|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] has exactly k distinct integers.If there are multiple answers, print any of them.
Example 1:
Input: n = 3, k = 1
Output: [1, 2, 3]
Explanation: The [1, 2, 3] has three different positive integers ranging from 1 to 3, and the [1, 1] has exactly 1 distinct integer: 1.
Example 2:
Input: n = 3, k = 2
Output: [1, 3, 2]
Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2.
Note:
The n and k are in the range 1 <= k < n <= 104.
思路:
此题其实很简单,因为k一定比n小,而我们知道,对于1toN的整数来说,最大的gap为n-1,所以当给定一个k时,一定优先满足k的gap,再满足k-1的gap,直到k=0。
这里可以证明下,假设某个数a,优先满足较小的gap,那么下一个数为
那么,从哪个数开始构建数组呢?从
那么如何保证没有被安排的元素是否会出现新的gap?哈哈,其实也很好理解,因为在构造时,我们的策略是一加一减,把一加一减看成一个整体,两次操作,末尾元素+1,那么当迭代结束时,末尾元素为:
举个简单的例子就能理解了:
n = 12, k = 5; +5-4+3-2+11 6 2 5 3 4 | 7 8 9 10 11 12n = 12, k = 6; +6-5+4-3+2-11 7 2 6 3 5 4 | 8 9 10 11 12
所以代码如下:
public int[] constructArray(int n, int k) { int[] ans = new int[n]; ans[0] = 1; int j = 0; boolean[] used = new boolean[n + 1]; used[1] = true; used[0] = true; while (k > 0){ j ++; if (j % 2 != 0){ ans[j] = ans[j - 1] + k; used[ans[j]] = true; } else{ ans[j] = ans[j - 1] - k; used[ans[j]] = true; } k--; } for (int i = 1; i < used.length; ++i){ if (!used[i]){ ans[++j] = i; } } return ans; }
有了上述结论,这初级代码可以优化成:
public int[] constructArray(int n, int k) { int[] ans = new int[n]; ans[0] = 1; int j = 0; int iter = k; while (iter > 0){ j ++; if (j % 2 != 0){ ans[j] = ans[j - 1] + iter; } else{ ans[j] = ans[j - 1] - iter; } iter--; } for (int i = k + 2; i <= n; ++i){ ans[++j] = i; } return ans; }
Leetcode 668. Kth Smallest Number in Multiplication Table
Problem:
Nearly every one have used the Multiplication Table. But could you find out the k-th smallest number quickly from the multiplication table?
Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, you need to return the k-th smallest number in this table.
Example 1:
Input: m = 3, n = 3, k = 5
Output:
Explanation:
The Multiplication Table:
1 2 3
2 4 6
3 6 9The 5-th smallest number is 3 (1, 2, 2, 3, 3).
Example 2:
Input: m = 2, n = 3, k = 6
Output:
Explanation:
The Multiplication Table:
1 2 3
2 4 6The 6-th smallest number is 6 (1, 2, 2, 3, 4, 6).
Note:
- The m and n will be in the range [1, 30000].
- The k will be in the range [1, m * n]
二分题,还是挺简单的,对于每一行,对要猜的答案进行个数统计,kth-smallest的充分必要条件为:
- 不超过x的数不少于k个
- 小于x的数有不到k个
所以二分很好写,有点类似区间搜索,[lo, hi),其中lo满足 count(lo) < k,而count(hi) >= k,如何写count函数?对于每一行分别统计,一开始想用binarySearch,但没必要,统计函数为:cnt += Math.min(mid / i, n);
嘿嘿,整体代码如下:
public int findKthNumber(int m, int n, int k) { int lo = 0, hi = 1000000000; while (hi - lo > 1){ int mid = (lo + hi) / 2; int cnt = 0; for (int i = 1; i <= m; ++i){ cnt += Math.min(mid / i, n); } if (cnt < k){ lo = mid; } else{ hi = mid; } } return hi; }
起初纠结 hi为什么是最终的答案,该答案一定会在m*n这张表里么?答案是一定的,因为if-else在更新mid时,始终满足lo < k && hi >= k,如果hi不在表里,那么计数器是不会increase的,那么自然的也不可能是这个答案了,所以只有hi在表格里时,counter才会增加。
- LeetCode Weekly Contest 47解题思路
- LeetCode Weekly Contest 26解题思路
- LeetCode Weekly Contest 27解题思路
- LeetCode Weekly Contest 28解题思路
- LeetCode Weekly Contest 29解题思路
- LeetCode Weekly Contest 30解题思路
- LeetCode Weekly Contest 31解题思路
- LeetCode Weekly Contest 32解题思路
- LeetCode Weekly Contest 33解题思路
- LeetCode Weekly Contest 34解题思路
- LeetCode Weekly Contest 35解题思路
- LeetCode Weekly Contest 36解题思路
- LeetCode Weekly Contest 37解题思路
- LeetCode Weekly Contest 38解题思路
- LeetCode Weekly Contest 39解题思路
- LeetCode Weekly Contest 40解题思路
- LeetCode Weekly Contest 41解题思路
- LeetCode Weekly Contest 42解题思路
- C++构造函数和编译器自动生成代码的陷阱
- 数据库事务中的隔离级别和锁
- 小白文科生眼中的Linux系统
- POJ2004-Mix and Build
- DLL编程一一对应头文件约定
- LeetCode Weekly Contest 47解题思路
- |与||,&与&&区别
- 程序员必备的代码审查(Code Review)清单
- 安装opensips遇到的错误
- runtime理解
- 《Effective C++读书笔记》--条款07:为多态基类声明virtual析构函数
- java.net.SocketException四大异常解决方案
- ubuntu15.4、16.4、17.4设置nginx自启动
- React-Native Android真机测试 -unable to load script from assets 'index.android bundle'...