因为常常看到有人询问Guardian/Knight的勋章怎么得,在力扣官方的帖子中,我们看到如下这句话:竞赛积分 ≥1600 的用户中,根据比例 5%、20%、75% 设定三档段位,段位每周比赛结束后计算一次。
也就是说,Guardian是在>=1600分的所有人中,排名前5%的,而Knight是排名前25%;小于1600的人是不参与这个勋章排名计算的。
于是想到通过API之类的东西查询这个数据。然后就发现了这个项目,大佬做出了各种关于竞赛排名的查询的东西,包括每场比赛全球参与人数,你搜索的用户排百分之几之类的。但是我并没有找到类似Guardian多少分查询的东西。但是问题不大,从这个项目中的global_ranking_crawler.py小小的改动一下就好了。
这是我刚刚跑完的结果:
Guardian 2158
Knight 1840

由于国内的勋章好像是根据国内的人数算的,所以与国外不同 (2021/08/08运行结果):
Guardian 2175 (全国排名627)
Knight 1843 (全国排名3137)
[注意] 这里的数仍然有一点儿小误差,因为第12551人,他的全国排名实际写的比这个数小。可能排行榜有一些奇奇怪怪的人被算进去。暂时没有处理这个小问题。

新增了全国全球不同的查询!!!
GitHub地址
#!/usr/bin/env python3
import json
import requests
# 力扣目前勋章的配置
RATING = 1600
GUARDIAN = 0.05
KNIGHT = 0.25
# 查询的地址为全国还是全球?很关键
GLOBAL = False
# 二分查找的右端点(可自调)
RIGHT = 3000
class RankingCrawler:
URL = 'https://leetcode.com/graphql' if GLOBAL else 'https://leetcode.cn/graphql'
_REQUEST_PAYLOAD_TEMPLATE = {
"operationName": None,
"variables": {},
"query":
r'''{
localRanking(page: 1) {
totalUsers
userPerPage
rankingNodes {
currentRating
currentGlobalRanking
}
}
}
''' if not GLOBAL else
r'''{
globalRanking(page: 1) {
totalUsers
userPerPage
rankingNodes {
currentRating
currentGlobalRanking
}
}
}
'''
}
def fetch_lastest_ranking(self, mode):
l, r = 1, RIGHT
retry_cnt = 0
ansRanking = None
while l < r:
cur_page = (l + r + 1) // 2
try:
payload = RankingCrawler._REQUEST_PAYLOAD_TEMPLATE.copy()
payload['query'] = payload['query'].replace('page: 1', 'page: {}'.format(cur_page))
resp = requests.post(RankingCrawler.URL,
headers = {'Content-type': 'application/json'},
json = payload).json()
resp = resp['data']['localRanking'] if not GLOBAL else resp['data']['globalRanking']
# no more data
if len(resp['rankingNodes']) == 0:
break
if not mode:
top = int(resp['rankingNodes'][0]['currentRating'].split('.')[0])
if top < RATING:
r = cur_page - 1
else:
l = cur_page
ansRanking = resp['rankingNodes']
else:
top = int(resp['rankingNodes'][0]['currentGlobalRanking'])
if top > mode:
r = cur_page - 1
else:
l = cur_page
ansRanking = resp['rankingNodes']
print('The first contest current rating in page {} is {} .'.format(cur_page, resp['rankingNodes'][0]['currentRating']))
retry_cnt = 0
except:
# print(f'Failed to retrieved data of page {cur_page}...retry...{retry_cnt}')
retry_cnt += 1
ansRanking = ansRanking[::-1]
last = None
if not mode:
while ansRanking and int(ansRanking[-1]['currentRating'].split('.')[0]) >= 1600:
last = ansRanking.pop()
else:
while ansRanking and int(ansRanking[-1]['currentGlobalRanking']) <= mode:
last = ansRanking.pop()
return last
if __name__ == "__main__":
crawler = RankingCrawler()
ans = crawler.fetch_lastest_ranking(0)
n = int(ans['currentGlobalRanking'])
guardian = crawler.fetch_lastest_ranking(int(GUARDIAN * n))
knight = crawler.fetch_lastest_ranking(int(KNIGHT * n))
if not GLOBAL:
guardian['currentCNRanking'] = guardian['currentGlobalRanking']
guardian.pop('currentGlobalRanking')
knight['currentCNRanking'] = knight['currentGlobalRanking']
knight.pop('currentGlobalRanking')
print("Done!")
print()
print("目前全{}1600分以上的有{}人".format("球" if GLOBAL else "国",n))
print("根据这个人数,我们得到的Guardian排名及分数信息为:{}".format(guardian))
print("根据这个人数,我们得到的Knight排名及分数信息为:{}".format(knight))目前实现复杂度为o(logn)
有任何不对的地方或是更快算法的建议,欢迎指出!