持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
1409. 查询带键的排列:
给你一个待查数组 queries ,数组中的元素为 1 到 m 之间的正整数。 请你根据以下规则处理所有待查项 queries[i](从 i=0 到 i=queries.length-1):
- 一开始,排列
P=[1,2,3,...,m]。 - 对于当前的
i,请你找出待查项queries[i]在排列P中的位置**(下标从 0 开始)**,然后将其从原位置移动到排列P的起始位置(即下标为 0 处)。注意,queries[i]在P中的位置就是queries[i]的查询结果。
请你以数组形式返回待查数组 queries 的查询结果。
样例 1:
输入:
queries = [3,1,2,1], m = 5
输出:
[2,1,2,1]
解释:
待查数组 queries 处理如下:
对于 i=0: queries[i]=3, P=[1,2,3,4,5], 3 在 P 中的位置是 2,接着我们把 3 移动到 P 的起始位置,得到 P=[3,1,2,4,5] 。
对于 i=1: queries[i]=1, P=[3,1,2,4,5], 1 在 P 中的位置是 1,接着我们把 1 移动到 P 的起始位置,得到 P=[1,3,2,4,5] 。
对于 i=2: queries[i]=2, P=[1,3,2,4,5], 2 在 P 中的位置是 2,接着我们把 2 移动到 P 的起始位置,得到 P=[2,1,3,4,5] 。
对于 i=3: queries[i]=1, P=[2,1,3,4,5], 1 在 P 中的位置是 1,接着我们把 1 移动到 P 的起始位置,得到 P=[1,2,3,4,5] 。
因此,返回的结果数组为 [2,1,2,1] 。
样例 2:
输入:
queries = [4,1,2,2], m = 4
输出:
[3,1,2,0]
提示:
- 1 <= m <=
- 1 <= queries.length <= m
- 1 <= queries[i] <= m
分析
-
面对这道算法题目,二当家的陷入了沉思。
-
按照题意去模拟,涉及到两个重要操作,一个是查找目标数字的位置,另一个是将目标数字移动到起始位置。
-
先来解决将目标数字移动到起始位置的问题。
-
直接使用动态数组,链表,双端队列等数据结构,可以简化操作,但是肯定会多花时间和空间,如果使用普通数组,在把查询数字放到起始位置时涉及到大量移动,太花时间。
-
事实上我们提前知道一共有多少次查询,也就知道有多少次移动,每次移动都会将一个数字移动到起始位置。
-
所以我们可以开始就在数组前面多申请一些空间,将开始的数字放在数组最后面,这样移动数字就可以用普通数组实现,用空间换时间。
_ _ _ _ 1 2 3 4 5 _ _ _ 3 1 2 _ 4 5 _ _ 1 3 _ 2 _ 4 5 _ 2 1 3 _ _ _ 4 5 1 2 _ 3 _ _ _ 4 5
-
再来看看查找目标数字的问题。
-
如果直接遍历,那就是O(n)的复杂度,已经不算慢了,但是如果想再提高点效率呢?
-
由于在查询几次后,排列是无序的,也不能用二分查找,还有什么其他办法呢?
-
我们提前知道数字的取值范围,所以我们可以使用一个数组来存储某个数字的当前位置,循环中动态维护,但是如前面所说,我们如果使用普通数组存储排列,在移动了数字之后,排列中就有一些空位,仅仅拿到数字的位置是不对的,还需要把这些空位排除掉。
-
计数可以使用线段树,但是有点大材小用了,我们还可以使用树状数组,统计某个位置前面有多少个数字。
题解
rust
impl Solution {
pub fn process_queries(queries: Vec<i32>, m: i32) -> Vec<i32> {
// 排列
let mut p = vec![0; m as usize + queries.len()];
(0..m).for_each(|i| { p[queries.len() + i as usize] = i + 1; });
queries.iter().enumerate().map(|(i, &v)| {
// 计数
let mut count = 0;
// 现在数字所在位置
let mut cur = queries.len() - i;
while p[cur] != v {
if p[cur] > 0 {
count += 1;
}
cur += 1;
}
// 原来的位置赋空
p[cur] = 0;
// 新的位置
cur = queries.len() - i - 1;
p[cur] = v;
// 返回
count
}).collect()
}
}
go
func processQueries(queries []int, m int) []int {
// 排列
p := make([]int, m+len(queries))
for i := 0; i < m; i++ {
p[len(queries)+i] = i + 1
}
ans := make([]int, len(queries))
for i, v := range queries {
// 查找位置
cur := len(queries) - i
for p[cur] != v {
if p[cur] > 0 {
ans[i]++
}
cur++
}
// 原来的位置赋空
p[cur] = 0
// 新的位置
cur = len(queries) - i - 1
p[cur] = queries[i]
}
return ans
}
typescript
function processQueries(queries: number[], m: number): number[] {
// 排列
let p = [];
for (let i = 1; i <= m; ++i) {
p.push(i);
}
let ans = [];
for (let i = 0; i < queries.length; ++i) {
const v = queries[i];
// 查找位置
const cur = p.indexOf(v);
ans.push(cur);
// 移动位置
p.splice(cur, 1);
p.unshift(v);
}
return ans;
};
c
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* processQueries(int* queries, int queriesSize, int m, int* returnSize){
// 排列
int p[m + queriesSize];
for (int i = 0; i < m; ++i) {
p[queriesSize + i] = i + 1;
}
int *ans = calloc(queriesSize, sizeof(int));
for (int i = 0; i < queriesSize; ++i) {
// 查找位置
int cur = queriesSize - i;
while (p[cur] != queries[i]) {
if (p[cur] > 0) {
++ans[i];
}
++cur;
}
// 原来的位置赋空
p[cur] = 0;
// 新的位置
cur = queriesSize - i - 1;
p[cur] = queries[i];
}
*returnSize = queriesSize;
return ans;
}
c++
class Solution {
public:
vector<int> processQueries(vector<int>& queries, int m) {
// 排列
int p[m + queries.size()];
for (int i = 0; i < m; ++i) {
p[queries.size() + i] = i + 1;
}
vector<int> ans;
for (int i = 0; i < queries.size(); ++i) {
// 查找位置
int cur = queries.size() - i;
int count = 0;
while (p[cur] != queries[i]) {
if (p[cur] > 0) {
++count;
}
++cur;
}
// 原来的位置赋空
p[cur] = 0;
// 新的位置
cur = queries.size() - i - 1;
p[cur] = queries[i];
ans.emplace_back(count);
}
return ans;
}
};
python
class Solution:
def processQueries(self, queries: List[int], m: int) -> List[int]:
# 排列
p = [i + 1 for i in range(m)]
# 结果
ans = []
for v in queries:
# 查找位置
cur = p.index(v)
ans.append(cur)
# 移动位置
p.pop(cur)
p.insert(0, v)
return ans
java
class Solution {
public int[] processQueries(int[] queries, int m) {
// 排列
int[] p = new int[m + queries.length];
for (int i = 0; i < m; ++i) {
p[queries.length + i] = i + 1;
}
int[] ans = new int[queries.length];
for (int i = 0; i < queries.length; ++i) {
// 查找位置
int cur = queries.length - i;
while (p[cur] != queries[i]) {
if (p[cur] > 0) {
++ans[i];
}
++cur;
}
// 原来的位置赋空
p[cur] = 0;
// 新的位置
cur = queries.length - i - 1;
p[cur] = queries[i];
}
return ans;
}
}
树状数组实现
class Solution {
public int[] processQueries(int[] queries, int m) {
// 维护数字的位置
int[] pos = new int[m + 1];
// 维护位置前面有多少数字
BIT bit = new BIT(m + queries.length);
for (int i = 1; i <= m; ++i) {
pos[i] = queries.length + i;
bit.update(queries.length + i, 1);
}
int[] ans = new int[queries.length];
for (int i = 0; i < queries.length; ++i) {
// 现在的位置
int cur = pos[queries[i]];
bit.update(cur, -1);
ans[i] = bit.query(cur);
// 新的位置
cur = queries.length - i;
pos[queries[i]] = cur;
bit.update(cur, 1);
}
return ans;
}
/**
* 树状数组
* 下标从1开始
*/
class BIT {
int[] a;
public BIT(int n) {
// 多用一个空间,来简化下标计算
this.a = new int[n + 1];
}
public int query(int i) {
int ret = 0;
while (i > 0) {
ret += a[i];
i -= Integer.lowestOneBit(i);
}
return ret;
}
public int update(int i, int v) {
while (i < a.length) {
a[i] += v;
i += Integer.lowestOneBit(i);
}
return i;
}
}
}
原题传送门:https://leetcode.cn/problems/queries-on-a-permutation-with-key/
非常感谢你阅读本文~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://juejin.cn/user/2771185768884824/posts 博客原创~