Category Archives: LeetCode

LeetCode How Many Numbers Are Smaller Than the Current Number

1365. How Many Numbers Are Smaller Than the Current Number

Given the array nums, for each nums[i] find out how many numbers in the array are smaller than it. That is, for each nums[i] you have to count the number of valid j's such that j != i and nums[j] < nums[i].

Return the answer in an array.

Example 1:

Input: nums = [8,1,2,2,3]
Output: [4,0,1,1,3]
Explanation: 
For nums[0]=8 there exist four smaller numbers than it (1, 2, 2 and 3). 
For nums[1]=1 does not exist any smaller number than it.
For nums[2]=2 there exist one smaller number than it (1). 
For nums[3]=2 there exist one smaller number than it (1). 
For nums[4]=3 there exist three smaller numbers than it (1, 2 and 2).

Example 2:

Input: nums = [6,5,4,8]
Output: [2,1,0,3]

Example 3:

Input: nums = [7,7,7,7]
Output: [0,0,0,0]

Constraints:

  • 2 <= nums.length <= 500
  • 0 <= nums[i] <= 100

给定一个数组,对于数组中的每个元素,计算数组中有多少个数小于这个元素。

简单题。因为数组元素的范围很小,在[0, 100]之间,所以直接用hash计算出每个元素出现的次数,然后算出前n项累加和,即可得出小于当前项的元素个数之和,最后查表即可。

完整代码如下:

class Solution {
public:
	vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
		const int MAXN = 101;
		vector<int> ans(nums.size(), 0);
		vector<int> cnt(MAXN, 0);
		for (int i = 0; i < nums.size(); ++i) {
			++cnt[nums[i]];
		}
		for (int i = 1; i < MAXN; ++i) {
			cnt[i] += cnt[i - 1];
		}
		for (int i = 0; i < nums.size(); ++i) {
			if (nums[i] > 0) {
				ans[i] = cnt[nums[i] - 1];
			}
		}
		return ans;
	}
};

本代码提交AC,用时12MS。

LeetCode Image Smoother

661. Image Smoother

Given a 2D integer matrix M representing the gray scale of an image, you need to design a smoother to make the gray scale of each cell becomes the average gray scale (rounding down) of all the 8 surrounding cells and itself. If a cell has less than 8 surrounding cells, then use as many as you can.

Example 1:

Input:
[[1,1,1],
 [1,0,1],
 [1,1,1]]
Output:
[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]]
Explanation:
For the point (0,0), (0,2), (2,0), (2,2): floor(3/4) = floor(0.75) = 0
For the point (0,1), (1,0), (1,2), (2,1): floor(5/6) = floor(0.83333333) = 0
For the point (1,1): floor(8/9) = floor(0.88888889) = 0

Note:

  1. The value in the given matrix is in the range of [0, 255].
  2. The length and width of the given matrix are in the range of [1, 150].

简单题,直接根据题意做就行。

class Solution {
public:
	vector<vector<int>> imageSmoother(vector<vector<int>>& M) {
		int x = M.size(), y = M[0].size();
		vector<vector<int>> A(x, vector<int>(y, 0));
		for (int i = 0; i < x; ++i) {
			for (int j = 0; j < y; ++j) {
				int s = 0, n = 0;
				for (int u = i - 1 >= 0 ? i - 1 : 0; u < x && u <= i + 1; ++u) {
					for (int v = j - 1 >= 0 ? j - 1 : 0; v < y && v <= j + 1; ++v) {
						s += M[u][v];
						++n;
					}
				}
				A[i][j] = s / n;
			}
		}
		return A;
	}
};

本代码提交AC,用时148MS。其实还可以保存之前算过的结果,给后面的计算节省时间,但是我估计也节省不了多少,因为对每个位置,穷举也就做9次加法,节省也快不了多少。

LeetCode Closest Divisors

1362. Closest Divisors

Given an integer num, find the closest two integers in absolute difference whose product equals num + 1 or num + 2.

Return the two integers in any order.

Example 1:

Input: num = 8
Output: [3,3]
Explanation: For num + 1 = 9, the closest divisors are 3 & 3, for num + 2 = 10, the closest divisors are 2 & 5, hence 3 & 3 is chosen.

