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

118 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情


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], 3P 中的位置是 2,接着我们把 3 移动到 P 的起始位置,得到 P=[3,1,2,4,5] 。
	对于 i=1: queries[i]=1, P=[3,1,2,4,5], 1P 中的位置是 1,接着我们把 1 移动到 P 的起始位置,得到 P=[1,3,2,4,5] 。 
	对于 i=2: queries[i]=2, P=[1,3,2,4,5], 2P 中的位置是 2,接着我们把 2 移动到 P 的起始位置,得到 P=[2,1,3,4,5] 。
	对于 i=3: queries[i]=1, P=[2,1,3,4,5], 1P 中的位置是 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 <= 10310^3
  • 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 博客原创~