目录

810. 黑板异或游戏

817. 链表组件

853. 车队

869. 重新排序得到 2 的幂

870. 优势洗牌

873. 最长的斐波那契子序列的长度

875. 爱吃香蕉的珂珂

877. 石子游戏

887. 鸡蛋掉落

889. 根据前序和后序遍历构造二叉树

907. 子数组的最小值之和

932. 漂亮数组

934. 最短的桥

946. 验证栈序列

948. 令牌放置

971. 翻转二叉树以匹配先序遍历

979. 在二叉树中分配硬币

990. 等式方程的可满足性


 

810. 黑板异或游戏

博弈 javascript:void(0)

817. 链表组件

给定一个链表(链表结点包含一个整型值)的头结点 head。

同时给定列表 G,该列表是上述链表中整型值的一个子集。

返回列表 G 中组件的个数,这里对组件的定义为:链表中一段最长连续结点的值(该值必须在列表 G 中)构成的集合。

示例 1:

输入: 
head: 0->1->2->3
G = [0, 1, 3]
输出: 2
解释: 
链表中,0 和 1 是相连接的,且 G 中不包含 2,所以 [0, 1] 是 G 的一个组件,同理 [3] 也是一个组件,故返回 2。
示例 2:

输入: 
head: 0->1->2->3->4
G = [0, 3, 1, 4]
输出: 2
解释: 
链表中,0 和 1 是相连接的,3 和 4 是相连接的,所以 [0, 1] 和 [3, 4] 是两个组件,故返回 2。
注意:

如果 N 是给定链表 head 的长度,1 <= N <= 10000。
链表中每个结点的值所在范围为 [0, N - 1]。
1 <= G.length <= 10000
G 是链表中所有结点的值的一个子集.

 

代码:

class Solution {
public:
	bool has(vector<int>& G, int k)
	{
		return (lower_bound(G.begin(), G.end(), k) != upper_bound(G.begin(), G.end(), k));
	}
	int numComponents(ListNode* head, vector<int>& G) {
		int ans = 0, num = 0;
		ListNode* p = head;
		sort(G.begin(), G.end());
		while (p)
		{
			if(has(G, p->val))num++;
			else if (num > 0)ans++, num = 0;
			p = p->next;
		}
		if (num > 0)ans++;
		return ans;
	}
};

 

853. 车队

 

题目:

N  辆车沿着一条车道驶向位于 target 英里之外的共同目的地。

每辆车 i 以恒定的速度 speed[i] (英里/小时),从初始位置 position[i] (英里) 沿车道驶向目的地。

一辆车永远不会超过前面的另一辆车,但它可以追上去,并与前车以相同的速度紧接着行驶。

此时,我们会忽略这两辆车之间的距离,也就是说,它们被假定处于相同的位置。

车队 是一些由行驶在相同位置、具有相同速度的车组成的非空集合。注意,一辆车也可以是一个车队。

即便一辆车在目的地才赶上了一个车队,它们仍然会被视作是同一个车队。

 

会有多少车队到达目的地?

 

示例:

输入:target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]
输出:3
解释:
从 10 和 8 开始的车会组成一个车队,它们在 12 处相遇。
从 0 处开始的车无法追上其它车,所以它自己就是一个车队。
从 5 和 3 开始的车会组成一个车队,它们在 6 处相遇。
请注意,在到达目的地之前没有其它车会遇到这些车队,所以答案是 3。

提示:

0 <= N <= 10 ^ 4
0 < target <= 10 ^ 6
0 < speed[i] <= 10 ^ 6
0 <= position[i] < target
所有车的初始位置各不相同。

 

代码:

bool cmp(pair<int, int>a, pair<int, int>b)
{
	return a.first > b.first;
}
 
class Solution {
public:
	int carFleet(int target, vector<int>& position, vector<int>& speed) {
		if (position.empty())return 0;
		vector<pair<int, int>>pos;
		for (int i = 0; i < position.size(); i++)
		{
			pos.insert(pos.end(), make_pair(position[i], speed[i]));
		}
		sort(pos.begin(), pos.end(), cmp);
		int ans = pos.size(), k = 0;
		for (int i = 1; i < pos.size(); i++)
		{
			long long a = target - pos[k].first, b = target - pos[i].first;
			if (a*pos[i].second >= b*pos[k].second)ans--;
			else k = i;
		}
		return ans;
	}
};

 

 