Example 2:

Input: num = 123
Output: [5,25]

Example 3:

Input: num = 999
Output: [40,25]

Constraints:

  • 1 <= num <= 10^9

给定一个数num,找出所有a*b等于num+1或num+2的(a,b)组合中,a和b的差最小的那个组合。

子问题就是给定一个数num,找出a*b==num的(a,b)组合中,a和b的差最小的组合。根据反证法,a和b必定有一个小于等于sqrt(num),另一个大于等于sqrt(num)。所以我最开始的做法是,用之前快速求sqrt的方法,计算sqrt(num),令left和right都等于sqrt(num),然后判断left*right的积,如果大于num,则–right;否则++left。这样虽然能得到正确结果,但提交后TLE。

百思不得其解,后来看别人的解答,才理解为什么我会TLE。我的解法设置了left和right两个指针,根据乘积的大小关系,left和right都在移动,在某些情况总的移动数会很大。而正确解法是,只设置一个移动指针i,i从sqrt(num)递减,然后判断num是否能整除i,如果能整除,则肯定找到一个因式分解。这种方法相当于我的方法中只移动了left,速度快了不少。在这种解法下,用不用快速sqrt都能AC。

完整代码如下:

class Solution {
public:
	int mySqrt(int x)
	{
		long long y = x;
		while (y*y > x)
		{
			y = (y + x / y) / 2;
		}
		return y;
	}

	vector<int> work(int num) {
		for (int i = mySqrt(num) + 1; i >= 1; --i) {
			if (num%i == 0) {
				return { i,num / i };
			}
		}
		return { 1,num };
	}
	vector<int> closestDivisors(int num) {
		vector<int> plus1 = work(num + 1);
		vector<int> plus2 = work(num + 2);
		if (abs(plus1[1] - plus1[0]) < abs(plus2[1] - plus2[0]))return plus1;
		else return plus2;
	}
};

LeetCode Number of Days Between Two Dates

1360. Number of Days Between Two Dates

Write a program to count the number of days between two dates.

The two dates are given as strings, their format is YYYY-MM-DD as shown in the examples.

Example 1:

Input: date1 = "2019-06-29", date2 = "2019-06-30"
Output: 1

Example 2:

Input: date1 = "2020-01-15", date2 = "2019-12-31"
Output: 15

Constraints:

  • The given dates are valid dates between the years 1971 and 2100.

给定两个日期,计算这两个日期之间相隔的天数。

比较简单。如果年份和月份相同,直接天数作差。如果年份相同月份不同,则先把相隔的完整月份的天数算出来,存到part2;对于较早的日期,算出这个月还剩的天数part1;对于较晚的日期,算出这个月走过的天数part3;三者加起来。如果年份也不同,和上一种情况类似,先把间隔的完整年的天数算出来,寸到part2;对于较早的日期,算出这一年还剩的天数part2;对于较晚的日期,算出这一年走过的天数part3;三者加起来。

判断闰年的标准:是400的倍数;或者是4的倍数但不是100的倍数。

完整代码如下:

struct Date {
	int year;
	int month;
	int day;
	Date() {
		year = month = day = 0;
	}
	void parse(string date) {
		year = stoi(date.substr(0, 4));
		month = stoi(date.substr(5, 2));
		day = stoi(date.substr(8, 2));
	}
};
class Solution {
public:
	bool isRunYear(int year) {
		return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
	}
	int DaysInMonth(int m, int y) {
		switch (m) {
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			return 31;
		case 2:
			if (isRunYear(y))return 29;
			else return 28;
		default:
			return 30;
		}
	}

	int DaysInYear(int year) {
		if (isRunYear(year))return 366;
		else return 365;
	}

