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版)

LeetCode篇:810 黑板异或游戏(JavaScript版)

LeetCode 810 黑板异或游戏[数学 异或] HERODING的LeetCode之路