57. Insert Interval
思路 模拟
用指针去扫 intervals,最多可能有三个阶段:
- 不重叠的绿区间,在蓝区间的左边
- 有重叠的绿区间
- 不重叠的绿区间,在蓝区间的右边
逐个分析
不重叠,需满足:绿区间的右端,位于蓝区间的左端的左边,如 [1,2]。
则当前绿区间,推入 ans数组,指针 +1,考察下一个绿区间。
循环结束时,当前绿区间的屁股,就没落在蓝区间之前,有重叠了,如 [3,5]。
现在看重叠的。我们反过来想,没重叠,就要满足:绿区间的左端,落在蓝区间的屁股的后面,反之就有重叠:绿区间的左端 <= 蓝区间的右端,极端的例子就是 [8,10]。
和蓝有重叠的区间,会合并成一个区间:左端取蓝绿左端的较小者,右端取蓝绿右端的较大者,不断更新给蓝区间。
循环结束时,将蓝区间(它是合并后的新区间)推入 ans 数组。
剩下的,都在蓝区间右边,不重叠。不用额外判断,依次推入 ans 数组。
代码
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
vector<vector<int>> ans;
int i=0;
for (; i < intervals.size(); ++i) {
if (intervals[i][1]<newInterval[0]){
ans.push_back(intervals[i]);
}
else{
break;
}
}
int i_begin=i;
int end=newInterval[1];
if (i_begin>=intervals.size()){
intervals.push_back(newInterval);
return intervals;
}
int begin=min(intervals[i_begin][0],newInterval[0]);
while (i<intervals.size()&&intervals[i][1]<=newInterval[1]){
i++;
}
if (i>=intervals.size()){
ans.push_back({begin,newInterval[1]});
return ans;
}
if (intervals[i][0]<=newInterval[1])
{
end=intervals[i][1];
i++;
}
ans.push_back({begin,end});
for (; i < intervals.size(); ++i) {
ans.push_back(intervals[i]);
}
return ans;
}
};
*137. Single Number II
思路 数学
如下图所示,考虑数字的二进制形式,对于出现三次的数字,各二进制位出现的次数都是 3
的倍数。 因此,统计所有数字的各二进制位中1
的出现次数,并对 3
求余,结果则为只出现一次的数字。
代码
class Solution {
public int singleNumber(int[] nums) {
int ans=0;
for (int i = 0;i<32;i++){
int total=0;
for (int num:nums){
total+=(num>>i)&1;
}
ans|=(total%3)<<i;
}
return ans;
}
}
*260. Single Number III
思路
代码实现时,需要找到异或和中的某个值为 1
的比特位。
一种方式是计算 lowbit,只保留二进制最低位的 1
,举例如下:
s = 101100
~s = 010011
(~s)+1 = 010100 // 根据补码的定义,这就是 -s 最低 1 左侧取反,右侧不变
s & -s = 000100 // lowbit
代码
class Solution {
public int[] singleNumber(int[] nums) {
int xor=0;
for (int val:nums){
xor=val^xor;
}
int lowBit=getLowBit(xor);
int xorA=0;
int xorB=0;
for (int val:nums){
if ((val&lowBit)>=1){
xorA^=val;
}
else{
xorB^=val;
}
}
return new int[]{xorA,xorB};
}
static int getLowBit(int x){
return x&-x;
}
}
764. Largest Plus Sign
思路
对数据进行缓存,缓存每一个坐标的当前列·行的长度信息至max_length_column
与max_length_row
。若对目标矩阵的每一个元素进行遍历,若缓存不存在则建立缓存,若缓存存在则直接返回缓存结果。最终的十字大小为min(列,行)
代码
class Solution {
private int n;
private int[][] mines_arr;
private int[][] max_length_column;
private int[][] max_length_row;
public int orderOfLargestPlusSign(int _n, int[][] mines) {
n = _n;
mines_arr = new int[n][n];
max_length_column = new int[n][n];
max_length_row = new int[n][n];
if (n * n == mines.length) {
return 0;
}
for (int[] vec : mines) {
mines_arr[vec[0]][vec[1]] = 1;
}
int curr_ans = 1;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (mines_arr[i][j] == 1) {
continue;
}
int column_length = max_length_column[i][j];
if (column_length == 0) {
updateColumn(i, j);
column_length = max_length_column[i][j];
}
int row_length = max_length_row[i][j];
if (row_length == 0) {
updateRow(i, j);
row_length = max_length_row[i][j];
}
curr_ans = Math.max(Math.min(column_length, row_length), curr_ans);
if (curr_ans == 3) {
int a = 3;
}
}
}
return curr_ans;
}
private void updateColumn(int r, int c) {
int r_org = r;
int curr = 1;
while (r < n && mines_arr[r][c] != 1) {
r++;
}
r--;
for (int i = r_org; i <= r; i++, r--) {
max_length_column[i][c] = curr;
max_length_column[r][c] = curr;
curr++;
}
}
private void updateRow(int r, int c) {
int column_org = c;
int curr = 1;
while (c < n && mines_arr[r][c] != 1) {
c++;
}
c--;
for (int i = column_org; i <= c; i++, c--) {
max_length_row[r][i] = curr;
max_length_row[r][c] = curr;
curr++;
}
}
}
766. Toeplitz Matrix
思路
模拟题,按照顺序遍历即可
代码
bool isToeplitzMatrix(vector<vector<int>> &matrix) {
int column_length=matrix[0].size();
int row_length=matrix.size();
int r = 0;
int c = 0;
while (c < column_length) {
int first_element=matrix[0][c];
for (int i = 1; i < min(column_length-c,row_length); ++i) {
if (matrix[i][c+i]!=first_element){
return false;
}
}
c++;
}
while (r < row_length) {
int first_element=matrix[r][0];
for (int i = 1; i < min(row_length-r,column_length); ++i) {
if (matrix[r+i][i]!=first_element){
return false;
}
}
r++;
}
return true;
}
796. Rotate String
思路(相对顺序不变)
首先应考虑原字符串和目标字符串长度相同。
考虑旋转后相对顺序不变的特点,在字符串后添加一个相同的原字符串,直接在修改后的字符串寻找是否存在目标字串即可。
bool rotateString(string _s, string goal) {
if (_s.length()!=goal.length()){
return false;
}
string s = _s + _s;
if (s.find(goal) != string::npos) {
return true;
}
return false;
}
816. Ambiguous Coordinates
思路 模拟
从坐标1开始尝试对每一个位置进行左右切分,并确认切分后的字串有多少种合法形式,然后令左右字串的合法形式做叉集即可。
代码
class Solution {
List<String> ans=new ArrayList<>();
public List<String> ambiguousCoordinates(String s) {
s=s.substring(1,s.length()-1);
for (int i=1;i<s.length();i++){
combineResults(s,i);
}
return ans;
}
public void combineResults(String str,int idx){
String[] x_axis=getValidateStrs(str.substring(0,idx));
String[] y_axis=getValidateStrs(str.substring(idx));
for (var x:x_axis){
for (var y:y_axis){
ans.add("("+x+", "+y+")");
}
}
}
public String[] getValidateStrs(String str){
ArrayList<String> res=new ArrayList<>();
char[] ch_list=str.toCharArray();
for (int i=1;i<ch_list.length;i++){
String integer_part=String.valueOf(ch_list,0,i);
if (integer_part.matches("^0.+")){
continue;
}
String decimal_part= str.substring(i);
if (decimal_part.endsWith("0")){
continue;
};
res.add(integer_part+"."+decimal_part);
}
if (!str.matches("^0.+")){
res.add(str);
}
return res.toArray(new String[0]);
}
}
*899. Orderly Queue
思路 数学
当 k = = 1 k==1 k==1时,相当于将一个环切开,使字典序最小,直接模拟即可,无需多言 当k>=2时,可以证明,经过有限次的操作,一定可以变成升序,所以直接sort返回即可 下面证明之,分为两步
- 可以交换任意两个相邻的字符 我们总是可以把要交换的两个字符移到最前面,然后先移动第二个,再移动第一个,接着复原,就达成了交换的目的。 举个例子,例如
b
d
c
a
e
bdcae
bdcae,我们希望交换c和a,依次执行如下操作
- 将bd移到后面,使ca移到最前面,得到 c a e b d caebd caebd
- 先移动a,再移动c,实现交换,得到 e b d a c ebdac ebdac
- 其他位置复原,即将e移动到后面,得到$bdace4,完成交换
- 通过不断交换两个相邻字符,可以排列成升序 类似于冒泡排序的思想,不断地比较并交换相邻的字符,可以依次地将最大的、次大的、第三大的……移到后面,完成升序排列。
综上,我们就证明了当k>=2时,一定可以得到有序字符串。
代码
string orderlyQueue(string s, int k) {
if(k==1){
string ans=s;
for(int i=0;i<s.size();i++){
s+=s[0];
s.erase(0,1);
ans=min(ans,s);
}
return ans;
}
else{
std::sort(s.begin(), s.end());
return s;
}
}
921. Minimum Add to Make Parentheses Valid
思路 统计
我们从左向右遍历统计当前数列中左括号(
与右括号)
的差值cnt
,设当前坐标为’i
。考虑下列情况
- cnt<0(左括号的数量小于右括号):这种情况例如对于序列
())(
,当i=2时,右括号比左括号多一个,这种情况下显然我们需要手动添加左括号,因此ans+1
,cnt++
。 - cnt>0(左括号的数量大于右括号):无需理会,因为后面可能出现新的右括号。
当遍历结束时,我们令ans
加上cnt
即可。
代码
public int minAddToMakeValid(String s) {
int ans=0;
int curr_cnt=0;
for (char ch:s.toCharArray()){
if (ch=='('){
curr_cnt++;
}
else{
curr_cnt--;
if (curr_cnt<0){
curr_cnt++;
ans++;
}
}
}
return ans+curr_cnt;
}
937. Reorder Data in Log Files
思路 模拟
按照题目要求,首先对数字类和字母类的log进行分类,数组类的直接并入ArrayList、字母类的宪先并入,最后按照要求排序即可。要注意的是,处理字母类时最好存储为ArrayList<String[]> 方便排序。
代码
class Solution {
public String[] reorderLogFiles(String[] logs) {
ArrayList<String[]>alphaSet=new ArrayList<>();
ArrayList<String>numSet=new ArrayList<>();
for (String log:logs){
if (judgeNum(log)){
numSet.add(log);
continue;
}
alphaSet.add(log.split(" "));
alphaSet.sort((o1,o2)->{
int curr=1;
for (; curr < Math.min(o1.length,o2.length); curr++) {
int cmp=o1[curr].compareTo(o2[curr]);
if (cmp!=0){
return cmp;
}
}
if (o1.length==o2.length){
return o1[0].compareTo(o2[0]);
}
if (o1.length<o2.length){
return -1;
}
return 1;
});
}
String[] ans=new String[logs.length];
ArrayList<String> res= alphaSet.stream().map(obj->
String.join(" ",obj)
).collect(Collectors.toCollection(ArrayList::new));
for (int i = 0; i < res.size(); i++) {
ans[i]=res.get(i);
}
for (int i = 0; i < numSet.size(); i++) {
ans[i+res.size()]=numSet.get(i);
}
return ans;
}
boolean judgeNum(String string){
var list=string.split(" ",2);
return Character.isDigit(list[1].charAt(0));
}
}
970. Powerful Integers
思路 数学遍历
对于每一种 x i x^i xi对于 y i , i ∈ 1.. n y^i,i\in{1..n} yi,i∈1..n且 b o u n d − x i > = 0 bound-x^i>=0 bound−xi>=0。进行遍历即可,要注意1的n此幂仍是1,需要特殊处理。
代码
vector<int> powerfulIntegers(int x, int y, int bound) {
vector<int> ans;
unordered_set<int> set1;
if (x == 1 || y == 1) {
if (x == 1 && y == 1) {
if (bound >= 2) {
ans.push_back(2);
}
return ans;
}
int i =1;
int j = x == 1 ? y : x;
for (; i <= bound - 1; i *= j) {
if (!set1.count(1 + i)) {
ans.push_back(1 + i);
set1.insert(1 + i);
}
}
return ans;
}
int x_base = 1;
while (x_base < bound) {
int residual = bound - x_base;
for (int i = 1; i <= residual; i *= y) {
if (!set1.count(i + x_base)) {
ans.push_back(i + x_base);
set1.insert(i + x_base);
}
}
x_base *= x;
}
return ans;
}
1006. Clumsy Factorial
思路
首先求出一共有多少次完整加减乘除循环:
c
i
r
c
l
e
s
=
n
/
4
circles=n/4
circles=n/4。对于完整循环,对每个元素按照顺序逐个计算即可。唯有要注意的是对于第一次循环和后面的循环有所不同。
对于剩下的数字residual
,分情况讨论即可。
代码
public int clumsy(int n) {
int circles = n / 4;
int residual = n % 4;
int ans = 0;
for (int i = 0; i < circles; i++) {
if (i == 0) {
ans += n * (n - 1) / (n - 2) + n - 3;
} else {
ans -= n * (n - 1) / (n - 2) - n + 3;
}
n -= 4;
}
if (circles == 0) {
if (n == 1) {
ans = 1;
}
if (n == 2) {
ans = 2;
}
if (n == 3) {
ans = 6;
}
} else {
if (n == 1) {
ans -= 1;
}
if (n == 2) {
ans -= 2;
}
if (n == 3) {
ans -= 6;
}
}
return ans;
}
1016. Binary String With Substrings Representing 1 To N
思路
因为更长的字串会覆盖更短的字串,所以我们直接尝试遍历匹配最长的字串即可
代码
public boolean queryString(String s, int n) {
int base=1;
while (base*2*2<n){
base*=2;
}
for (int i = base; i <= n; i++) {
String str=Integer.toBinaryString(i);
if (!s.contains(str)){
return false;
}
}
return true;
}
1267. Count Servers that Communicate
思路
统计每一行列计算机的数量,然后再对grid的每一个元素进行遍历,若grid[i][j]为计算机且其所在行列数量大于1则结果+1;
代码
public int countServers(int[][] grid) {
int[] rowServers=new int[grid.length];
int[] columnServers=new int[grid[0].length];
int ans=0;
for (int i = 0; i < rowServers.length; i++) {
int rowSum=0;
for (int j = 0; j < columnServers.length; j++) {
rowSum+=grid[i][j];
}
rowServers[i]=rowSum;
}
for (int j = 0; j < columnServers.length; j++) {
int columnSum=0;
for (int i = 0; i <rowServers.length ; i++) {
columnSum+=grid[i][j];
}
columnServers[j]=columnSum;
}
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j <grid[0].length ; j++) {
if (grid[i][j]==1&&(rowServers[i]>1||columnServers[j]>1)){
ans++;
}
}
}
return ans;
}
1333. Filter Restaurants by Vegan-Friendly, Price and Distance
思路 模拟
按照要求对结果进行过滤排序即可
代码
public List<Integer> filterRestaurants(int[][] restaurants, int veganFriendly, int maxPrice, int maxDistance) {
return Arrays.stream(restaurants).filter(i->i[3]<=maxPrice).filter(i->i[4]<=maxDistance).filter( i-> veganFriendly == 0 || i[2] == 1
).sorted((a,b)->{
if (a[1]!=b[1]){
return b[1]-a[1];
}
else{
return b[0]-a[0];
}
}).map(arr->arr[0]).toList();
}
1921. Eliminate Maximum Number of Monsters
思路 模拟
使用
d
i
s
t
i
/
s
p
e
e
d
i
dist_i/speed_i
disti/speedi即可求出每个怪物的到达时间
t
i
m
e
i
time_i
timei。对time
进行排序。只要任意时刻来的怪兽数量大于时间即返回结果,若没有则说明可以全部消灭。
代码
public int eliminateMaximum(int[] dist, int[] speed) {
double[] time=new double[dist.length];
for (int i = 0; i < dist.length; i++) {
time[i]=(double) dist[i]/speed[i];
}
Arrays.sort(time);
for (int i=0;i<dist.length;i++){
if (time[i]-i<=EPSILON){
return i;
}
}
return dist.length;
}
2240. Number of Ways to Buy Pens and Pencils
思路 模拟
对于每一个可能的cost1商品的数量(total-cnt_1*cost1>=0),计算cost2的商品一共有几种可能方案,求和即可。
代码
public long waysToBuyPensPencils(int total, int cost1, int cost2) {
long ans=0;
long cnt_1=0;
while (total-cnt_1*cost1>=0){
long residual=total-cnt_1*cost1;
ans+=residual/cost2+1;
cnt_1++;
}
return ans;
}
2512. Reward Top K Students
思路 HashMap+根据条件统计积分排序
我们可以将单词对应的得分存到哈希表 scoreMap
中。
然后遍历 report
,对于每个学生,我们将其得分存入数组 sorting
中,数组中的每个元素为一个二元组
(
t
,
s
i
d
)
(t,sid)
(t,sid),其中 t
表示学生的得分,而 sid
表示学生的 ID。
最后我们对数组sorting
按照得分从高到低排序,如果得分相同则按照 ID 从小到大排序,然后取出前 k
个学生的 ID 即可。
代码
public List<Integer> topStudents(String[] positive_feedback, String[] negative_feedback, String[] report, int[] student_id, int k) {
HashMap<String,Integer> scoreMap=new HashMap<>();
for (String str:positive_feedback){
scoreMap.put(str,3);
}
for (String str:negative_feedback){
scoreMap.put(str,-1);
}
int[][] sorting=new int[report.length][2];
for (int i = 0; i < sorting.length; i++) {
for (String seg:report[i].split(" ")) {
sorting[i][0]+=scoreMap.getOrDefault(seg,0);
}
sorting[i][1]=student_id[i];
}
Arrays.sort(sorting,((o1, o2) -> {
if (o1[0]!=o2[0]){
return o2[0]-o1[0];
}
return o1[1]-o2[1];
}));
List<Integer> ans=new ArrayList<>();
for (int i = 0; i < k; i++) {
ans.add(sorting[i][1]);
}
return ans;
}
*2731. Movement of Robots
思路
两个机器人相撞后,它们会立即改变方向,实际上相当于两个机器人继续往原来的方向移动。因此,我们遍历数组 nums
,按照字符串 s
的指令,将每个机器人的位置加上或减去 d
,然后对数组 nums
进行排序。
接下来,我们从小到大枚举每个机器人的位置,计算出当前机器人与前面所有机器人的距离之和,即为答案。
代码
public int sumDistance(int[] nums, String s, int d) {
int n = nums.length;
long[] arr = new long[n];
for (int i = 0; i < n; ++i) {
arr[i] = (long) nums[i] + (s.charAt(i) == 'L' ? -d : d);
}
Arrays.sort(arr);
long ans = 0, sum = 0;
final int mod = (int) 1e9 + 7;
for (int i = 0; i < n; ++i) {
ans = (ans + i * arr[i] - sum) % mod;
sum += arr[i];
}
return (int) ans;
}
2840. Check if Strings Can be Made Equal With Operations II
思路
题目提到:
可以对两个字符串中的 任意一个 执行以下操作 任意 次:
选择两个下标 i 和 j ,满足 i < j 且 j - i 是 偶数,然后 交换 这个字符串中两个下标对应的字符。
也就是说 奇数位置和奇数位置之间的字符的位置可以任意互换,偶数位置和偶数位置之间的字符可以任意互换,因为他们满足j-i是偶数这一条件。因此,直接分别统计技术位和偶数位的各个字符的出现次数并比较两字符串的出现次数是否相同即可。
代码
class Solution {
public boolean checkStrings(String s1, String s2) {
int[] singleCnt=new int[26];
int[] evenCnt=new int[26];
for (int i=0;i<s1.length();i+=2){
singleCnt[s1.charAt(i)-'a']++;
}
for (int i=1;i<s1.length();i+=2){
evenCnt[s1.charAt(i)-'a']++;
}
for (int i=0;i<s2.length();i+=2){
singleCnt[s2.charAt(i)-'a']--;
if (singleCnt[s2.charAt(i)-'a']<0){
return false;
}
}
for (int i=1;i<s2.length();i+=2){
evenCnt[s2.charAt(i)-'a']--;
if (evenCnt[s2.charAt(i)-'a']<0){
return false;
}
}
return true;
}
}