	int daysBetweenDates(string date1, string date2) {
		Date d1, d2;
		if (date1 < date2) {
			d1.parse(date1);
			d2.parse(date2);
		}
		else {
			d1.parse(date2);
			d2.parse(date1);
		}

		if (d1.year == d2.year && d1.month==d2.month) {
			return d2.day - d1.day;
		}
		else if (d1.year == d2.year) {
			int part1 = DaysInMonth(d1.month, d1.year) - d1.day;
			int part3 = d2.day;
			int part2 = 0;
			for (int m = d1.month + 1; m < d2.month; ++m) {
				part2 += DaysInMonth(m, d1.year);
			}
			return part1 + part2 + part3;
		}
		else {
			int part1 = DaysInMonth(d1.month, d1.year) - d1.day;
			for (int m = d1.month + 1; m <= 12; ++m) {
				part1 += DaysInMonth(m, d1.year);
			}
			int part2 = 0;
			for (int y = d1.year + 1; y < d2.year; ++y) {
				part2 += DaysInYear(y);
			}
			int part3 = d2.day;
			for (int m = 1; m < d2.month; ++m) {
				part3 += DaysInMonth(m, d2.year);
			}
			return part1 + part2 + part3;
		}
	}
};

本代码提交AC,用时4MS。

LeetCode Find K Closest Elements

LeetCode Find K Closest Elements Given a sorted array, two integers k and x, find the k closest elements to x in the array. The result should also be sorted in ascending order. If there is a tie, the smaller elements are always preferred. Example 1:

Input: [1,2,3,4,5], k=4, x=3
Output: [1,2,3,4]
Example 2:
Input: [1,2,3,4,5], k=4, x=-1
Output: [1,2,3,4]
Note:
  1. The value k is positive and will always be smaller than the length of the sorted array.
  2. Length of the given array is positive and will not exceed 104
  3. Absolute value of elements in the array and x will not exceed 104

给定一个有序数组,要从中找出k个和x最接近的元素,按升序排列输出。 首先在有序数组中用二分查找找到x的lowerbound,如果lowerbound指向begin(),则取开头k个元素即可;如果lowerbound指向end(),则取末尾k个元素;否则,令left指向lowerbound-1,right指向lowerbound,每次取left、right中和x差值最小的那个数,直到取够k个为止。最后对结果数组排序。 完整代码如下: [cpp] class Solution { public: vector<int> findClosestElements(vector<int>& arr, int k, int x) { vector<int> ans; int n = arr.size(); auto it = lower_bound(arr.begin(), arr.end(), x); if (it == arr.begin()) { while (ans.size() < k) { ans.push_back(*it++); } return ans; } else if (it == arr.end()) { while (ans.size() < k) { ans.push_back(*–it); } return ans; } else { int right = it – arr.begin(); int left = right – 1; int left_diff = INT_MAX, right_diff = INT_MAX; while (ans.size() < k) { if (left >= 0)left_diff = abs(arr[left] – x); else left_diff = INT_MAX; if (right < n)right_diff = abs(arr[right] – x); else right_diff = INT_MAX; if (left_diff <= right_diff) { ans.push_back(arr[left]); –left; } else { ans.push_back(arr[right]); ++right; } } } sort(ans.begin(), ans.end()); return ans; } }; [/cpp] 本代码提交AC,用时143MS。]]>

LeetCode Judge Route Circle

LeetCode Judge Route Circle Initially, there is a Robot at position (0, 0). Given a sequence of its moves, judge if this robot makes a circle, which means it moves back to the original place. The move sequence is represented by a string. And each move is represent by a character. The valid robot moves are R (Right), L (Left), U (Up) and D (down). The output should be true or false representing whether the robot makes a circle. Example 1:

Input: "UD"
Output: true
Example 2:
Input: "LL"
Output: false

一个机器人,初始站在原点(0,0),UDLR分别表示上下左右,给定一个机器人行走的轨迹字符串,问最终机器人能回到原点吗。 简单题,直接判断走过的水平和垂直方向的差值是否为0,代码如下: [cpp] class Solution { public: bool judgeCircle(string moves) { int horizon = 0, vertical = 0; for (auto c : moves) { if (c == ‘U’)++vertical; else if (c == ‘D’)–vertical; else if (c == ‘L’)–horizon; else if (c == ‘R’)++horizon; } return horizon == 0 && vertical == 0; } }; [/cpp] 本代码提交AC,用时29MS。]]>

LeetCode Dota2 Senate

LeetCode Dota2 Senate In the world of Dota2, there are two parties: the Radiant and the Dire. The Dota2 senate consists of senators coming from two parties. Now the senate wants to make a decision about a change in the Dota2 game. The voting for this change is a round-based procedure. In each round, each senator can exercise one of the two rights:

  1. Ban one senator's right: A senator can make another senator lose all his rights in this and all the following rounds.
  2. Announce the victory: If this senator found the senators who still have rights to vote are all from the same party, he can announce the victory and make the decision about the change in the game.
