跳转至

2102. 序列顺序查询

题目描述

一个观光景点由它的名字 name 和景点评分 score 组成,其中 name 是所有观光景点中 唯一 的字符串,score 是一个整数。景点按照最好到最坏排序。景点评分 越高 ,这个景点越好。如果有两个景点的评分一样,那么 字典序较小 的景点更好。

你需要搭建一个系统,查询景点的排名。初始时系统里没有任何景点。这个系统支持:

  • 添加 景点,每次添加 一个 景点。
  • 查询 已经添加景点中第 i  的景点,其中 i 是系统目前位置查询的次数(包括当前这一次)。
    • 比方说,如果系统正在进行第 4 次查询,那么需要返回所有已经添加景点中第 4 好的。

注意,测试数据保证 任意查询时刻 ,查询次数都 不超过 系统中景点的数目。

请你实现 SORTracker 类:

  • SORTracker() 初始化系统。
  • void add(string name, int score) 向系统中添加一个名为 name 评分为 score 的景点。
  • string get() 查询第 i 好的景点,其中 i 是目前系统查询的次数(包括当前这次查询)。

 

示例:

输入:
["SORTracker", "add", "add", "get", "add", "get", "add", "get", "add", "get", "add", "get", "get"]
[[], ["bradford", 2], ["branford", 3], [], ["alps", 2], [], ["orland", 2], [], ["orlando", 3], [], ["alpine", 2], [], []]
输出:
[null, null, null, "branford", null, "alps", null, "bradford", null, "bradford", null, "bradford", "orland"]

解释:
SORTracker tracker = new SORTracker(); // 初始化系统
tracker.add("bradford", 2); // 添加 name="bradford" 且 score=2 的景点。
tracker.add("branford", 3); // 添加 name="branford" 且 score=3 的景点。
tracker.get();              // 从好到坏的景点为:branford ,bradford 。
                            // 注意到 branford 比 bradford 好,因为它的 评分更高 (3 > 2) 。
                            // 这是第 1 次调用 get() ,所以返回最好的景点:"branford" 。
tracker.add("alps", 2);     // 添加 name="alps" 且 score=2 的景点。
tracker.get();              // 从好到坏的景点为:branford, alps, bradford 。
                            // 注意 alps 比 bradford 好,虽然它们评分相同,都为 2 。
                            // 这是因为 "alps" 字典序 比 "bradford" 小。
                            // 返回第 2 好的地点 "alps" ,因为当前为第 2 次调用 get() 。
tracker.add("orland", 2);   // 添加 name="orland" 且 score=2 的景点。
tracker.get();              // 从好到坏的景点为:branford, alps, bradford, orland 。
                            // 返回 "bradford" ,因为当前为第 3 次调用 get() 。
tracker.add("orlando", 3);  // 添加 name="orlando" 且 score=3 的景点。
tracker.get();              // 从好到坏的景点为:branford, orlando, alps, bradford, orland 。
                            // 返回 "bradford".
tracker.add("alpine", 2);   // 添加 name="alpine" 且 score=2 的景点。
tracker.get();              // 从好到坏的景点为:branford, orlando, alpine, alps, bradford, orland 。
                            // 返回 "bradford" 。
tracker.get();              // 从好到坏的景点为:branford, orlando, alpine, alps, bradford, orland 。
                            // 返回 "orland" 。

 

提示:

  • name 只包含小写英文字母,且每个景点名字互不相同。
  • 1 <= name.length <= 10
  • 1 <= score <= 105
  • 任意时刻,调用 get 的次数都不超过调用 add 的次数。
  • 总共 调用 add 和 get 不超过 4 * 104 

解法

方法一:有序集合

我们可以使用有序集合来存储景点,用一个变量 $i$ 来记录当前查询的次数,初始时 $i = -1$。

调用 add 方法时,我们将景点的评分取负数,这样就可以使用有序集合按照评分从大到小排序,如果评分相同,按照景点名字的字典序从小到大排序。

调用 get 方法时,我们将 $i$ 加一,然后返回有序集合中第 $i$ 个景点的名字。

