Talentd
FleetCode
Make and follow sheets.Sign in

Queries on a Permutation With Key solution and explanation

MediumArrayBinary Indexed TreeSimulation8 min read84.9% acceptanceUpdated 9 Apr 2026
花花酱 LeetCode 1409. Queries on a Permutation With Key - 刷题找工作 EP319Hua Hua · 18:00 · 1K viewsWatch on YouTube
9 more videos on this problem
  1. 花花酱 LeetCode 1409. Queries on a Permutation With Key - 刷题找工作 EP319Hua Hua · 18:00 · 1K views
  2. Queries on a Permutation With Key (Leetcode 1409)Coding Interviews · 10:26 · 1K views
  3. 1409. Queries on a Permutation With Key/ Explanation and C++ codeCoding with U · 8:58 · 316 views
  4. LeetCode 1409 : C++ Solution | Queries on a Permutation With KeyAmit Manjarly · 13:22 · 302 views
  5. LeetCode 1409. Queries on a Permutation With Key Solution Explained - JavaAlgorithms and Data Structures Course · 10:58 · 286 views
  6. LeetCode 1409 : JAVA Solution | Queries on a Permutation With KeyAmit Manjarly · 16:07 · 261 views
  7. 1409. Queries on a Permutation With KeyMuerde un zombie · 9:58 · 138 views
  8. Leetcode 1409. Queries on a Permutation With KeyInterview Block · 45:24 · 99 views
  9. 1409 Queries on a Permutation With KeyMath Geeks · 3:11 · 96 views
  10. Queries on a Permutation With Key | Leetcode 1409 | Hindi | With Code in C++Core Concepts · 16:50 · 51 views

The problem

Given the array queries of positive integers between 1 and m, you have to process all queries[i] (from i=0 to i=queries.length-1) according to the following rules:

  • In the beginning, you have the permutation P=[1,2,3,...,m].
  • For the current i, find the position of queries[i] in the permutation P (indexing from 0) and then move this at the beginning of the permutation P. Notice that the position of queries[i] in P is the result for queries[i].

Return an array containing the result for the given queries.

Example 1:

Input: queries = [3,1,2,1], m = 5
Output: [2,1,2,1] 
Explanation: The queries are processed as follow: 
For i=0: queries[i]=3, P=[1,2,3,4,5], position of 3 in P is 2, then we move 3 to the beginning of P resulting in P=[3,1,2,4,5]. 
For i=1: queries[i]=1, P=[3,1,2,4,5], position of 1 in P is 1, then we move 1 to the beginning of P resulting in P=[1,3,2,4,5]. 
For i=2: queries[i]=2, P=[1,3,2,4,5], position of 2 in P is 2, then we move 2 to the beginning of P resulting in P=[2,1,3,4,5]. 
For i=3: queries[i]=1, P=[2,1,3,4,5], position of 1 in P is 1, then we move 1 to the beginning of P resulting in P=[1,2,3,4,5]. 
Therefore, the array containing the result is [2,1,2,1].  

Example 2:

Input: queries = [4,1,2,2], m = 4
Output: [3,1,2,0]

Example 3:

Input: queries = [7,5,5,8,3], m = 8
Output: [6,5,0,7,5]

Constraints:

  • 1 <= m <= 10^3
  • 1 <= queries.length <= m
  • 1 <= queries[i] <= m

Approach overview

Problem Overview: You start with a permutation P = [1, 2, ..., m]. For each value in the queries array, find its current index in the permutation, record that index, then move the value to the front of the permutation. The permutation keeps changing after every query.

Approach 1: Naive Linear Search Simulation (Time: O(m * q), Space: O(m))

The direct approach simulates the permutation operations exactly as described. Store the permutation in an array and for each query perform a linear scan to find the position of the queried value. After locating it, append the index to the result and move that element to the front by shifting the elements before it one position to the right. This approach relies purely on array manipulation and works well when m and q are small. It clearly models the process but becomes inefficient because each lookup costs O(m) and you repeat it for every query.

This solution mainly uses simple array operations and basic simulation. Despite the higher time complexity, it is often the fastest way to implement the idea during interviews before optimizing.

