力扣OJ(0801-1000)

目录

802. 找到最终的安全状态

805. 数组的均值分割

808. 分汤

809. 情感丰富的文字

810. 黑板异或游戏

813. 最大平均值和的分组

814. 二叉树剪枝

817. 链表组件

820. 单词的压缩编码

822. 翻转卡片游戏

823. 带因子的二叉树

827. 最大人工岛

829. 连续整数求和

837. 新 21 点

840. 矩阵中的幻方

841. 钥匙和房间

843. 猜猜这个单词

849. 到最近的人的最大距离

852. 山脉数组的峰顶索引

853. 车队

858. 镜面反射

860. 柠檬水找零

865. 具有所有最深节点的最小子树

866. 回文素数

869. 重新排序得到 2 的幂

870. 优势洗牌

872. 叶子相似的树

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

875. 爱吃香蕉的珂珂

877. 石子游戏

878. 第 N 个神奇数字

887. 鸡蛋掉落

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

892. 三维形体的表面积

894. 所有可能的真二叉树

897. 递增顺序搜索树

901. 股票价格跨度

907. 子数组的最小值之和

913. 猫和老鼠

914. 卡牌分组

919. 完全二叉树插入器

927. 三等分

931. 下降路径最小和

932. 漂亮数组

933. 最近的请求次数

934. 最短的桥

943. 最短超级串

946. 验证栈序列

947. 移除最多的同行或同列石头

948. 令牌放置

950. 按递增顺序显示卡牌

952. 按公因数计算最大组件大小

953. 验证外星语词典

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

979. 在二叉树中分配硬币

980. 不同路径 III

984. 不含 AAA 或 BBB 的字符串

987. 二叉树的垂序遍历

988. 从叶结点开始的最小字符串

990. 等式方程的可满足性

993. 二叉树的堂兄弟节点

995. K 连续位的最小翻转次数

996. 正方形数组的数目


802. 找到最终的安全状态

图的连通分量

805. 数组的均值分割

搜索else

808. 分汤

概率DP

809. 情感丰富的文字

双指针

810. 黑板异或游戏

 博弈

813. 最大平均值和的分组

二维DP

814. 二叉树剪枝

二叉树DFS

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;
	}
};

820. 单词的压缩编码

单词数组 words 的 有效编码 由任意助记字符串 s 和下标数组 indices 组成,且满足:

  • words.length == indices.length
  • 助记字符串 s 以 '#' 字符结尾
  • 对于每个下标 indices[i] ,s 的一个从 indices[i] 开始、到下一个 '#' 字符结束(但不包括 '#')的 子字符串 恰好与 words[i] 相等

给你一个单词数组 words ,返回成功对 words 进行编码的最小助记字符串 s 的长度 。

示例 1:

输入:words = ["time", "me", "bell"]
输出:10
解释:一组有效编码为 s = "time#bell#" 和 indices = [0, 2, 5] 。
words[0] = "time" ,s 开始于 indices[0] = 0 到下一个 '#' 结束的子字符串,如加粗部分所示 "time#bell#"
words[1] = "me" ,s 开始于 indices[1] = 2 到下一个 '#' 结束的子字符串,如加粗部分所示 "time#bell#"
words[2] = "bell" ,s 开始于 indices[2] = 5 到下一个 '#' 结束的子字符串,如加粗部分所示 "time#bell#"

示例 2:

输入:words = ["t"]
输出:2
解释:一组有效编码为 s = "t#" 和 indices = [0] 。

提示:

  • 1 <= words.length <= 2000
  • 1 <= words[i].length <= 7
  • words[i] 仅由小写字母组成
bool cmp(string s1,string s2){
    return s1.length()>s2.length();
}
class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        sort(words.begin(),words.end(),cmp);
        map<string,int>m;
        int ans = 0;
        for(auto s:words){
            if(m[s])continue;
            ans+=s.length()+1;
            for(int i=0;i<s.length();i++)m[s.substr(i,s.length()-i)]++;
        }
        return ans;
    }
};

822. 翻转卡片游戏

在桌子上有 n 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。

我们可以先翻转任意张卡片,然后选择其中一张卡片。

如果选中的那张卡片背面的数字 x 与任意一张卡片的正面的数字都不同,那么这个数字是我们想要的数字。