Given a string representing each senator’s party belonging. The character ‘R’ and ‘D’ represent the Radiant party and the Dire party respectively. Then if there are n senators, the size of the given string will be n. The round-based procedure starts from the first senator to the last senator in the given order. This procedure will last until the end of voting. All the senators who have lost their rights will be skipped during the procedure. Suppose every senator is smart enough and will play the best strategy for his own party, you need to predict which party will finally announce the victory and make the change in the Dota2 game. The output should be Radiant or Dire. Example 1:
Input: "RD"
Output: "Radiant"
Explanation: The first senator comes from Radiant and he can just ban the next senator's right in the round 1.
And the second senator can't exercise any rights any more since his right has been banned.
And in the round 2, the first senator can just announce the victory since he is the only guy in the senate who can vote.
Example 2:
Input: "RDD"
Output: "Dire"
Explanation:
The first senator comes from Radiant and he can just ban the next senator's right in the round 1.
And the second senator can't exercise any rights anymore since his right has been banned.
And the third senator comes from Dire and he can ban the first senator's right in the round 1.
And in the round 2, the third senator can just announce the victory since he is the only guy in the senate who can vote.
Note:
  1. The length of the given string will in the range [1, 10,000].

Dota2中有两个党派,分别是Radiant和Dire,分别用R和D表示。给定一个长度为n的字符串s,表示n个人,如果s[i]=’R’,表示第i个人是Radiant党的人;如果s[i]=’D’,表示第i个人是Dire党的人。 投票过程是从1~n不断重复的,每个人可以禁止任何一个其他人投票。如果某个人投票之后,剩下的人都是同一个党派的,则该党派胜利。问最终是哪个党派的人胜利。 使用贪心的原则,因为是round-based的投票方式,为了获胜,第i个人肯定是禁止其后的第一个非本党人员投票。比如序列DRDRR: 第一个D如果杀其后的第一个R:
  1. DXDRR
  2. DXDXR
  3. XXDXR
  4. XXDXX
Dire胜利;第一个D如果杀其后的第二个R:
  1. DRDXR
  2. DRXXR
  3. XRXXR
Radiant胜利。 这其实很好理解,因为是round-based的投票方式,对于D来说,他必须首先把其后的第一个R杀掉,要不然很快就会轮到这个R杀己方人员了。 round-based的方式是用%n的方式实现,代码如下: [cpp] class Solution { public: string predictPartyVictory(string senate) { int r_cnt = 0, d_cnt = 0, n = senate.size(); for (int i = 0; i < n; ++i) { if (senate[i] == ‘R’) ++r_cnt; else ++d_cnt; } int pos = 0; while (r_cnt > 0 && d_cnt > 0) { if (senate[pos] == ‘X’) { pos = (pos + 1) % n; continue; } int pos_next = (pos + 1) % n; while (senate[pos_next] == senate[pos] || senate[pos_next] == ‘X’) pos_next = (pos_next + 1) % n; if (senate[pos_next] == ‘R’) –r_cnt; else –d_cnt; senate[pos_next] = ‘X’; pos = (pos + 1) % n; } return r_cnt == 0 ? "Dire" : "Radiant"; } }; [/cpp] 本代码提交AC,用时752MS。]]>

LeetCode 4 Keys Keyboard

LeetCode 4 Keys Keyboard Imagine you have a special keyboard with the following keys: Key 1: (A): Prints one ‘A’ on screen. Key 2: (Ctrl-A): Select the whole screen. Key 3: (Ctrl-C): Copy selection to buffer. Key 4: (Ctrl-V): Print buffer on screen appending it after what has already been printed. Now, you can only press the keyboard for N times (with the above four keys), find out the maximum numbers of ‘A’ you can print on screen. Example 1:

Input: N = 3
Output: 3
Explanation:
We can at most get 3 A's on screen by pressing following key sequence:
A, A, A
Example 2:
Input: N = 7
Output: 9
Explanation:
We can at most get 9 A's on screen by pressing following key sequence:
A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V
Note:
  1. 1 <= N <= 50
  2. Answers will be in the range of 32-bit signed integer.

