1有多少小于当前数字的数字
给你一个数组 nums
,对于其中每个元素 nums[i]
,请你统计数组中比它小的所有数字的数目。
换而言之,对于每个 nums[i]
你必须计算出有效的 j
的数量,其中 j
满足 j != i
且 nums[j] < nums[i]
。
以数组形式返回答案。
示例 1:
输入:nums = [8,1,2,2,3] 输出:[4,0,1,1,3] 解释: 对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。 对于 nums[1]=1 不存在比它小的数字。 对于 nums[2]=2 存在一个比它小的数字:(1)。 对于 nums[3]=2 存在一个比它小的数字:(1)。 对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
示例 2:
输入:nums = [6,5,4,8] 输出:[2,1,0,3]
示例 3:
输入:nums = [7,7,7,7] 输出:[0,0,0,0]
提示:
2 <= nums.length <= 500
0 <= nums[i] <= 100
思路:
- 复制原始数组,以便进行排序,排序后得到一个有序数组。排序之后,其实每一个数值的下标就代表这前面有几个比它小的了
- 使用哈希表记录每个数字对应的较小数字的数量,即对于每个数字,记录有多少个数字比它小。
- 遍历原始数组,根据哈希表记录的较小数字数量来更新结果数组,即将原始数组中的每个数字替换为对应的较小数字数量。
- 返回更新后的结果数组。
但有一个情况,就是数值相同怎么办?
例如,数组:1 2 3 4 4 4 ,第一个数值4的下标是3,第二个数值4的下标是4了。
这里就需要一个技巧了,在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了
代码:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
// 复制原始数组以便排序
vector<int> sorted_nums = nums;
// 对复制的数组进行排序
sort(sorted_nums.begin(), sorted_nums.end());
// 哈希表,记录每个数字对应的较小数字的数量
int hash[101];
// 从大到小遍历排序后的数组,记录每个数字对应的较小数字的数量
for(int i = sorted_nums.size() - 1; i >= 0; i--){
hash[sorted_nums[i]] = i;
}
// 遍历原始数组,根据哈希表记录的较小数字数量来更新结果数组
for(int i = 0; i < nums.size(); i++){
sorted_nums[i] = hash[nums[i]];
}
// 返回结果数组
return sorted_nums;
}
};
2有效的山脉数组
给定一个整数数组 arr
,如果它是有效的山脉数组就返回 true
,否则返回 false
。
让我们回顾一下,如果 arr
满足下述条件,那么它是一个山脉数组:
arr.length >= 3
- 在
0 < i < arr.length - 1
条件下,存在i
使得:arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
示例 1:
输入:arr = [2,1] 输出:false
示例 2:
输入:arr = [3,5,5] 输出:false
示例 3:
输入:arr = [0,3,2,1] 输出:true
提示:
1 <= arr.length <= 104
0 <= arr[i] <= 104
思路:
判断是山峰,主要就是要严格的保证左边到中间,和右边到中间是递增的。
使用两个指针,left和right,让其按照一定规则向中间移动
- 如果数组长度小于3,不可能构成山脉,直接返回false。
- 初始化左右指针分别指向数组的两端。
- 向右移动左指针,直到不再递增,即找到山峰的左边界。
- 向左移动右指针,直到不再递减,即找到山峰的右边界。
- 如果左右指针相等,并且不在数组两端,则说明左右指针之间构成山脉,返回true;否则不构成山脉,返回false。
代码:
class Solution {
public:
bool validMountainArray(vector<int>& arr) {
// 如果数组长度小于3,不可能构成山脉
if(arr.size() < 3) return false;
// 左右指针初始化为数组的两端
int left = 0;
int right = arr.size() - 1;
// 向右移动左指针,直到不再递增
while(left < arr.size() - 1 && arr[left] < arr[left + 1]) left++;
// 向左移动右指针,直到不再递减
while(right > 0 && arr[right] < arr[right - 1]) right--;
// 如果左右指针相等,并且不在数组两端,则说明左右指针之间构成山脉
if (left == right && left != 0 && right != arr.size() - 1) return true;
// 否则不构成山脉
return false;
}
};
3独一无二的出现次数
给你一个整数数组 arr
,请你帮忙统计数组中每个数的出现次数。
如果每个数的出现次数都是独一无二的,就返回 true
;否则返回 false
。
示例 1:
输入:arr = [1,2,2,1,1,3] 输出:true 解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。
示例 2:
输入:arr = [1,2] 输出:false
示例 3:
输入:arr = [-3,0,1,-3,1,1,1,-3,10,0] 输出:true
思路:
- 创建一个哈希映射,用于存储每个数字出现的次数。
- 遍历给定的整数数组,对每个数字进行计数,并将结果存储在哈希映射中。
- 创建一个无序集合,用于存储出现次数的唯一值。
- 遍历哈希映射中的每个键值对,将每个数字出现的次数加入无序集合中,这样集合中就只会包含每个数字出现的唯一次数。
- 最后,比较无序集合的大小和哈希映射的大小,如果它们相等,则说明每个数字出现的次数都是唯一的,返回true;否则返回false。
代码:
class Solution {
public:
bool uniqueOccurrences(vector<int>& arr) {
// 创建一个哈希映射,用于存储每个数字出现的次数
map<int, int> my_map;
// 遍历数组,统计每个数字的出现次数
for(int num : arr) my_map[num]++;
// 创建一个无序集合,用于存储出现次数的唯一值
unordered_set<int> my_set;
// 遍历哈希映射中的每个键值对,将出现次数加入无序集合
for(auto num : my_map) my_set.insert(num.second);
// 如果无序集合的大小等于哈希映射的大小,说明每个数字出现的次数都是唯一的
return my_set.size() == my_map.size();
}
};
4移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
思路:
- 初始化一个慢指针
slowIndex
,用于记录非零元素应该被移动到的位置。 - 遍历整个数组,使用一个快指针
fastIndex
来找到非零元素,并将其移动到慢指针位置。 - 如果当前元素不为零,则将其移动到慢指针位置,并递增慢指针
slowIndex
。 - 最后,将慢指针之后的位置(即原数组中剩余的位置)赋值为零,确保所有非零元素已经移到了数组的前面,而剩余位置都为零。
代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// 初始化慢指针
int slowIndex = 0;
// 遍历数组,使用快指针来找到非零元素并将其移动到慢指针位置
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
// 如果当前元素不为零
if (nums[fastIndex] != 0) {
// 将非零元素移动到慢指针位置
nums[slowIndex++] = nums[fastIndex];
}
}
// 将慢指针之后的冗余元素赋值为0
for (int i = slowIndex; i < nums.size(); i++) {
nums[i] = 0;
}
}
};
5员工的直属部门
表:Employee
+---------------+---------+ | Column Name | Type | +---------------+---------+ | employee_id | int | | department_id | int | | primary_flag | varchar | +---------------+---------+ 这张表的主键为 employee_id, department_id (具有唯一值的列的组合) employee_id 是员工的ID department_id 是部门的ID,表示员工与该部门有关系 primary_flag 是一个枚举类型,值分别为('Y', 'N'). 如果值为'Y',表示该部门是员工的直属部门。 如果值是'N',则否
一个员工可以属于多个部门。当一个员工加入超过一个部门的时候,他需要决定哪个部门是他的直属部门。请注意,当员工只加入一个部门的时候,那这个部门将默认为他的直属部门,虽然表记录的值为'N'
.
请编写解决方案,查出员工所属的直属部门。
返回结果 没有顺序要求 。
返回结果格式如下例子所示:
示例 1:
输入: Employee table: +-------------+---------------+--------------+ | employee_id | department_id | primary_flag | +-------------+---------------+--------------+ | 1 | 1 | N | | 2 | 1 | Y | | 2 | 2 | N | | 3 | 3 | N | | 4 | 2 | N | | 4 | 3 | Y | | 4 | 4 | N | +-------------+---------------+--------------+ 输出: +-------------+---------------+ | employee_id | department_id | +-------------+---------------+ | 1 | 1 | | 2 | 1 | | 3 | 3 | | 4 | 3 | +-------------+---------------+ 解释: - 员工 1 的直属部门是 1 - 员工 2 的直属部门是 1 - 员工 3 的直属部门是 3 - 员工 4 的直属部门是 3
代码:
-- 查询只出现过一次的员工及其部门
(
select
employee_id, -- 员工ID
department_id -- 部门ID
from
Employee -- 员工表
group by
employee_id -- 按员工ID分组
having
count(*) = 1 -- 只出现过一次的员工
)
union
-- 查询主要标志为 'Y' 的员工及其部门
(
select
employee_id, -- 员工ID
department_id -- 部门ID
from
Employee -- 员工表
where
primary_flag = 'Y' -- 主要标志为 'Y'
)