这次最后一题时间超限,没有拿全分数。但就算90也到第五了哈哈。
文章目录
1. 查找点在自然区间的坐标
1.1 题目描述
-
定义
实数轴上的一个区间由左右两个端点,假设区间是左闭右开的,例如区间[0,1)
给定一个有序的不重合非负整数区间列表range_list
:[ [0,1), [3,4) ]
该非负整数区间列表将实数轴分割成了这些区间列表
range_list_nature_ext
: [(-∞,0),[0,1),[1,3),[3,4),[4,+∞)]
我们称range_list_nature_ext
为由range_list
扩展的 自然区间 -
问题
写一个查找算法,对于给定非负整数区间列表range_list
,查找一个非负整数 p 落在了range_list_nature_ext
的那个区间,返回那个区间的在range_list_nature_ext
里的下标,我们称这个下标为非负整数 p 在rage_list
里的 自然坐标。
1.2 思路分析
我们需要转化题目意思,求自然坐标,转成求一个数在有序数组的位置。
首先将所有区间的左端点和右端点存入一个数组中,并对这个数组进行升序排序,得到有序数组 arr;
当 p 小于 arr 中第一个元素时,其所在的自然区间下标为 0;
当 p 大于或等于 arr 中最后一个元素时,其所在的自然区间下标为 arr 的长度;
在 arr 中查找第一个比 p 大的元素,该元素左边的自然区间下标为该元素的下标。
1.3 代码实现
class Solution {
solution(P, N, vector) {
var result;
let arr = [];
// 将所有区间的左端点和右端点存入一个数组中,并对这个数组进行升序排序,得到有序数组 arr
for(let i=0;i<N;i++){
arr.push(vector[i][0]);
arr.push(vector[i][1]);
}
arr.sort((a,b)=>{
return a-b;
})
// 当 p 小于 arr 中第一个元素时,其所在的自然区间下标为 0
if(P < arr[0]) return 0;
// 当 p 大于或等于 arr 中最后一个元素时,其所在的自然区间下标为 arr 的长度
if(P >= arr[arr.length-1]) return arr.length;
// 在 arr 中查找第一个比 p 大的元素,该元素左边的自然区间下标为该元素的下标
let i = 1;
for(i=1;i<arr.length;i++){
if(P>=arr[i-1] && P<arr[i] ){
return i;
}
}
return i;
}
}
2. 鬼画符门之大师兄恋爱
2.1 题目描述
鬼画符门,每年都会统计自己宗门鬼画符消耗的数量。 往年一直是大师兄管理。 但是大师兄谈恋爱了!!怎么能让这种事
耽误自己恋爱时间呢!! 鬼艺接手了!! 你能帮鬼艺写一个程序帮助她统计每年消耗数量最多的鬼画符吗?
2.2 思路分析
该题目的输入就是不同的颜色字符串,我们只需要用Map来存储每个鬼画符出现的次数,然后遍历Map,找出出现次数最多的鬼画符即可。
2.3 代码实现
class Solution {
solution(n, vector) {
let m = new Map();
for(let i=0;i<n;i++){
let str = vector[i];
if(m.has(str) == false){
m.set(str,1);
}else{
m.set(str,1+m.get(str))
}
}
let maxCount = 0;
let maxStr = "";
m.forEach((value,key)=>{
if(value > maxCount){
maxCount = value;
maxStr = key;
}
})
return maxStr;
}
}
3. 去除整数
3.1 题目描述
已知存在集合A包含n个整数,从1到n。 存在m个整数a。 在集合A中去除这m个整数的的倍数。 输出集合中包含的元素的个数。
3.2 思路分析
这道题目的思路是遍历所有要去除的整数的倍数,将其添加到一个集合中。最后,集合的大小就是集合中剩余的元素数量。具体来说,算法的步骤如下:
-
如果要去除的整数数量 m 等于 1,那么将 n 除以这个整数的值,向下取整,即可得到集合中剩余的元素数量。
-
创建一个空集合 set,遍历要去除的整数数组 arr,对于每个整数 x,从 x 开始,以 x 为步长遍历集合 A 中的元素,将每个倍数添加到集合 set 中。
-
集合中剩余的元素数量即为 n 减去集合 set 的大小。
3.3 代码实现
class Solution {
solution(n, m, arr) {
var result = 0;
// 如果要去除的整数数量 m 等于 1,那么直接计算集合中剩余的元素数量
if(m == 1){
n -= Math.floor(n/arr[0]); // 计算整数 arr[0] 的倍数个数
return n;
}
// 创建一个空集合 set,遍历要去除的整数数组 arr,将所有倍数添加到集合 set 中
var set = new Set();
for(let i=0;i<arr.length;i++){
let x = arr[i];
for(let j=x;j<=n;j+=x){
set.add(j);
}
}
// 集合中剩余的元素数量即为 n 减去集合 set 的大小
return n - set.size;
}
}
4. 括号上色
4.1 题目描述
小艺酱又得到了一堆括号。 括号是严格匹配的。 现在给括号进行上色。 上色有三个要求:
1、只有三种上色方案,不上色,上红色,上蓝色。
2、每对括号只有一个上色。
3、相邻的两个括号不能上相同的颜色,但是可以都不上色。
问括号上色有多少种方案?答案对1000000007取模。
4.2 思路分析
这里是我没全过的题目分析:暴力回溯法(时间超限:通过率60%)
正确的解法应该是动态规划,这里期待一下其他大佬的题解。
这道题目可以使用递归的方式求解,每次递归遍历到一个括号的时候,判断当前括号是否可以填色,如果可以填色就填色,否则直接跳过。
在递归的过程中,可以使用一个数组 path 来记录每个括号填的颜色,如果递归结束之后 path 数组记录的就是一种可行的方案,累加器 count 就加1即可。
4.3 代码实现(纯暴力)
class Solution {
solution(s) {
var count = 0;
let n = s.length;
let tag = new Map();
let st = [];
// 先找到每一个括号匹配的另外一个括号的位置
for (let i = 0; i < n; i++) {
if (s[i] == '(') {
st.push(i);
} else {
let leftIndex = st.pop();
tag.set(leftIndex, i);
tag.set(i, leftIndex);
}
}
let path = new Array(n).fill(0);
digui(path, 0)
function digui(path, index) {
if (index == n) {
count = (count + 1) % 1000000007;
return;
}
// 第一个括号可任意上色
if (index == 0) {
path[0] = 1; // 1 代表 红色
digui(path, index + 1);
path[0] = 2; // 2 代表 蓝色
digui(path, index + 1);
path[0] = 0; // 0 代表不上色
digui(path, index + 1);
return;
}
let other = tag.get(index);
// 如果是右括号,考虑另外一个括号的限制
if (index > other) {
// 如果左括号没填色
if (path[other] == 0) {
if (path[index - 1] != 1) {
path[index] = 1;
digui(path, index + 1);
path[index] = 0;
}
if (path[index - 1] != 2) {
path[index] = 2;
digui(path, index + 1);
path[index] = 0;
}
return;
} else {
digui(path, index + 1);
}
} else {
// 如果是左括号就不考虑右括号的限制
if (path[index - 1] != 1) {
path[index] = 1;
digui(path, index + 1);
path[index] = 0;
}
if (path[index - 1] != 2) {
path[index] = 2;
digui(path, index + 1);
path[index] = 0;
}
digui(path, index + 1);
}
}
return count;
}
}