给定四个按键,有四种操作:
  • A: 打印一个字符A
  • Ctrl-A: 全选
  • Ctrl-C: 复制
  • Ctrl-V: 粘贴
问通过n次按键操作,最多可以打印出多少个字符。 稍微分析一下,增加字符比较快的方法是用Ctrl-V,连带着需要Ctrl-C和Ctrl-A的配合,所以要想快速增加字符,必须要以Ctrl-A, Ctrl-C, Ctrl-V结尾,这就需要3个操作。当n比较小时,这3个操作相对来说比较耗时,通过分析可知,当N<=6时,直接用N次Print(A)操作能得到最多的字符。 假设dp[i]表示使用i次操作能得到的最多字符,显然dp[i]=i,当i<=6时。 当i>6时,我们可以考虑用Ctrl-A, Ctrl-C, Ctrl-V,所以结尾我们至少要空出3个操作来使用技能,也就是我们最多只能在i-3的地方开始释放Ctrl-A, Ctrl-C, Ctrl-V技能,假设释放技能的位置是b,则b<=i-3;下界是我们可以在一开始只有一个字符时使用技能,即b>=1。所以我们遍历[1,i-3]的地方释放技能,则我们可以有i-b-2个Ctrl-V结尾,再加上释放技能的位置已经有一个dp[b]了,所以总字符数是dp[b]*(i-b-1)。使用贪心策略求最大值。 完整代码如下: [cpp] class Solution { public: int maxA(int N) { if (N <= 6)return N; vector<int> dp(N + 1, 0); for (int i = 1; i <= 6; ++i)dp[i] = i; for (int i = 7; i <= N; ++i) { for (int b = i – 3; b >= 1; –b) { dp[i] = max(dp[i], dp[b] + dp[b] * (i – b – 2)); } } return dp[N]; } }; [/cpp] 本代码提交AC,用时0MS。]]>

LeetCode 2 Keys Keyboard

LeetCode 2 Keys Keyboard Initially on a notepad only one character ‘A’ is present. You can perform two operations on this notepad for each step:

  1. Copy All: You can copy all the characters present on the notepad (partial copy is not allowed).
  2. Paste: You can paste the characters which are copied last time.
Given a number n. You have to get exactly n ‘A’ on the notepad by performing the minimum number of steps permitted. Output the minimum number of steps to get n ‘A’. Example 1:
Input: 3
Output: 3
Explanation:
Intitally, we have one character 'A'.
In step 1, we use Copy All operation.
In step 2, we use Paste operation to get 'AA'.
In step 3, we use Paste operation to get 'AAA'.
Note:
  1. The n will be in the range [1, 1000].