Approach 2: Binary Indexed Tree for Efficient Index Management (Time: O(q log(m + q)), Space: O(m + q))

The expensive step in the naive solution is repeatedly finding the index of a number after elements shift. A better strategy stores element positions and uses a Binary Indexed Tree (Fenwick Tree) to maintain how many elements exist before a position. Initially place all numbers in positions offset by q so the front area is empty for future moves.

For each query, look up the stored position of the value and use the tree to compute how many active elements appear before it. That count is exactly the index in the permutation. After recording the result, update the tree to remove the element from its old position and reinsert it at the next available front position. Each update and prefix sum takes O(log n), which keeps the whole process efficient even for large inputs.

This approach relies on a Binary Indexed Tree to maintain dynamic prefix counts while elements move. The key insight is separating logical order (computed with prefix sums) from physical storage of elements.

Recommended for interviews: Start with the simulation approach to demonstrate understanding of the permutation updates. Then optimize using a Binary Indexed Tree. Interviewers typically expect the Fenwick Tree solution because it reduces repeated index searches and handles dynamic position updates efficiently.

Approach 1: Naive Linear Search Approach

This approach utilizes a simple linear scan to locate each query item within the permutation array. Post discovery, this item is repositioned to the start of the array. Specifically, we iterate over the 'queries' array, locate 'queries[i]' within 'P', log the index, then elevate the item to the permutation's forefront by erasing it from its original position and reinserting it at the beginning. Despite its straightforwardness, this methodology can be suboptimal, especially for substantial 'm', given its O(n * m) time complexity.

def process_queries(queries, m):
    P = list(range(1, m + 1))
    result = []
    for q in queries:
        index = P.index(q)
        result.append(index)
        P.pop(index)
        P.insert(0, q)
    return result

queries = [3, 1, 2, 1]
m = 5
result = process_queries(queries, m)
print(result)
import java.util.ArrayList;
import java.util.List;

public class QueriesProcessor {
    public static List<Integer> processQueries(int[] queries, int m) {
        List<Integer> P = new ArrayList<>();
        List<Integer> result = new ArrayList<>();
        for (int i = 1; i <= m; i++) P.add(i);
        for (int q : queries) {
            int index = P.indexOf(q);
            result.add(index);
            P.remove(index);
            P.add(0, q);
        }
        return result;
    }

    public static void main(String[] args) {
        int[] queries = {3, 1, 2, 1};
        int m = 5;
        List<Integer> result = processQueries(queries, m);
        for (int r : result) {
            System.out.print(r + " ");
        }
    }
}
#include <iostream>
#include <vector>

std::vector<int> processQueries(std::vector<int>& queries, int m) {
    std::vector<int> P(m);
    std::vector<int> result;
    for(int i = 0; i < m; ++i) P[i] = i + 1;
    for(auto q : queries) {
        auto it = std::find(P.begin(), P.end(), q);
        int index = std::distance(P.begin(), it);
        result.push_back(index);
        P.erase(it);
        P.insert(P.begin(), q);
    }
    return result;
}

int main() {
    std::vector<int> queries = {3, 1, 2, 1};
    int m = 5;
    std::vector<int> result = processQueries(queries, m);
    for(int r : result) {
        std::cout << r << " ";
    }
    return 0;
}
function processQueries(queries, m) {
    let P = Array.from({length: m}, (_, i) => i + 1);
    let result = [];
    queries.forEach(q => {
        let index = P.indexOf(q);
        result.push(index);
        P.splice(index, 1);
        P.unshift(q);
    });
    return result;
}

let queries = [3, 1, 2, 1];
let m = 5;
let result = processQueries(queries, m);
console.log(result);
#include <stdio.h>

void processQueries(int* queries, int queriesSize, int m, int* returnSize, int* result) {
    int P[m];
    for(int i = 0; i < m; i++) {
        P[i] = i + 1;
    }
    for(int i = 0; i < queriesSize; i++) {
        int index = 0;
        while(P[index] != queries[i]) index++;
        result[i] = index;
        int value = P[index];
        for(int j = index; j > 0; j--) {
            P[j] = P[j - 1];
        }
        P[0] = value;
    }
    *returnSize = queriesSize;
}