每次操作的时间复杂度为 $O(\log n)$,其中 $n$ 为已添加的景点数。空间复杂度为 $O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from sortedcontainers import SortedList


class SORTracker:

    def __init__(self):
        self.sl = SortedList()
        self.i = -1

    def add(self, name: str, score: int) -> None:
        self.sl.add((-score, name))

    def get(self) -> str:
        self.i += 1
        return self.sl[self.i][1]


# Your SORTracker object will be instantiated and called as such:
# obj = SORTracker()
# obj.add(name,score)
# param_2 = obj.get()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;

template <class T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

class SORTracker {
public:
    SORTracker() {
    }

    void add(string name, int score) {
        st.insert({-score, name});
    }

    string get() {
        return st.find_by_order(++i)->second;
    }

private:
    ordered_set<pair<int, string>> st;
    int i = -1;
};

/**
 * Your SORTracker object will be instantiated and called as such:
 * SORTracker* obj = new SORTracker();
 * obj->add(name,score);
 * string param_2 = obj->get();
 */

方法二:双优先队列(大小根堆)

我们注意到,由于本题中的查询操作是按照严格递增的顺序进行的,因此我们可以使用类似于数据流中的中位数的方法,定义两个优先队列 goodbad,其中 good 是一个小根堆,存储当前最好的景点,bad 是一个大根堆,存储当前第 $i$ 好的景点。

每次调用 add 方法时,我们将景点的评分和名字加入 good 中,然后将 good 中的最差的景点加入 bad 中。

每次调用 get 方法时,我们将 bad 中的最好的景点加入 good 中,然后返回 good 中的最差的景点。

每次操作的时间复杂度为 $O(\log n)$,其中 $n$ 为已添加的景点数。空间复杂度为 $O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Node:
    def __init__(self, s: str):
        self.s = s

    def __lt__(self, other):
        return self.s > other.s


class SORTracker:

    def __init__(self):
        self.good = []
        self.bad = []

    def add(self, name: str, score: int) -> None:
        score, node = heappushpop(self.good, (score, Node(name)))
        heappush(self.bad, (-score, node.s))

    def get(self) -> str:
        score, name = heappop(self.bad)
        heappush(self.good, (-score, Node(name)))
        return self.good[0][1].s


# Your SORTracker object will be instantiated and called as such:
# obj = SORTracker()
# obj.add(name,score)
# param_2 = obj.get()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class SORTracker {
    private PriorityQueue<Map.Entry<Integer, String>> good = new PriorityQueue<>(
        (a, b)
            -> a.getKey().equals(b.getKey()) ? b.getValue().compareTo(a.getValue())
                                             : a.getKey() - b.getKey());
    private PriorityQueue<Map.Entry<Integer, String>> bad = new PriorityQueue<>(
        (a, b)
            -> a.getKey().equals(b.getKey()) ? a.getValue().compareTo(b.getValue())
                                             : b.getKey() - a.getKey());

    public SORTracker() {
    }

    public void add(String name, int score) {
        good.offer(Map.entry(score, name));
        bad.offer(good.poll());
    }

    public String get() {
        good.offer(bad.poll());
        return good.peek().getValue();
    }
}

/**
 * Your SORTracker object will be instantiated and called as such:
 * SORTracker obj = new SORTracker();
 * obj.add(name,score);
 * String param_2 = obj.get();
 */
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using pis = pair<int, string>;

class SORTracker {
public:
    SORTracker() {
    }

    void add(string name, int score) {
        good.push({-score, name});
        bad.push(good.top());
        good.pop();
    }

    string get() {
        good.push(bad.top());
        bad.pop();
        return good.top().second;
    }

private:
    priority_queue<pis, vector<pis>, less<pis>> good;
    priority_queue<pis, vector<pis>, greater<pis>> bad;
};

/**
 * Your SORTracker object will be instantiated and called as such:
 * SORTracker* obj = new SORTracker();
 * obj->add(name,score);
 * string param_2 = obj->get();
 */

评论