869. 重新排序得到 2 的幂

 

题目:

给定正整数 N ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。

如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false。

 

示例 1:

输入:1
输出:true
示例 2:

输入:10
输出:false
示例 3:

输入:16
输出:true
示例 4:

输入:24
输出:false
示例 5:

输入:46
输出:true
 

提示:

1 <= N <= 10^9

 

 

思路:

2的幂非常少,一个个匹配就行了。

列出所有的2的幂,依次判断是不是能对应上即可。

代码:

class Solution {
public:    
    bool isSame(int a,int b) {
        int num[10]={0};
        while(a>0){
            num[a%10]++;
            a/=10;
        }
        while(b>0){
            num[b%10]--;
            b/=10;
        }
        for(int i=0;i<10;i++){
            if(num[i]!=0){
                return false;
            }
        }
        return true;
    }
    bool reorderedPowerOf2(int N) {
        for(int i=0;i<30;i++){
            if(isSame(N,(1<<i))){
                return true; 
            }
        }
        return false;
    }
};

 

870. 优势洗牌

贪心 javascript:void(0)

873. 最长的斐波那契子序列的长度

题目:

如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:

n >= 3
对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+2}
给定一个严格递增的正整数数组形成序列,找到 A 中最长的斐波那契式的子序列的长度。如果一个不存在,返回  0 。

(回想一下,子序列是从原序列 A 中派生出来的,它从 A 中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8] 是 [3, 4, 5, 6, 7, 8] 的一个子序列)

 

示例 1:

输入: [1,2,3,4,5,6,7,8]
输出: 5
解释:
最长的斐波那契式子序列为:[1,2,3,5,8] 。
示例 2:

输入: [1,3,7,11,12,14,18]
输出: 3
解释:
最长的斐波那契式子序列有:
[1,11,12],[3,11,14] 以及 [7,11,18] 。
 

提示:

3 <= A.length <= 1000
1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9
(对于以 Java,C,C++,以及 C# 的提交,时间限制被减少了 50%)

 

思路:

动态规划。DP[x][y]表示以A[x]和A[y]开头的最长斐波那契子数列的长度。

代码一:

备忘录写法

#define MAX 1000
int ans[MAX][MAX];
 
int dp(int x,int y,vector<int>& A)
{
    if(ans[x][y]){
        return ans[x][y];
    }
    auto it= lower_bound(A.begin(),A.end(),A[x]+A[y]);
    ans[x][y]=((it!=A.end() && *it==A[x]+A[y])?(dp(y,it-A.begin(),A)+1):2);
    return ans[x][y];
}
 
class Solution {
public:
    int lenLongestFibSubseq(vector<int>& A) {
        int res=0;
        for(int i=0;i<A.size();i++){
            memset(ans[i],0,sizeof(ans[0][0])*A.size());
        }
        for(int i=0;i<A.size();i++){
            for(int j=i+1;j<A.size();j++){
                if(res<dp(i,j,A)){
                    res=dp(i,j,A);
                }
            }
        }
        if(res<3)res=0;
        return res;
    }
};

 

结果:AC 820ms

代码二:

非备忘录方法,控制计算顺序,省略初始化步骤

#define MAX 1000
int ans[MAX][MAX];
 
class Solution {
public:
    int lenLongestFibSubseq(vector<int>& A) {
        int res=0;
        for(int j=A.size()-1;j>=0;j--){
            for(int i=j-1;i>=0;i--){
                auto it= lower_bound(A.begin(),A.end(),A[i]+A[j]);
                ans[i][j]=((it!=A.end() && *it==A[i]+A[j])?(ans[j][it-A.begin()]+1):2);
                res=max(res,ans[i][j]);
            }
        }
        if(res<3)res=0;
        return res;
    }
};

 

结果:AC 436ms

改进思路:

上述算法时间复杂度O(n* n * lg n)

利用双指针代替lower_bound完成查找工作,算法时间复杂度可以降为O(n* n)

 

875. 爱吃香蕉的珂珂

 

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。  

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

 

示例 1:

输入: piles = [3,6,7,11], H = 8
输出: 4
示例 2:

输入: piles = [30,11,23,4,20], H = 5
输出: 30
示例 3:

输入: piles = [30,11,23,4,20], H = 6
输出: 23
 

提示:

1 <= piles.length <= 10^4
piles.length <= H <= 10^9
1 <= piles[i] <= 10^9

bool ok(vector<int>&b, int m,int ans)
{
    int s=0;
    for(int i=0;i<b.size();i++)
    {
        m-=(b[i]%ans>0)+b[i]/ans;
    }
    return m>=0;
}
 
class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int H) {
        int low=0,high=1000000000;
        while (low < high-1)
	    {
		    int mid = (low + high) / 2;
            if(ok(piles,H,mid))high = mid;
		    else low = mid;
	    }
        return high;
    }
};

 