int main() {
    int queries[] = {3, 1, 2, 1};
    int m = 5;
    int result[4];
    int returnSize;
    processQueries(queries, 4, m, &returnSize, result);
    for(int i = 0; i < returnSize; i++) {
        printf("%d ", result[i]);
    }
    return 0;
}
using System;
using System.Collections.Generic;

public class Solution {
    public static List<int> ProcessQueries(int[] queries, int m) {
        List<int> P = new List<int>();
        List<int> result = new List<int>();
        for (int i = 1; i <= m; i++) P.Add(i);
        foreach (int q in queries) {
            int index = P.IndexOf(q);
            result.Add(index);
            P.RemoveAt(index);
            P.Insert(0, q);
        }
        return result;
    }

    public static void Main() {
        int[] queries = {3, 1, 2, 1};
        int m = 5;
        List<int> result = ProcessQueries(queries, m);
        foreach (int r in result) {
            Console.Write(r + " ");
        }
    }
}

Python lists natively support index discovery via 'index', fostering direct insertion, deletion, and modification of elements, enabling efficient management of the permutation and queries operations.

Complexity

Time complexity: O(n * m), Space complexity: O(m)

Try this approach in the editor

Approach 2: Using Binary Indexed Tree for Efficient Index Management

This advanced approach employs a Binary Indexed Tree (BIT) for enhanced efficiency, minimizing the time required to reposition each queried element. The idea involves leveraging the BIT to keep track of current positions and perform range sum queries efficiently. This reduces the need for repeated O(n) operations encountered in naive searching strategies.

class FenwickTree:
    def __init__(self, size):
        self.size = size
        self.tree = [0] * (size + 1)

    def update(self, idx, delta):
        while idx <= self.size:
            self.tree[idx] += delta
            idx += idx & -idx

    def query(self, idx):
        sum_ = 0
        while idx > 0:
            sum_ += self.tree[idx]
            idx -= idx & -idx
        return sum_


def process_queries(queries, m):
    max_size = m + len(queries)
    bit = FenwickTree(max_size)
    position = {i + 1: i + len(queries) for i in range(m)}
    result = []
    for i, q in enumerate(queries):
        current_pos = position[q]
        res = bit.query(current_pos) - bit.query(i)
        result.append(res)
        # Update the position in BIT and to new index i
        bit.update(current_pos + 1, -1)
        position[q] = i
        bit.update(i + 1, 1)
    return result

queries = [3, 1, 2, 1]
m = 5
result = process_queries(queries, m)
print(result)

In this Python solution, a Fenwick Tree (Binary Indexed Tree) is employed to efficiently manage indices and cumulative sums. It allows fast updates and queries on prefix sums, enabling us to calculate the needed offset for answers effectively and manage the permutations without frequent array manipulation.

Complexity

Time complexity: O((n + m) * log(n + m)), Space complexity: O(n + m)

Try this approach in the editor

Approach 3: Simulation

The problem's data scale is not large, so we can directly simulate it.

func processQueries(queries []int, m int) []int {
	p := make([]int, m)
	for i := range p {
		p[i] = i + 1
	}
	ans := []int{}
	for _, v := range queries {
		j := 0
		for i := range p {
			if p[i] == v {
				j = i
				break
			}
		}
		ans = append(ans, j)
		p = append(p[:j], p[j+1:]...)
		p = append([]int{v}, p...)
	}
	return ans
}
Try this approach in the editor

Approach 4: Binary Indexed Tree

The Binary Indexed Tree (BIT), also known as the Fenwick Tree, efficiently supports the following two operations:

  1. Point Update update(x, delta): Adds a value delta to the element at position x in the sequence.
  2. Prefix Sum Query query(x): Queries the sum of the sequence over the interval [1,...,x], i.e., the prefix sum at position x.

Both operations have a time complexity of O(log n).

The fundamental functionality of the Binary Indexed Tree is to count the number of elements smaller than a given element x. This comparison is abstract and can refer to size, coordinate, mass, etc.

For example, given the array a[5] = {2, 5, 3, 4, 1}, the task is to compute b[i] = the number of elements to the left of position i that are less than or equal to a[i]. For this example, b[5] = {0, 1, 1, 2, 0}.