哪个数是这些想要的数字中最小的数(找到这些数中的最小值)呢?如果没有一个数字符合要求的,输出 0 。

其中, fronts[i] 和 backs[i] 分别代表第 i 张卡片的正面和背面的数字。

如果我们通过翻转卡片来交换正面与背面上的数,那么当初在正面的数就变成背面的数,背面的数就变成正面的数。

示例 1:

输入:fronts = [1,2,4,4,7], backs = [1,3,4,1,3]
输出:2
解释:假设我们翻转第二张卡片,那么在正面的数变成了 [1,3,4,4,7] , 背面的数变成了 [1,2,4,1,3]。
接着我们选择第二张卡片,因为现在该卡片的背面的数是 2,2 与任意卡片上正面的数都不同,所以 2 就是我们想要的数字。

示例 2:

输入:fronts = [1], backs = [1]
输出:0
解释:
无论如何翻转都无法得到想要的数字,所以返回 0 。

提示:

  • n == fronts.length == backs.length
  • 1 <= n <= 1000
  • 1 <= fronts[i], backs[i] <= 2000
class Solution {
public:
    int flipgame(vector<int>& fronts, vector<int>& backs) {
        map<int,int>m;
        vector<int>v;
        for(int i=0;i<fronts.size();i++){
            if(fronts[i]==backs[i])m[backs[i]]++;
            v.push_back(fronts[i]);
            v.push_back(backs[i]);
        }
        sort(v.begin(),v.end());
        for(auto vi:v)if(m[vi]==0)return vi;
        return 0;
    }
};

823. 带因子的二叉树

DP

826. 安排工作以达到最大收益

贪心

827. 最大人工岛

并查集

829. 连续整数求和

因式分解

837. 新 21 点

概率DP

840. 矩阵中的幻方

幻方

841. 钥匙和房间

BFS

843. 猜猜这个单词

贪心

849. 到最近的人的最大距离

给你一个数组 seats 表示一排座位,其中 seats[i] = 1 代表有人坐在第 i 个座位上,seats[i] = 0 代表座位 i 上是空的(下标从 0 开始)。

至少有一个空座位,且至少有一人已经坐在座位上。

亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座位上。

返回他到离他最近的人的最大距离。

示例 1:

输入:seats = [1,0,0,0,1,0,1]
输出:2
解释:
如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。
如果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。
因此,他到离他最近的人的最大距离是 2 。 

示例 2:

输入:seats = [1,0,0,0]
输出:3
解释:
如果亚历克斯坐在最后一个座位上,他离最近的人有 3 个座位远。
这是可能的最大距离,所以答案是 3 。

示例 3:

输入:seats = [0,1]
输出:1

提示:

  • 2 <= seats.length <= 2 * 104
  • seats[i] 为 0 或 1
  • 至少有一个 空座位
  • 至少有一个 座位上有人
class Solution {
public:
	int maxDistToClosest(vector<int>& seats) {
		int a = -1, ans = 0, last = 0;
		for (int i = 0; i < seats.size(); i++) {
			if (seats[i]) {
				if (a == -1)ans = i;
				else ans = max(ans, (i - a)/2);
				a = i;
				last = i;
			}
		}
		return max(ans, int(seats.size() - 1 - last));
	}
};

852. 山脉数组的峰顶索引

三分法

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;
	}
};

857. 雇佣 K 名工人的最低成本

优先队列

858. 镜面反射

欧几里得

860. 柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例 1:

输入:bills = [5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。

示例 2:

输入:bills = [5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。

提示:

  • 1 <= bills.length <= 105
  • bills[i] 不是 5 就是 10 或是 20 
class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int x5=0,x10=0,x20=0;
        for(auto b:bills){
            if(b==5)x5++;
            else if(b==10){
                if(x5<1)return false;
                x10++,x5--;
            }else{
                if(x10==0){
                    if(x5<3)return false;
                    x5-=3;
                }else{
                    if(x5<1)return false;
                    x10--,x5--;
                }
            }
        }
        return true;
    }
};

865. 具有所有最深节点的最小子树

1123. 最深叶节点的最近公共祖先

866. 回文素数

素数筛

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. 优势洗牌

