其實下面這兩個問題本質上是一個問題。
例 1:《劍指 Offer 》第 51 題:逆序數的計算
在數組中的兩個數字如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。
輸入一個數組,求出這個數組中的逆序對的總數。
樣例
輸入:[1,2,3,4,5,6,0] 輸出:6
分析:這道題最經典的思路是使用分治法計算,不過使用樹狀數組語義更清晰一些。
Python 代碼:
class Solution(object):
def inversePairs(self, nums):
class FenwickTree:
def __init__(self, n):
self.size = n
self.tree = [0 for _ in range(n + 1)]
def __lowbit(self, index):
return index & (-index)
# 單點更新:從下到上,最多到 size,可以取等
def update(self, index, delta):
while index <= self.size:
self.tree[index] += delta
index += self.__lowbit(index)
# 區間查詢:從上到下,最少到 1,可以取等
def query(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= self.__lowbit(index)
return res
# 特判
l = len(nums)
if l < 2:
return 0
# 原始數組去除重複以後從小到大排序
s = list(set(nums))
# 構建最小堆,因爲從小到大一個一個拿出來,用堆比較合適
import heapq
heapq.heapify(s)
# 由數字查排名
rank_map = dict()
index = 1
# 不重複數字的個數
size = len(s)
for _ in range(size):
num = heapq.heappop(s)
rank_map[num] = index
index += 1
res = 0
# 樹狀數組只要不重複數字個數這麼多空間就夠了
ft = FenwickTree(size)
# 從後向前看,拿出一個數字來,就更新一下,然後向前查詢比它小的個數
for i in range(l - 1, -1, -1):
rank = rank_map[nums[i]]
ft.update(rank, 1)
res += ft.query(rank - 1)
return res
說明:中間將數字映射到排名是將原數組「離散化」,「離散化」的原因有 2 點:
1、樹狀數組我們看到,下標是從 開始的,我們不能保證我們的數組所有的元素都大於等於 ;
2、即使元素都大於等於 ,爲了節約樹狀數組的空間,我們將之「離散化」可以把原始的數都壓縮到一個小區間。我說的有點不太清楚,這一點可以參考 樹狀數組 求逆序數 poj 2299。
例2:「力扣」第 315 題:計算右側小於當前元素的個數
- 題目鏈接:315. 計算右側小於當前元素的個數。
給定一個整數數組 nums,按要求返回一個新數組 counts。數組 counts 有該性質:
counts[i]
的值是nums[i]
右側小於nums[i]
的元素的數量。示例:
輸入: [5,2,6,1] 輸出: [2,1,1,0] 解釋: 5 的右側有 2 個更小的元素 (2 和 1). 2 的右側僅有 1 個更小的元素 (1). 6 的右側有 1 個更小的元素 (1). 1 的右側有 0 個更小的元素.
分析:事實上,這個問題就是在計算「逆序數」,和上一個問題是一樣的。「計算右側小於當前元素的個數」我們可以「從後向前一個一個填」。因爲涉及大小關係,所以要排個序,並且給出排名(rank
)。這一步操作也叫「離散化」。具體方法是:先畫出一個排名表,對於這個問題,排名表是:
數 | 排名 |
---|---|
從後向前填:
1、遇到 , 的排名是 ,首先先在 那個位置更新 ,那麼 之前肯定沒有數了,所以就是 ;
2、遇到 , 的排名是 ,首先先在 那個位置更新 ,那麼 之前可以在樹狀樹組裏面查一下,是 ;
3、遇到 , 的排名是 ,首先先在 那個位置更新 ,那麼 之前可以在樹狀樹組裏面查一下,是 ;
4、遇到 , 的排名是 ,首先先在 那個位置更新 ,那麼 之前可以在樹狀樹組裏面查一下,是 ;
反過來就是結果 。
Python 代碼:
class Solution:
def countSmaller(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
class FenwickTree:
def __init__(self, n):
self.size = n
self.tree = [0 for _ in range(n + 1)]
def __lowbit(self, index):
return index & (-index)
# 單點更新:從下到上,最多到 size,可以取等
def update(self, index, delta):
while index <= self.size:
self.tree[index] += delta
index += self.__lowbit(index)
# 區間查詢:從上到下,最少到 1,可以取等
def query(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= self.__lowbit(index)
return res
l = len(nums)
if l == 0:
return []
if l == 1:
return [0]
s = list(set(nums))
import heapq
heapq.heapify(s)
index = 1
size = len(s)
rank_map = dict()
ft = FenwickTree(size)
for _ in range(size):
num = heapq.heappop(s)
rank_map[num] = index
index += 1
# 從後向前填表
res = [None for _ in range(l)]
for index in range(l - 1, -1, -1):
rank = rank_map[nums[index]]
ft.update(rank, 1)
res[index] = ft.query(rank - 1)
return res
(本文完)