Leetcode进阶之路——Weekly Contest 132

37 篇文章 0 订阅

1025. Divisor Game

Alice and Bob take turns playing a game, with Alice starting first.
Initially, there is a number N on the chalkboard. On each player’s turn, that player makes a move consisting of:
Choosing any x with 0 < x < N and N % x == 0.
Replacing the number N on the chalkboard with N - x.
Also, if a player cannot make a move, they lose the game.
Return True if and only if Alice wins the game, assuming both players play optimally.
Example 1:
Input: 2
Output: true
Explanation: Alice chooses 1, and Bob has no more moves.
Example 2:
Input: 3
Output: false
Explanation: Alice chooses 1, Bob chooses 1, and Alice has no more moves.

给定一个整数N,A先手,每次A和B可以从N的因子中移走一个数0<x<N,使N变为N-x,最后无法移动时为输
这道题刚开始做的时候举了几个例子:
N=1 lose
N=2 win
N=3 lose
N=4 win
···
于是有个直觉,直接判断奇偶即可,结果一试还真是… 真不愧是easy难度
然后比赛结束后想了想原因,解释如下:

  • 假设对于数字N,A必输,那么对于数字N+1,A必赢,因为A可以先手移1,使N+1变为N,这样B必输
  • 而对于任意奇数N,由于N的因子中必定不含有偶数,因此移动一次后,N将变为偶数
    基于上述两条规律,来看一下:
    当N=2,A必赢
    N=3,A移动一次后,N将变为偶数,而比N小的偶数只有2,因此B必赢
    N=4,由于N=3时A输,因此N=4,A赢
    N=5,同理A移动一次后,N将变为偶数,而比N小的偶数有2和4,这两者都是B必赢
    N=6,由于N=5时A输,因此N=6,A赢
    于是可以得到,N为偶数必赢,奇数必输,代码:
class Solution {
public:
    bool divisorGame(int N) {
        return (N % 2 == 0);
    }
};

1026. Maximum Difference Between Node and Ancestor

Given the root of a binary tree, find the maximum value V for which there exists different nodes A and B where V = |A.val - B.val| and A is an ancestor of B.
(A node A is an ancestor of B if either: any child of A is equal to B, or any child of A is an ancestor of B.)
Example 1:
Input: [8,3,10,1,6,null,14,null,null,4,7,13]
Output: 7
Explanation:
We have various ancestor-node differences, some of which are given below :
|8 - 3| = 5
|3 - 7| = 4
|8 - 1| = 7
|10 - 13| = 3
Among all possible differences, the maximum value of 7 is obtained by |8 - 1| = 7.

给定一棵二叉树,求出所有父节点与子节点之间最大的差值(绝对值)
遍历整棵树的同时,保留最大值和最小值即可,每次判断是否更新最大差值:

