【发布时间】:2020-06-26 09:19:00
【问题描述】:
在其他语言中,存在具有二叉搜索树语义的内置数据结构,例如 C++ 的std::map。
请注意,我使用 C++ 作为示例,但并不是因为我试图在 C++ 本身和 Python 之间进行直接比较。我在这个问题上获得了“基于意见”的密切投票,但没有关于这个问题的“基于意见”。
通过二叉搜索树语义,我的意思是:
- 我可以在亚线性时间内将元素插入到结构中,即 std::map 的 O(logn)
- 我可以在线性时间内以 排序顺序 遍历所有元素,即 std::map 的 O(n)
Python中有没有二叉搜索树语义的数据结构?
为了说明,下面的程序可以用内置的 Python 数据结构高效地编写吗?
假设我是一家银行的基金经理。银行账户经常被添加到我的基金中。银行账户有一个 ID 和一个值。较小的 ID 意味着银行帐户的创建时间早于具有较大 ID 的帐户。每次将帐户添加到我的基金时,我都想知道我的基金中第 1000 个最早的帐户的 ID 号,以供记录。
以下是 Python 3 和 C++ 中的实现:
from random import randint
dist = lambda: randint(0, 2147483647)
def main():
my_map = {}
# fills a map with 1000000 random mappings of type (<int>: <int>)
for i in range(0, 1000000):
my_map[dist()] = dist()
# prints out the 1000th smallest key and its value
target_key = nth_smallest_key(1000, my_map)
print("({}: {})".format(target_key, my_map[target_key]))
# writes a new random mapping to the map
# then prints out the 1000th smallest key and its value if that key
# has changed
# 100000 times
for i in range(100000):
my_map[dist()] = dist()
test_key = nth_smallest_key(1000, my_map)
if target_key != test_key:
target_key = test_key
print("({}: {})".format(target_key, my_map[target_key]))
# print an indicator every 100 iterations for comparison
if i % 100 == 0:
print("iteration: {}".format(i))
def nth_smallest_key(n, m):
return sorted(m.keys())[n]
if __name__ == "__main__":
main()
#include <cstdio>
#include <map>
#include <random>
using namespace std;
int nth_smallest_key(int n, map<int, int>& m);
int main() {
random_device rd;
mt19937 psrng(rd());
uniform_int_distribution<> dist(0, 2147483647);
map<int, int> my_map;
// fills a map with 1000000 random mappings of type (<int>: <int>)
for (int i = 0; i < 1000000; i++) {
my_map[dist(psrng)] = dist(psrng);
}
// prints out the 1000th smallest key and its value
int target_key = nth_smallest_key(1000, my_map);
printf("(%d: %d)\n", target_key, my_map[target_key]);
// writes a new random mapping to the map
// then prints out the 1000th smallest key and its value if that key
// has changed
// 100000 times
for (int i = 0; i <= 100000; i++) {
my_map[dist(psrng)] = dist(psrng);
int test_key = nth_smallest_key(1000, my_map);
if (target_key != test_key) {
target_key = test_key;
printf("(%d: %d)\n", target_key, my_map[target_key]);
}
}
return 0;
}
int nth_smallest_key(int n, map<int, int>& m) {
map<int, int>::iterator curr = m.begin();
for (int i = 0; i < n; i++) {
curr = next(curr);
}
return curr->first;
}
生成文件:
buildcpp:
g++ -o main bst_semantics_cpp.cpp
runcpp: buildcpp
./main
runpython:
python3 bst_semantics_python.py
在 C++ 中,这个程序在我的机器上运行大约需要 5 秒
$ time ./main
(2211193: 2021141747)
(2208771: 1079444337)
(2208700: 1187119077)
(2208378: 1447743503)
...
(1996019: 1378401665)
(1989217: 1457497754)
(1989042: 1336229409)
real 0m4.915s
user 0m4.750s
sys 0m0.094s
$
但是,在 Python 中,程序在 120 秒后还没有达到 1% 的完成度
$ time make runpython
python3 bst_semantics_python.py
(2158070: 1498305903)
iteration: 0
iteration: 100
iteration: 200
^CTraceback (most recent call last):
File "bst_semantics_python.py", line 36, in <module>
main()
File "bst_semantics_python.py", line 23, in main
test_key = nth_smallest_key(1000, my_map)
File "bst_semantics_python.py", line 33, in nth_smallest_key
return sorted(m.keys())[n]
KeyboardInterrupt
Makefile:8: recipe for target 'runpython' failed
make: *** [runpython] Error 1
real 2m2.040s
user 1m59.063s
sys 0m0.375s
$
要高效地编写这个程序,你需要一个具有二叉搜索树语义的数据结构。
Python中有这样的数据结构吗?你能用内置的 Python 数据结构高效地编写这个程序吗?
【问题讨论】:
-
我的猜测是 C++ 键是排序的,所以你总是很快得到最小的。这是真的吗?
-
std::map 实现为二叉搜索树,可以按排序顺序遍历
-
有些库提供排序字典,例如:grantjenks.com/docs/sortedcontainers
-
部分问题是
smallest_key是您正在对整个数组进行排序以获取最小的元素,而您可以使用min(my_map) -
sorted调用强制 Python 遍历未排序键的列表并对其进行排序。一个更快的实现应该是min(my_map.keys())
标签: python data-structures binary-search-tree