算法leetcode1409. 查询带键的排列(多语言实现)

Posted 二当家的白帽子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法leetcode1409. 查询带键的排列(多语言实现)相关的知识,希望对你有一定的参考价值。


文章目录


1409. 查询带键的排列:

给你一个待查数组 queries ,数组中的元素为 1m 之间的正整数。 请你根据以下规则处理所有待查项 queries[i](从 i=0i=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 0 3 10^3 103
  • 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://le-yi.blog.csdn.net/ 博客原创~


以上是关于算法leetcode1409. 查询带键的排列(多语言实现)的主要内容,如果未能解决你的问题,请参考以下文章

K:leetcode 5381.查询带键的排列 这题简单,但我还能优化。精益求精,才是算法的乐趣所在!

leetcode-184周赛-5381-查询带键的排列

20.4.12 周赛 查询带键的排列 中等

Python中是不是有带键的同步锁?

带键的 ReactJS 动态子项

对象作为 React 子对象无效(找到:带键的对象...)