题目描述
给你一个整数数组 distance 。
从 X-Y 平面上的点 (0,0) 开始,先向北移动 distance[0] 米,然后向西移动 distance[1] 米,向南移动 distance[2] 米,向东移动 distance[3] 米,持续移动。也就是说,每次移动后你的方位会发生逆时针变化。
判断你所经过的路径是否相交。如果相交,返回 true ;否则,返回 false 。
思路
枚举可能产生相交的几种情况
1.d[i] 与 d[i - 3]d[i−3] 发生相交:此时满足 d[i] >= d[i - 2]d[i]>=d[i−2],同时 d[i - 1] <= d[i - 3]d[i−1]<=d[i−3];
2.d[i] 与 d[i - 4]d[i−4] 发生相交:此时满足 d[i - 1] = d[i - 3]d[i−1]=d[i−3],同时 d[i] + d[i - 4] >= d[i - 2]d[i]+d[i−4]>=d[i−2];
3.d[i]d[i] 与 d[i - 5]d[i−5] 发生相交:此时满足d[i - 1] <= d[i - 3]d[i−1]<=d[i−3],同时 d[i - 2] > d[i - 4]d[i−2]>d[i−4],同时 d[i] + d[i - 4] >= d[i - 2]d[i]+d[i−4]>=d[i−2],同时 d[i - 1] + d[i - 5] >= d[i - 3]d[i−1]+d[i−5]>=d[i−3]。
代码
class Solution {
public:
bool isSelfCrossing(vector<int>& distance) {
int n = distance.size();
if(n <= 3) return false;
for(int i = 3; i < n; i++) {
if(distance[i] >= distance[i-2] && distance[i-1] <= distance[i-3]) return true;
if (i >= 4 && distance[i - 1] == distance[i - 3] && distance[i] +
distance[i - 4] >= distance[i - 2]) return true;
if (i >= 5 && distance[i - 1] <= distance[i - 3] && distance[i - 2] >
distance[i - 4] && distance[i] + distance[i - 4] >= distance[i - 2] &&
distance[i - 1] + distance[i - 5] >= distance[i - 3]) return true;
}
return false;
}
};
2021.10.30 260. 只出现一次的数字 III
题目描述
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
先看一个简单的题
136.只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?(异或)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto e: nums) ret ^= e;
return ret;
}
};
思路
哈希表、排序等等都不符合线性时间复杂度+空间复杂度O(1)
使用异或
利用除答案以外的元素均出现两次,我们可以先对 numsnums 中的所有元素执行异或操作,得到 sumsum,sumsum 为两答案的异或值(sumsum 必然不为 00)。
然后取 sumsum 二进制表示中为 1 的任意一位 k,sumsum 中的第 k 位为 1 意味着两答案的第 k 位二进制表示不同。
对 numsnums 进行遍历,对第 k 位分别为 0和 1的元素分别求异或和(两答案必然会被分到不同的组),即为答案。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int xorsum = 0;
for (int num: nums) {
xorsum ^= num;
}
// 防止溢出
int lsb = (xorsum == INT_MIN ? xorsum : xorsum & (-xorsum));
int type1 = 0, type2 = 0;
for (int num: nums) {
if (num & lsb) {
type1 ^= num;
}
else {
type2 ^= num;
}
}
return {type1, type2};
}
};
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int xorsum = 0;
for(int x:nums){
xorsum ^= x;
}
int k = -1;
for(int i = 31; i >= 0 && k == -1; i--) {
if(((xorsum>>i) & 1) == 1) k = i;
}
int type1 = 0, type2 = 0;
for(int x:nums){
if(((x>>k) & 1) == 1) {
type1 ^= x;
}else {
type2 ^= x;
}
}
return {type1,type2};
}
};
2021.10.31 500. 键盘行
题目描述
思路
哈希法:每行的字母保存在哈希表中,看每个单词是否都是同一个哈希表
或者直接给字母编号,不同的编号代表不同的行数。
class Solution {
public:
vector<string> findWords(vector<string>& words) {
vector<string> ans;
string rowIdx = "12210111011122000010020202";
for (auto & word : words) {
bool isValid = true;
char idx = rowIdx[tolower(word[0]) - 'a'];
for (int i = 1; i < word.size(); ++i) {
if(rowIdx[tolower(word[i]) - 'a'] != idx) {
isValid = false;
break;
}
}
if (isValid) {
ans.emplace_back(word);
}
}
return ans;
}
};
2021.11.1 575.分糖果
class Solution {
public:
int distributeCandies(vector<int>& candyType) {
set<int> types;
int length = candyType.size() / 2;
for(int x: candyType) {
types.insert(x);
}
return length > types.size() ? types.size() : length;
}
};
2021.11.2 237.删除链表中的节点
请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。
代码
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
2021.11.3 407接雨水II
给你一个 m x n
的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
思路
和42.接雨水类似,只是从一维变到了二维。
设方块接水后的高度为water[i][j],则有:
矩阵的最外层的方块接水后的高度就是方块的自身高度,因为最外层的方块无法接水.
根据木桶原理,接到的雨水的高度由这个容器周围最短的木板来确定的。我们可以知道容器内水的高度取决于最外层高度最低的方块
我们假设已经知道最外层的方块接水后的高度的最小值,则此时我们根据木桶原理,肯定可以确定最小高度方块的相邻方块的接水高度。我们同时更新最外层的方块标记,我们在新的最外层的方块再次找到接水后的高度的最小值,同时确定与其相邻的方块的接水高度,
然后再次更新最外层,依次迭代直到求出所有的方块的接水高度,即可知道矩阵中的接水容量。
typedef pair<int,int> pii;
class Solution {
public:
int trapRainWater(vector<vector<int>>& heightMap) {
if (heightMap.size() <= 2 || heightMap[0].size() <= 2) {
return 0;
}
int m = heightMap.size();
int n = heightMap[0].size();
priority_queue<pii, vector<pii>, greater<pii>> pq;
vector<vector<bool>> visit(m, vector<bool>(n, false));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
pq.push({heightMap[i][j], i * n + j});
visit[i][j] = true;
}
}
}
int res = 0;
int dirs[] = {-1, 0, 1, 0, -1};
while (!pq.empty()) {
pii curr = pq.top();
pq.pop();
for (int k = 0; k < 4; ++k) {
int nx = curr.second / n + dirs[k];
int ny = curr.second % n + dirs[k + 1];
if( nx >= 0 && nx < m && ny >= 0 && ny < n && !visit[nx][ny]) {
if (heightMap[nx][ny] < curr.first) {
res += curr.first - heightMap[nx][ny];
}
visit[nx][ny] = true;
pq.push({max(heightMap[nx][ny], curr.first), nx * n + ny});
}
}
}
return res;
}
};
2021.11.4 367. 有效的完全平方数
思路
唯一需要注意的是:不适用库函数,遍历寻找完全平方数的时候需要放置溢出,得使用long数据类型。
class Solution {
public:
bool isPerfectSquare(int num) {
int x = (int) sqrt(num);
return x * x == num;
}
};
class Solution {
public:
bool isPerfectSquare(int num) {
long x = 1, square = 1;
while (square <= num) {
if (square == num) {
return true;
}
++x;
square = x * x;
}
return false;
}
};
class Solution {
public:
bool isPerfectSquare(int num) {
int left = 0, right = num;
while (left <= right) {
int mid = (right - left) / 2 + left;
long square = (long) mid * mid;
if (square < num) {
left = mid + 1;
} else if (square > num) {
right = mid - 1;
} else {
return true;
}
}
return false;
}
};
2021.11.5 1218.最长定差子序列
很简单的动态规划,如果写两重循环会超时。关键点是第二趟循环可以不需要,当考虑序列中第i个元素时,需要找到0-i之间是否存在j满足arr[j] = arr[i] - difference。所以可以使用哈希表存起来。
class Solution {
public:
int longestSubsequence(vector<int> &arr, int difference) {
int ans = 0;
unordered_map<int, int> dp;
for (int v: arr) {
dp[v] = dp[v - difference] + 1;
ans = max(ans, dp[v]);
}
return ans;
}
};
观察题目给定的数据范围为 -10^4到 10^4,且 d 的范围也是如此,为了防止越界,所以,我们声明一个4∗10^4的数组来记录以某个数结尾的长度。这样就可以不用map,提高效率。防止数组越界,需要理解v+20000的意思。
class Solution {
public:
int longestSubsequence(vector<int> &arr, int difference) {
int ans = 0;
vector<int> dp(40001,0);
for (int v: arr) {
dp[v + 20000] = dp[v + 20000 - difference] + 1;
ans = max(ans, dp[v + 20000]);
}
return ans;
}
};
2021.11.6 268.丢失的数字
思路
思路1 排序
class Solution {
public:
int missingNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n = nums.size();
for (int i = 0; i < n; i++) {
if (nums[i] != i) {
return i;
}
}
return n;
}
};
思路2 哈希
思路3 异或
数组nums 中有n个数,在这n个数的后面添加从0到n的每个整数,则添加了n+1个整数,共有 2n+1整数。缺失的数字只出现了一次,对这2n+1个数异或,得到的结果即为缺失的数字。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int res = 0;
int n = nums.size();
for (int i = 0; i < n; i++) {
res ^= nums[i];
}
for (int i = 0; i <= n; i++) {
res ^= i;
}
return res;
}
};
思路4:所有的下标和相加减去出现的数字即为缺失的数字。
思路5
遍历一轮,对于出现的数字i,则将nums[abs(i)] 记为-1*nums[[abs(i)]]。再遍历一轮下来,数字为正的下标则是缺失的数字。
注意细节:
- 对于某个下标i,若其值为n,则跳过。
- 对于某个下标i,若其值为0,将nums[i] 记为-1*n,同时将nums[0] 记为-1*abs(num[0])。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < n; i++) {
int value = abs(nums[i]);//下标
if(value == n) continue;
if(nums[value] == 0) {
nums[value] = -1 * n;
nums[0] = -1 * abs(nums[0]);
continue;
}
nums[value] = -1 * nums[value];
}
for(int i = 0; i < n; i++) {
if(nums[i] >= 0 ) {
return i;
}
}
return n;
}
};
2021.11.7 598.范围求和
思路
贼简单
class Solution {
public:
int maxCount(int m, int n, vector<vector<int>>& ops) {
int hang = m+1, lie = n+1;
if(ops.size() == 0) return m * n;
for(int i = 0; i < ops.size(); i++) {
for(int j = 0; j < 2; j++) {
hang = min(hang,ops[i][0]);
lie = min(lie,ops[i][1]);
}
}
return hang * lie;
}
};
2021.11.8 299.猜数字游戏
思路
代码
class Solution {
public:
string getHint(string secret, string guess) {
int length = secret.size();
int a_sum = 0;
int b_sum = 0;
vector<int> sta1(10,0);
vector<int> sta2(10,0);
for(int i = 0; i < length; i++) {
int x1 = secret[i] -'0';
int x2 = guess[i] -'0';
if(x1 == x2) {
a_sum += 1;
continue;
}
sta1[x1] += 1;
sta2[x2] += 1;
}
for(int i = 0; i < 10; i++) {
b_sum += min(sta1[i],sta2[i]);
}
return to_string(a_sum) + "A"+to_string(b_sum) + "B";
}
};