/**
 * 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 maxAncestorDiff(TreeNode* root) {
        if(root == NULL) return 0;
        int maxn = 0;
        // 初始化最大最小值均为根节点值
        helper(root, root->val, root->val, maxn);
        return maxn;
    }
    
    void helper(TreeNode* root, int maxn, int minn, int& res)
    {
        if(root == NULL) return;
        int v = root->val;
        res = max(res, max(abs(maxn-v), abs(minn-v)));
        helper(root->left, max(maxn, v), min(minn, v), res);
        helper(root->right, max(maxn, v), min(minn, v), res);
    }
};

1027. Longest Arithmetic Sequence

Given an array A of integers, return the length of the longest arithmetic subsequence in A.
Recall that a subsequence of A is a list A[i_1], A[i_2], …, A[i_k] with 0 <= i_1 < i_2 < … < i_k <= A.length - 1, and that a sequence B is arithmetic if B[i+1] - B[i] are all the same value (for 0 <= i < B.length - 1).
Example 1:
Input: [3,6,9,12]
Output: 4
Explanation:
The whole array is an arithmetic sequence with steps of length = 3.
Example 2:
Input: [9,4,7,2,10]
Output: 3
Explanation:
The longest arithmetic subsequence is [4,7,10].
Example 3:
Input: [20,1,15,3,10,5,8]
Output: 4
Explanation:
The longest arithmetic subsequence is [20,15,10,5].

给定一个数组A,求出最长的Arithmetic Sequence
定义为:对于A[i], A[j], A[k]…,有:A[j]-A[i] = A[k]-A[j]
直接动态规划,dp[i][diff]表示到i为止,差值为diff的子串长度(-1),O(n2)复杂度

class Solution {
public:
    int longestArithSeqLength(vector<int>& A) {
		if (A.size() < 2) return 0;
		int len = A.size();
		map<int, map<int, int>> mm;
		int maxn = 0;
		for(int i = 0; i < len; ++i)
        {
            for(int j = 0; j < i; ++j)
            {
                int diff = A[i] - A[j];
                mm[i][diff] = mm[j][diff] + 1;
                maxn = max(maxn, mm[i][diff]);
            }
        }
		return maxn + 1;
	}
};

1028. Recover a Tree From Preorder Traversal

We run a preorder depth first search on the root of a binary tree.
At each node in this traversal, we output D dashes (where D is the depth of this node), then we output the value of this node. (If the depth of a node is D, the depth of its immediate child is D+1. The depth of the root node is 0.)
If a node has only one child, that child is guaranteed to be the left child.
Given the output S of this traversal, recover the tree and return its root.
Example 1:
Input: “1-2–3--4-5–6--7”
Output: [1,2,5,3,4,6,7]
Example 2:
Input: “1-2–3—4-5–6—7”
Output: [1,2,5,3,null,6,null,4,null,7]

给定一个字符串,为某二叉树的前序遍历结果,重构该二叉树
字符串中的’-‘表示该数字对应的深度
"1-2–3--4-5–6--7"为例
根节点深度为0
2前面有一个’-’,因此2深度为1
6前面有两个’-’, 则6深度为2
由于刷题不久,刚开始想用哈希表存每个深度对应的数字,但结果总有差异,后来看了赛后的解析,get了一个新技能,可以用stack来解,先上自己的代码:

/**
 * 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:
    TreeNode* recoverFromPreorder(string S) {
		stack<TreeNode*> st;
		for (int i = 0; i < S.length();)
		{
			int depth = 0, num = 0;
			// 求深度
			while (S[i] == '-') depth++, i++;
			// 求数字
			while (i < S.length() && S[i] != '-')
			{
				num = num * 10 + S[i] - 48;
				i++;
			}
			TreeNode* cur = new TreeNode(num);
			while (st.size() > depth) st.pop();
			if (!st.empty()) 
			{
				// left child is occupied
				if (st.top()->left)
				{
					st.top()->right = cur;
				}
				else st.top()->left = cur;
			}
			st.emplace(cur);
		}
		while (st.size() > 1) st.pop();
		return st.top();
	}
};

前面一部分就不说了,分别求数字及其对应的深度,并创建当前树节点
由于字符串是前序遍历的结果,因此可以保证一定是左子树优先于右子树,因此每次push(代码中用的emplace函数,同样的效果,更高效)之前,保证当前的节点被push到指定的size(while (st.size() > depth) st.pop();)
以"1-401–349—90–88"为例:
先创建根节点1
然后401节点,深度为1,小于等于当前stack的大小=1,又由于根节点左子树为空,则令其左子树为401
接下来349、90也是同理,一直push到左子树
当来到88时,此时stack大小为4,但88的深度为2,因此需要把349和90都pop出去(后进先出),此时stack中只剩下根节点1和其左子树401,发现左子树被占,自然而然88成为根节点的右子树
遍历结束,由于要返回根节点,此时stack中保存了根节点和其左右子树,因此要把左右子树都pop出去,最后返回根节点即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值