LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段相关的知识,希望对你有一定的参考价值。
810. 黑板异或游戏
2021.5.22每日一题
题目描述
黑板上写着一个非负整数数组 nums[i] 。Alice 和 Bob 轮流从黑板上擦掉一个数字,Alice 先手。如果擦除一个数字后,剩余的所有数字按位异或运算得出的结果等于 0 的话,当前玩家游戏失败。 (另外,如果只剩一个数字,按位异或运算得到它本身;如果无数字剩余,按位异或运算结果为 0。)
换种说法就是,轮到某个玩家时,如果当前黑板上所有数字按位异或运算结果等于 0,这个玩家获胜。
假设两个玩家每步都使用最优解,当且仅当 Alice 获胜时返回 true。
示例:
输入: nums = [1, 1, 2]
输出: false
解释:
Alice 有两个选择: 擦掉数字 1 或 2。
如果擦掉 1, 数组变成 [1, 2]。剩余数字按位异或得到 1 XOR 2 = 3。那么 Bob 可以擦掉任意数字,因为 Alice 会成为擦掉最后一个数字的人,她总是会输。
如果 Alice 擦掉 2,那么数组变成[1, 1]。剩余数字按位异或得到 1 XOR 1 = 0。Alice 仍然会输掉游戏。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/chalkboard-xor-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
看到这个题,感觉以前有道每日一题出过个类似的,也是两个人玩游戏,从两边选,然后都是最优解;还有一个做游戏的题是肯定一个人赢,直接true就完事。。。这道题不会也是那样吧。。。
刚开始写了个代码,是从左右两边轮流取的,结果发现看了答案,怎么是任意取,回去一看题,是自己看错了…
看了答案,还真是…
首先,如果开始的时候,整个数组异或为0,那么直接胜利
其次,除了这种特殊情况,因为每次取出一个数字,对于两个人来说,剩下数组长度的奇偶性永远是一致的,因此考虑从奇偶性上出发,得到结果
如果nums长度是偶数,如果先手可能失败,那么这种情况是,无论取出哪一个数字,剩下所有数字的异或结果为0
反证法
如果数组长度为n,并且所有元素异或结果为S,并且S != 0
Si为取出nums[i]后的结果,Si ^ nums[i] = S => Si = nums[i] ^ S
假设无论取出哪个数字,剩余数字的异或结果都是0,即Si = 0, 那么S0 ^ S1 ^ … ^^ Sn-1 = 0也成立
将Si = nums[i] ^ S 代入上式,因为n为偶数
得到(S ^ S … ^ S) ^(nums[0] ^ nums[1] ^ … nums[n - 1]) = 0 ^ S = S = 0
得到S = 0,与S != 0矛盾,说明当长度是偶数时,先手方一定能找到一个数字,使剩下的数字异或结果不为0
因此如果长度是偶数,先手方不败
同理,如果是奇数,先手方必败
class Solution {
public boolean xorGame(int[] nums) {
//整了半天,还是数学推导,直接出答案...
if(nums.length % 2 == 0)
return true;
int sum = 0;
for(int num : nums)
sum ^= num;
return sum == 0;
}
}
看到两个理解:
数组长度为偶数时,全部元素的异或结果S:
S=0 赢
S≠0 数组中必然存在至少一个不等于S的数,因为偶数个S异或结果应该是0。
找到任意一个不等于S的数擦掉,即可确保剩余元素异或结果不为0
若这些偶数个数字异或结果不为 0,则说明结果中至少有一位为 1,那么说明有且仅有奇数个数字该位的值为 1
所以至少有一个数字该位的值为 0,那么取走该数剩余数字的的异或结果该位的值也一定为 1,保证了剩余数字的异或结果不为 0.
455. 分发饼干
题目描述
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/assign-cookies
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
今天学习内容是贪心,给了个这个题,之前也做过
这个贪心还是比较容易想的,就是把尽量用最小的饼干满足孩子,那么剩下的饼干就可以满足更多的孩子
class Solution {
public int findContentChildren(int[] g, int[] s) {
//简单想法:排序然后双指针,争取用最小的尺寸满足孩子
Arrays.sort(g);
Arrays.sort(s);
int i = 0;
int j = 0;
while(i < g.length && j < s.length){
if(g[i] <= s[j]){
i++;
j++;
}else{
j++;
}
}
return i;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
题目描述
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
一看排序数组找数,就是二分,找最左边和最右边出现的位置
class Solution {
public int search(int[] nums, int target) {
int l = nums.length;
if(l == 0)
return 0;
int left = 0;
int right = l - 1;
while(left < right){
int mid = (left + right) >> 1;
if(nums[mid] >= target){
right = mid;
}else{
left = mid + 1;
}
}
if(nums[left] != target)
return 0;
int start = left;
left = 0;
right = l; //因为是向下取整,所以不会越界
while(left < right){
int mid = (left + right) >> 1;
if(nums[mid] <= target)
left = mid + 1;
else
right = mid;
}
int end = left;
return end - start;
}
}
又看到二分还是想到最初看着weiwei哥的书学的二分,所以导致后面写二分条件都是写的while(left < right),受益匪浅
查找出现的第一个位置很简单,就是普通的二分,当中间数小于目标值,left = mid + 1,这样可以保证最后left指向的数肯定是出现的第一个目标值
找最后一个出现的目标值时,其实就是找,第一个大于目标值的数,这里改成,当中间数小于等于目标值时,left = mid + 1;这样可以保证left最终指向的不是target,但是如果数组中最后一个数也是目标值,那么算法就会出错,因此这里把right从l - 1 改成 l, 这样当数组中最后一个数也是target时,left就会指向l,也就是不存在的位置,保证最终输出结果成立(另外,因为是向下取整,所以不会越界)
或者是在查找最后一个出现的目标值时,可以将mid变成向上取整,相应的查找逻辑也要变化,这样,用二分查找找到的数就是目标值出现的最右位置,输出就是end - start + 1
class Solution {
public int search(int[] nums, int target) {
int l = nums.length;
if(l == 0)
return 0;
int left = 0;
int right = l - 1;
while(left < right){
//一般的向下取整,找到的是目标值第一次出现的位置
int mid = (left + right) >> 1;
if(nums[mid] >= target){
right = mid;
}else{
left = mid + 1;
}
}
if(nums[left] != target)
return 0;
int start = left;
left = 0;
right = l - 1;
while(left < right){
//向上取整,找到的是最后一次出现目标值的位置
int mid = (left + right + 1) >> 1;
//如果中值小于等于目标值
if(nums[mid] <= target)
left = mid;
//如果中值大于目标值
else
right = mid - 1;
}
int end = left;
return end - start + 1;
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字
题目描述
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
思路
没啥好说的,二分
这个二分我之前写的是right = l - 1,然后再最后对特殊情况(最后一位缺失)的情况进行判断,这里沿用刚刚做的,直接把right改成 l ,也能过,还是那个道理
当判断最后一个数字时,需要扩大边界,多加一次判断
class Solution {
public int missingNumber(int[] nums) {
//写个二分的
int left = 0;
int right = nums.length;
while(left < right){
int mid = (right - left) / 2 + left;
if(mid == nums[mid]){
left = mid + 1;
}else
right = mid;
}
return left;
}
}
剑指 Offer 54. 二叉搜索树的第k大节点
题目描述
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/ \\
1 4
\\
2
输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \\
3 6
/ \\
2 4
/
1
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
中序遍历是递增的,直接记录第几个返回就行了
然后觉得很简单,写了一下,发现是第K大,需要从大到小找,就是把中序遍历返回来
刚开始我是直接让中序遍历返回值,将k传入函数中,然后每次k–,k为0,返回当前结点的值,第一个测试用例返回值一直是3;想了一下,发现k变量得写在外面,结果输出是0;最后才发现,需要用一个成员变量去记录结果,也可以另外加一个剪枝操作,当k等于0的时候,直接结束,不再递归。
简单题还有大问题!!!!唉
class Solution {
int k;
int res;
public int kthLargest(TreeNode root, int k) {
res = 0;
this.k = k;
inorder(root);
return res;
}
public void inorder(TreeNode root){
if(root == null || k == 0)
return;
inorder(root.right);
k--;
if(k == 0){
res = root.val;
return;
}
inorder(root.left);
}
}
以上是关于LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode每日一题——810. 黑板异或游戏(博弈问题)
LeetCode篇:810 黑板异或游戏(JavaScript版)
LeetCode篇:810 黑板异或游戏(JavaScript版)
LeetCode篇:810 黑板异或游戏(JavaScript版)