贪心 贪心(1)田忌赛马_csuzhucong的博客-CSDN博客_力扣 田忌赛马

872. 叶子相似的树

二叉树

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. 爱吃香蕉的珂珂

rust

877. 石子游戏

 公开游戏

878. 第 N 个神奇数字

欧几里得算法

881. 救生艇

双指针

887. 鸡蛋掉落

高维DP 高维DP_csuzhucong的博客-CSDN博客

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

二叉树 二叉树_csuzhucong的博客-CSDN博客_二叉树csdn

892. 三维形体的表面积

空间几何

894. 所有可能的真二叉树

树形DP

897. 递增顺序搜索树

二叉搜索树

901. 股票价格跨度

单调栈

907. 子数组的最小值之和

单调栈 单调栈_csuzhucong的博客-CSDN博客

908. 最小差值 I

给你一个整数数组 nums,和一个整数 k 。

在一个操作中,您可以选择 0 <= i < nums.length 的任何索引 i 。将 nums[i] 改为 nums[i] + x ,其中 x 是一个范围为 [-k, k] 的整数。对于每个索引 i ,最多 只能 应用 一次 此操作。

nums 的 分数 是 nums 中最大和最小元素的差值。 

在对  nums 中的每个索引最多应用一次上述操作后,返回 nums 的最低 分数 。

示例 1:

输入:nums = [1], k = 0
输出:0
解释:分数是 max(nums) - min(nums) = 1 - 1 = 0。

示例 2:

输入:nums = [0,10], k = 2
输出:6
解释:将 nums 改为 [2,8]。分数是 max(nums) - min(nums) = 8 - 2 = 6。

示例 3:

输入:nums = [1,3,6], k = 3
输出:0
解释:将 nums 改为 [4,4,4]。分数是 max(nums) - min(nums) = 4 - 4 = 0。

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 104
  • 0 <= k <= 104
class Solution {
public:
	int smallestRangeI(vector<int>& nums, int k) {
		int mins = nums[0], maxs = nums[0];
		for (auto x : nums)mins = min(mins, x), maxs = max(maxs, x);
		return max(maxs - mins - k * 2, 0);
	}
};

910. 最小差值 II

给你一个整数数组 nums,和一个整数 k 。

对于每个下标 i0 <= i < nums.length),将 nums[i] 变成 nums[i] + k 或 nums[i] - k 。

nums 的 分数 是 nums 中最大元素和最小元素的差值。

在更改每个下标对应的值之后,返回 nums 的最小 分数 。

示例 1:

输入:nums = [1], k = 0
输出:0
解释:分数 = max(nums) - min(nums) = 1 - 1 = 0 。

示例 2:

输入:nums = [0,10], k = 2
输出:6
解释:将数组变为 [2, 8] 。分数 = max(nums) - min(nums) = 8 - 2 = 6 。

示例 3:

输入:nums = [1,3,6], k = 3
输出:3
解释:将数组变为 [4, 6, 3] 。分数 = max(nums) - min(nums) = 6 - 3 = 3 。

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 104
  • 0 <= k <= 104
class Solution {
public:
	int smallestRangeII(vector<int>& nums, int k) {
		sort(nums.begin(), nums.end());
		int ans = nums[nums.size() - 1] - nums[0];
		for (int i = 1; i < nums.size(); i++) {
			int mins = min(nums[0] + k, nums[i] - k);
			int maxs = max(nums[i - 1] + k, nums[nums.size() - 1] - k);
			ans = min(ans, maxs-mins);
		}
		return ans;
	}
};

913. 猫和老鼠

有向有环图游戏

914. 卡牌分组

欧几里得算法

919. 完全二叉树插入器

完全二叉树

920. 播放列表的数量

容斥原理

924. 尽量减少恶意软件的传播

并查集

927. 三等分

给定一个由 0 和 1 组成的数组 arr ,将数组分成  3 个非空的部分 ,使得所有这些部分表示相同的二进制值。

如果可以做到,请返回任何 [i, j],其中 i+1 < j,这样一来:

  • arr[0], arr[1], ..., arr[i] 为第一部分;
  • arr[i + 1], arr[i + 2], ..., arr[j - 1] 为第二部分;
  • arr[j], arr[j + 1], ..., arr[arr.length - 1] 为第三部分。
  • 这三个部分所表示的二进制值相等。