877. 石子游戏

区间DP javascript:void(0)

887. 鸡蛋掉落

高维DP javascript:void(0)

889. 根据前序和后序遍历构造二叉树

二叉树 javascript:void(0)

907. 子数组的最小值之和

单调栈 javascript:void(0)

932. 漂亮数组

分治 javascript:void(0)

934. 最短的桥

DFS、BFS javascript:void(0)

946. 验证栈序列

剑指 Offer 31. 栈的压入、弹出序列 javascript:void(0)

948. 令牌放置

贪心 javascript:void(0)

971. 翻转二叉树以匹配先序遍历

 

给定一个有 N 个节点的二叉树,每个节点都有一个不同于其他节点且处于 {1, ..., N} 中的值。

通过交换节点的左子节点和右子节点,可以翻转该二叉树中的节点。

考虑从根节点开始的先序遍历报告的 N 值序列。将这一 N 值序列称为树的行程。

(回想一下,节点的先序遍历意味着我们报告当前节点的值,然后先序遍历左子节点,再先序遍历右子节点。)

我们的目标是翻转最少的树中节点,以便树的行程与给定的行程 voyage 相匹配。 

如果可以,则返回翻转的所有节点的值的列表。你可以按任何顺序返回答案。

如果不能,则返回列表 [-1]。

 

示例 1:

输入:root = [1,2], voyage = [2,1]
输出:[-1]
示例 2:

输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
示例 3:

输入:root = [1,2,3], voyage = [1,2,3]
输出:[]
 

提示:

1 <= N <= 100

 

第一次AC的代码:

class Solution {
public:
    map<int,int>m;
    vector<int>ans;
    int fin(vector<int>& voyage,int low,int high,int k)
    {
        if(m[k]>=low && m[k]<high)return m[k];
        return high;
    }
    bool f(TreeNode* root, vector<int>& voyage,int low,int high){
        if(!root)return low==high;
        if(high<=low || voyage[low]!=root->val)return false;
        if(!root->left && !root->right)return high==low+1;
        if(high<=low+1)return false;
        if(root->left && root->left->val==voyage[low+1])
        {
            if(root->right)return f(root->left,voyage,low+1,fin(voyage,low,high,root->right->val)) && f(root->right,voyage,fin(voyage,low,high,root->right->val),high);
            return f(root->left,voyage,low+1,high);
        }
        if(root->right && root->right->val==voyage[low+1])
        {
            if(root->left)
            {
                ans.push_back(root->val);
                return f(root->left,voyage,fin(voyage,low,high,root->left->val),high) && f(root->right,voyage,low+1,fin(voyage,low,high,root->left->val));
            }
            return f(root->right,voyage,low+1,high);
        }
        return false;
    }
    vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
        m.clear();
        ans.clear();
        for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
        if(f(root,voyage,0,voyage.size()))return ans;
        ans.clear();
        ans.push_back(-1);
        return ans;
    }
};

然后感觉写的太繁琐,fin函数是不需要的,优化代码:

class Solution {
public:
    map<int,int>m;
    vector<int>ans;
    bool f(TreeNode* root, vector<int>& voyage,int low,int high){
        if(!root)return low==high;
        if(high<=low || voyage[low]!=root->val)return false;
        if(high==low+1)return !root->left && !root->right;
        if(root->left && root->left->val==voyage[low+1])
        {
            if(root->right)return f(root->left,voyage,low+1,m[root->right->val]) && f(root->right,voyage,m[root->right->val],high);
            return f(root->left,voyage,low+1,high);
        }
        if(root->right && root->right->val==voyage[low+1])
        {
            if(root->left)
            {
                ans.push_back(root->val);
                return f(root->left,voyage,m[root->left->val],high) && f(root->right,voyage,low+1,m[root->left->val]);
            }
            return f(root->right,voyage,low+1,high);
        }
        return false;
    }
    vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
        m.clear();
        ans.clear();
        for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
        if(f(root,voyage,0,voyage.size()))return ans;
        ans.clear();
        ans.push_back(-1);
        return ans;
    }
};