一个记事本中原本只有一个字符A,每次可以做两种操作中的一种,分别是全选和粘贴。问要得到n个字符A,至少需要经过多少次操作。 这一题我一开始想到用BFS来做,两种操作相当于两条搜索路径,所以很容易能写出BFS的代码: [cpp] class Solution { private: struct Node { int presented_cnt_; // 现在有多少个字符 int copied_cnt_; // 剪切板中有多少个字符 int step_; // 走过多少步了 Node(int p, int c, int s) :presented_cnt_(p), copied_cnt_(c), step_(s) {}; }; public: int minSteps(int n) { queue<Node> q; Node node(1, 0, 0); q.push(node); while (!q.empty()) { Node cur = q.front(); q.pop(); if (cur.presented_cnt_ == n) return cur.step_; if (cur.presented_cnt_ > n)continue; Node copy_node(cur.presented_cnt_, cur.presented_cnt_, cur.step_ + 1); // 全选 q.push(copy_node); if (cur.copied_cnt_ != 0) { // 粘贴 Node paste_node(cur.presented_cnt_ + cur.copied_cnt_, cur.copied_cnt_, cur.step_ + 1); q.push(paste_node); } } } }; [/cpp] 预料到了,本代码提交TLE。 搜索会超时的,用DP一般都不会超时。设dp[i]表示要得到i个字符A,至少需要的操作数。显然,dp[1]=0。 假设已经求到了dp[1,…,i-1],现在要求dp[i]。要得到i个字符,最多的操作数是i,即先复制一个A,然后粘贴i-1次,总共需要i次操作。比较快的方法是,如果i是偶数,则可以从i/2个字符开始,全选粘贴就能得到i个字符。 所以我们遍历i前面的所有j,如果i能整除j,则可以全选j,然后粘贴i/j-1次得到i个字符,即操作数是1+(i/j-1)=i/j,当然还需要加上到达i的操作数,所以dp[j]=dp[i]+i/j。 完整代码如下: [cpp] class Solution { public: int minSteps(int n) { vector<int> dp(n + 1, INT_MAX); dp[1] = 0; for (int i = 2; i <= n; ++i) { dp[i] = i; for (int j = i – 1; j >= 1; –j) { if (i%j == 0) { dp[i] = min(dp[i], dp[j] + i / j); } } } return dp[n]; } }; [/cpp] 本代码提交AC,用时79MS。 还有一种解法类似于素数筛法,我们首先知道dp[1]=0,如果我们对1个字符先全选,然后不停粘贴,则能得到2,3,4,…个字符,且操作数是2,3,4,…;下一次,我们知道dp[2]=2,如果我们对2个字符全选,然后不停粘贴,则能得到4,6,8,…个字符,且操作数是4,5,6,…。每一轮我们都只保留最小操作数。最终筛完之后,就是全局最优结果了。 完整代码如下: [cpp] class Solution { public: int minSteps(int n) { vector<int> dp(n + 1, INT_MAX); dp[1] = 0; for (int i = 1; i <= n; ++i) { for (int j = 2 * i, k = 2; j <= n; j += i, ++k) { dp[j] = min(dp[j], dp[i] + k); } } return dp[n]; } }; [/cpp] 本代码提交AC,用时3MS,速度飞快,因为越到后面,循环倍数越少。]]>

LeetCode Find Duplicate Subtrees

LeetCode Find Duplicate Subtrees Given a binary tree, return all duplicate subtrees. For each kind of duplicate subtrees, you only need to return the root node of any one of them. Two trees are duplicate if they have the same structure with same node values. Example 1: 

        1
       / \
      2   3
     /   / \
    4   2   4
       /
      4
The following are two duplicate subtrees:
      2
     /
    4
and
    4
Therefore, you need to return above trees’ root in the form of a list.
给定一棵二叉树,要求找出其中的重复子树,每组重复子树输出其中一个子树即可。 去重最好的办法就是hash,所以思想是把每个子树hash到一个unordered_map中,如果有多个子树hash到同一个桶,则出现了重复。 所以问题的关键是怎样对一个子树hash。我们可以把一棵子树表示成 (left_hash, root, right_hash),其中left_hash和right_hash分别是左子树和右子树的hash字符串,root表示当前根节点的hash字符串。所以这是一个递归定义。规定空节点的hash值为空串””。 根据子树hash的定义,叶子节点的hash值是(“”,val,””)。求解子树hash值的过程可以用中序遍历,左根右。为什么子树hash中需要逗号和括号呢,就是为了保证不同严格区分不同的子树,因为单纯的中序遍历是无法区分某些子树的,比如下面的两个子树,中序遍历结果都是2,1,无法区分;但是用本文的hash方法,得到的hash值分别是(2,1,)和(,2,1),这两个字符串是有区别的。
  1
 /
2
 2
  \
   1
在DFS的过程中,把每个子树的hash值记录到一个unordered_map中,然后遍历unordered_map,如果该桶放了多个子树,则取出其中一个即可。 完整代码如下: [cpp] class Solution { private: string DFS(TreeNode* root, unordered_map<string, vector<TreeNode*>>& inorders) { if (root == NULL)return ""; string left = DFS(root->left, inorders); string right = DFS(root->right, inorders); string cur = "(" + left + "," + to_string(root->val) + "," + right + ")"; inorders[cur].push_back(root); return cur; } public: vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) { unordered_map<string, vector<TreeNode*>> inorders; DFS(root, inorders); vector<TreeNode*> ans; for (auto &it : inorders) { if (it.second.size() > 1) ans.push_back(it.second[0]); } return ans; } }; [/cpp] 本代码提交AC,用时52MS。]]>