The solution is to traverse the array, first calculating query(a[i]) for each position, and then updating the Binary Indexed Tree with update(a[i], 1). When the range of numbers is large, discretization is necessary, which involves removing duplicates, sorting, and then assigning an index to each number.

type BinaryIndexedTree struct {
	n int
	c []int
}

func newBinaryIndexedTree(n int) *BinaryIndexedTree {
	c := make([]int, n+1)
	return &BinaryIndexedTree{n, c}
}

func (this *BinaryIndexedTree) lowbit(x int) int {
	return x & -x
}

func (this *BinaryIndexedTree) update(x, delta int) {
	for x <= this.n {
		this.c[x] += delta
		x += this.lowbit(x)
	}
}

func (this *BinaryIndexedTree) query(x int) int {
	s := 0
	for x > 0 {
		s += this.c[x]
		x -= this.lowbit(x)
	}
	return s
}

func processQueries(queries []int, m int) []int {
	n := len(queries)
	pos := make([]int, m+1)
	tree := newBinaryIndexedTree(m + n)
	for i := 1; i <= m; i++ {
		pos[i] = n + i
		tree.update(n+i, 1)
	}
	ans := []int{}
	for i, v := range queries {
		j := pos[v]
		tree.update(j, -1)
		ans = append(ans, tree.query(j))
		pos[v] = n - i
		tree.update(n-i, 1)
	}
	return ans
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Naive Linear Search Approach

Time complexity: O(n * m), Space complexity: O(m)

Using Binary Indexed Tree for Efficient Index Management

Time complexity: O((n + m) * log(n + m)), Space complexity: O(n + m)

Simulation–
Binary Indexed Tree–

Complexity analysis

ApproachTimeSpaceWhen to Use
Naive Linear Search SimulationO(m * q)O(m)Small constraints or when quickly prototyping the logic of the permutation operations
Binary Indexed Tree (Fenwick Tree)O(q log(m + q))O(m + q)Large inputs where repeated index lookups must be optimized

Frequently asked questions

Is Queries on a Permutation With Key easy or hard?

LeetCode classifies this problem as Medium. The simulation idea is straightforward, but recognizing that repeated index lookups can be optimized with a Binary Indexed Tree requires familiarity with advanced data structures.

Queries on a Permutation With Key Python/Java solution

The problem can be implemented in Python, Java, C++, JavaScript, and other languages. The naive version uses an array with linear search and element shifting, while the optimized Python solution often uses a Fenwick Tree to compute indices and update positions efficiently.

How to solve Queries on a Permutation With Key in O(n)?

A strict O(n) solution is not typical because elements continuously change positions. The closest efficient approach uses a Binary Indexed Tree or Fenwick Tree where each query performs prefix sum and update operations in O(log n), giving an overall complexity of O(q log(m + q)).

What is the best approach for Queries on a Permutation With Key?

The most efficient approach uses a Binary Indexed Tree (Fenwick Tree) to track active positions and compute prefix counts. Each query calculates the current index using a prefix sum and then updates the element's position to the front. This reduces repeated searches and achieves O(q log(m + q)) time complexity.

Is Queries on a Permutation With Key asked at Google/Amazon/Meta?

Permutation manipulation and Fenwick Tree problems appear frequently in interviews at companies like Google, Amazon, and Meta. This problem specifically tests simulation skills and knowledge of data structures that support dynamic prefix queries.

What data structure is used in Queries on a Permutation With Key?

The optimal solution uses a Binary Indexed Tree (Fenwick Tree) to maintain prefix counts of active elements. A simple array is sufficient for the brute-force simulation approach, but the Fenwick Tree enables efficient index queries and updates.

What is the time complexity of Queries on a Permutation With Key?

The naive simulation approach runs in O(m * q) time because each query requires a linear search in the permutation. The optimized Binary Indexed Tree approach improves this to O(q log(m + q)) by performing prefix sum and update operations in logarithmic time.

Comments

Now write it yourselfClose this page and solve Queries on a Permutation With Key in the editor, without looking.
Practise Queries on a Permutation With Key