因为这题没有要求说不能改变二叉树的结构,所以我还试了一下改变二叉树结构的写法,使得代码更简洁:

class Solution {
public:
    map<int,int>m;
    vector<int>ans;
    bool f(TreeNode* root, vector<int>& voyage,int low,int high){
        if(!root)return low==high;
        if(high<=low || voyage[low]!=root->val)return false;
        if(high==low+1)return !root->left && !root->right;
        if(root->right && root->right->val==voyage[low+1])
        {
            if(root->left)ans.push_back(root->val);
            TreeNode* p=root->left;
            root->left=root->right,root->right=p;
        }
        if(!root->left || root->left->val!=voyage[low+1])return false;
        if(!root->right)return f(root->left,voyage,low+1,high);
        return f(root->left,voyage,low+1,m[root->right->val]) && f(root->right,voyage,m[root->right->val],high);
    }
    vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
        ans.clear();
        for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
        if(f(root,voyage,0,voyage.size()))return ans;
        ans.clear();
        ans.push_back(-1);
        return ans;
    }
};

 

979. 在二叉树中分配硬币

给定一个有 N 个结点的二叉树的根结点 root,树中的每个结点上都对应有 node.val 枚硬币,并且总共有 N 枚硬币。

在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。(移动可以是从父结点到子结点,或者从子结点移动到父结点。)。

返回使每个结点上只有一枚硬币所需的移动次数。

 

示例 1:

输入:[3,0,0]
输出:2
解释:从树的根结点开始,我们将一枚硬币移到它的左子结点上,一枚硬币移到它的右子结点上。
示例 2:

输入:[0,3,0]
输出:3
解释:从根结点的左子结点开始,我们将两枚硬币移到根结点上 [移动两次]。然后,我们把一枚硬币从根结点移到右子结点上。
示例 3:

输入:[1,0,2]
输出:2
示例 4:

输入:[1,0,0,null,3]
输出:4
 

提示:

1<= N <= 100
0 <= node.val <= N

class Solution {
public:
    int distributeCoins(TreeNode* root,long &dif) {
        if(!root)
        {
            dif=0;
            return 0;
        }
        long dif1,dif2;
        int ans1=distributeCoins(root->left,dif1),ans2=distributeCoins(root->right,dif2);
        dif=dif1+dif2+root->val-1;
        return ans1+ans2+abs(dif);
    }
    int distributeCoins(TreeNode* root) {
        long dif=0;
        return distributeCoins(root,dif);
    }
};
988. 从叶结点开始的最小字符串

给定一颗根结点为 root 的二叉树,树中的每一个结点都有一个从 0 到 25 的值,分别代表字母 'a' 到 'z':值 0 代表 'a',值 1 代表 'b',依此类推。

找出按字典序最小的字符串,该字符串从这棵树的一个叶结点开始,到根结点结束。

(小贴士:字符串中任何较短的前缀在字典序上都是较小的:例如,在字典序上 "ab" 比 "aba" 要小。叶结点是指没有子结点的结点。)

 

示例 1:

输入:[0,1,2,3,4,3,4]
输出:"dba"
示例 2:

输入:[25,1,3,1,3,0,2]
输出:"adz"
示例 3:

输入:[2,2,1,null,1,0,null,0]
输出:"abc"
 

提示:

给定树的结点数介于 1 和 8500 之间。
树中的每个结点都有一个介于 0 和 25 之间的值。

class Solution {
public:
	void smallestFromLeaf(TreeNode* root, string cur, string &ans)
	{
		if (!root)return;
		cur.insert(0, 1, char(int('a') + root->val));
		if (!root->left && !root->right)
		{
			ans = min(ans, cur);
			return;
		}
		smallestFromLeaf(root->left, cur, ans);
		smallestFromLeaf(root->right, cur, ans);
	}
	string smallestFromLeaf(TreeNode* root) {
		string ans = " ";
		ans[0] = char(int('z') + 1);
		smallestFromLeaf(root, "", ans);
		return ans;
	}
};
990. 等式方程的可满足性

并查集 javascript:void(0)