如果无法做到,就返回 [-1, -1]

注意,在考虑每个部分所表示的二进制时,应当将其看作一个整体。例如,[1,1,0] 表示十进制中的 6,而不会是 3。此外,前导零也是被允许的,所以 [0,1,1] 和 [1,1] 表示相同的值。

示例 1:

输入:arr = [1,0,1,0,1]
输出:[0,3]

示例 2:

输入:arr = [1,1,0,1,1]
输出:[-1,-1]

示例 3:

输入:arr = [1,1,0,0,1]
输出:[0,2]

提示:

  • 3 <= arr.length <= 3 * 104
  • arr[i] 是 0 或 1
class Solution {
public:
    vector<int> threeEqualParts(vector<int>& arr) {
        int num1=0,num2=0,high;
        for(auto x:arr)if(x==1)num1++;
        if(num1==0)return vector<int>{0,2};
        if(num1%3)return vector<int>{-1,-1};
        num1/=3;
        int ai,aj;
        for(int i=0;i<arr.size();i++){
            if(arr[i]==1){
                ++num2;
                if(num2==num1)ai=i;
                if(num2==num1*2)aj=i+1;
                high=i;
            }
        }
        high=arr.size()-1-high;
        ai+=high,aj+=high;
        vector<int>ans{ai,aj};
        for(int i1=ai,i2=aj-1,i3=arr.size()-1;i1>=0&&i2>ai&&i3>=aj;i1--,i2--,i3--){
            if(arr[i1]!=arr[i2] || arr[i2]!=arr[i3])return vector<int>{-1,-1};
            if(arr[i1]==1){
                if(--num1 ==0)return ans;
            }
        }
        return vector<int>{-1,-1};
    }
};

928. 尽量减少恶意软件的传播 II

并查集

931. 下降路径最小和

二维DP

932. 漂亮数组

分治 分治_csuzhucong的博客-CSDN博客

933. 最近的请求次数

写一个 RecentCounter 类来计算特定时间范围内最近的请求。

请你实现 RecentCounter 类:

  • RecentCounter() 初始化计数器,请求数为 0 。
  • int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。

保证 每次对 ping 的调用都使用比之前更大的 t 值。

示例 1:

输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]

解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1);     // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100);   // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001);  // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002);  // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3

提示:

  • 1 <= t <= 109
  • 保证每次对 ping 调用所使用的 t 值都 严格递增
  • 至多调用 ping 方法 104 次
class RecentCounter {
public:
    int ping(int t) {
        v.push_back(t);
        while(v[0]<v.back()-3000)v.erase(v.begin());
        return v.size();
    }
    vector<int>v;
};

934. 最短的桥

BFS

938. 二叉搜索树的范围和

二叉搜索树

943. 最短超级串

最短哈密顿链路

946. 验证栈序列

 剑指 Offer 31. 栈的压入、弹出序列

947. 移除最多的同行或同列石头

并查集

948. 令牌放置

 贪心(4)选取问题

950. 按递增顺序显示卡牌

rust

952. 按公因数计算最大组件大小

因式分解

953. 验证外星语词典

某种外星语也使用英文小写字母,但可能顺序 order 不同。字母表的顺序(order)是一些小写字母的排列。

给定一组用外星语书写的单词 words,以及其字母表的顺序 order,只有当给定的单词在这种外星语中按字典序排列时,返回 true;否则,返回 false

示例 1:

输入:words = ["hello","leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz"
输出:true
解释:在该语言的字母表中,'h' 位于 'l' 之前,所以单词序列是按字典序排列的。

示例 2:

输入:words = ["word","world","row"], order = "worldabcefghijkmnpqstuvxyz"
输出:false
解释:在该语言的字母表中,'d' 位于 'l' 之后,那么 words[0] > words[1],因此单词序列不是按字典序排列的。

示例 3:

输入:words = ["apple","app"], order = "abcdefghijklmnopqrstuvwxyz"
输出:false
解释:当前三个字符 "app" 匹配时,第二个字符串相对短一些,然后根据词典编纂规则 "apple" > "app",因为 'l' > '∅',其中 '∅' 是空白字符,定义为比任何其他字符都小(更多信息)。

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 20
  • order.length == 26
  • 在 words[i] 和 order 中的所有字符都是英文小写字母。
class Solution {
public:
    bool isAlienSorted(vector<string>& words, string order) {
        map<char,int>m;
        int id=0;
        for(auto c:order)m[c]=++id;
        for(int i=1;i<words.size();i++){
            if(!isAlienSorted(words[i-1],words[i],m))return false;
        }
        return true;
    }
    bool isAlienSorted(string &s1,string &s2,map<char,int>&m){
        for(int i=0;i<s1.length()&&i<s2.length();i++){
            if(m[s1[i]]<m[s2[i]])return true;
            if(m[s1[i]]>m[s2[i]])return false;
        }
        return s1.length()<=s2.length();
    }
};

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);
    }
};


980. 不同路径 III

哈密顿

984. 不含 AAA 或 BBB 的字符串

rust

987. 二叉树的垂序遍历

二叉树DFS

988. 从叶结点开始的最小字符串

贪心 字典序

990. 等式方程的可满足性

并查集 并查集_csuzhucong的博客-CSDN博客

993. 二叉树的堂兄弟节点

二叉树DFS

994. 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

  • 值 0 代表空单元格;
  • 值 1 代表新鲜橘子;
  • 值 2 代表腐烂的橘子。

每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

示例 1:

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4

示例 2:

输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。

示例 3:

输入:grid = [[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10
  • grid[i][j] 仅为 01 或 2

class GridGraph
{
public:
	GridGraph(int row, int col)
	{
		this->row = row;
		this->col = col;
		initD4D8();
	}
	int gridId(int r, int c) //阅读顺序的id,先给col赋值再调用
	{
		return r * col + c;
	}
	vector<int> getNeighbor4(int k)//获得四邻居的id
	{
		vector<int>ans;
		for (int i = 0; i < 4; i++) {
			if (inBoard(k / col + dx4[i], k % col + dy4[i]))ans.push_back(k + d4[i]);
		}
		return ans;
	}
	vector<int> getNeighbor8(int k)//获得八邻居的id
	{
		vector<int>ans;
		for (int i = 0; i < 8; i++) {
			if (inBoard(k / col + dx8[i], k % col + dy8[i]))ans.push_back(k + d8[i]);
		}
		return ans;
	}
private:
	int row;
	int col;
	//二维坐标系的邻居偏移量
	const vector<int> dx4{ 0,0,1,-1 };
	const vector<int> dy4{ 1,-1,0,0 };
	const vector<int> dx8{ 0,0,1,-1,1,1,-1,-1 };
	const vector<int> dy8{ 1,-1,0,0 ,1,-1,1,-1 };
	//一维id坐标系的邻居偏移量
	vector<int> d4;
	vector<int> d8;
private:
	inline void initD4D8()
	{
		for (int i = 0; i < 4; i++)d4.push_back(gridId(dx4[i], dy4[i]));
		for (int i = 0; i < 8; i++)d8.push_back(gridId(dx8[i], dy8[i]));
	}
	inline bool inBoard(int r, int c)
	{
		return r >= 0 && r < row&& c >= 0 && c < col;
	}
	inline bool inBoard(int id)
	{
		return id >= 0 && inBoard(id / col, id % col);
	}
};
class Solution {
public:
	int orangesRotting(vector<vector<int>>& grid) {
		int ans = 0;
		GridGraph g(grid.size(), grid[0].size());
		while (true) {
			auto grid2 = grid;
			bool has1 = false, changed = false;
			for (int i = 0; i < grid.size(); i++) {
				for (int j = 0; j < grid[0].size(); j++) {
					if (grid[i][j] == 1) {
						has1 = true;
						auto v = g.getNeighbor4(g.gridId(i, j));
						for (auto id : v) {
							if (grid2[id / grid[0].size()][id%grid[0].size()] == 2) {
								grid[i][j] = 2;
								changed = true;
							}
						}
					}
				}
			}
			if (!has1)return ans;
			if (!changed)return -1;
			ans++;
		}		
		return 666;
	}
};

995. K 连续位的最小翻转次数

单人决策

996. 正方形数组的数目

哈密顿

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值