最小操作次数

中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

方法一:长度分奇偶计算中位数

arr.sort()
n = len(arr)
if n % 2: median = arr[n // 2]
else: median = (arr[n // 2] + arr[n // 2 - 1]) / 2

方法二:合并奇偶

median = (arr[(n - 1) // 2] + arr[n // 2]) / 2

方法三:采用 取反 的方式来求中位数

arr.sort()
h = len(arr) // 2
(arr[h] + arr[~h])/2

a = [0,1,2,3,4,5],a[3] 为列表的第 4 位数,~3 = -4, a[-4] 实际上是对列表进行反向查找,为列表的第 3 位数,中值 (2 + 3) / 2 = 2.5。
a = [0,1,2,3,4,5,6] ,a[3] 为列表的第 4 位数,a[-4] 在列表中是正向反向查找中的第 4 位数,所以 a[3] == a[-4], 中位数就为 a[3] 或者 a[-4]。

295. 数据流的中位数

from sortedcontainers import SortedList
class MedianFinder:
    def __init__(self):
        # self.sl = SortedList()
        self.sl = []

    def addNum(self, num: int) -> None:
        insort_left(self.sl, num)
        # self.sl.add(num)      
        
    def findMedian(self) -> float:
        n = len(self.sl)
        return (self.sl[n // 2] + self.sl[(n-1)//2]) / 2

480. 滑动窗口中位数

中位数(Median)统计学名词,是指将统计总体当中的各个变量值按大小顺序排列起来,形成一个数列,处于变量数列中间位置的变量值就称为中位数,用 Me 表示。当变量值的项数 N 为奇数时,处于中间位置的变量值即为中位数;当 N 为偶数时,中位数则为处于中间位置的 2 个变量值的平均数。
本题考查动态维护数组的中位数。

最小操作次数_第1张图片

对顶堆,所有 ≤ Me 的元素放到 small 中(大根堆),所有 > Me 的元素放到 big 中(小根堆),即一半小的放到 small,一半大的放到 big,small 至多多一个。

初始化:

先将前 k 个元素加入 small,然后从 small 弹出 k // 2 个元素到 big,small 至多多一个。

中位数:

当 k 为奇数时候,中位数是元素数量较多的 small 堆顶元素。
当 k 为偶数时候,中位数是 small 和 big 的堆顶元素平均值。

窗口滑动,左侧删除元素:

由于堆无法直接删除掉某个元素,所以需要延迟删除。d 记录这个元素欠账的个数。d[left]++;
堆两侧的平衡性发生了变化,如果 left ≤ small.top(),删掉的元素在 small 中,balance–; 否则,在 big 中,balance++;

右侧添加元素:
如果 right ≤ small.top(),right 放到 samll,balance++;否则放到 big,balance–。

操作后,balance = 0 或 2 或 -2。平衡调整后 balance = 0。
如果 balance = 0,以不用调整;
如果 balance = 2,small 比 big 多了两个,big.push(small.pop());
如果 balance = -2,small 比 big 少了两个,small.push(big.pop())。

清理堆顶删除的元素:
分别检查两边的堆顶元素,如果堆顶元素已被删除,则弹出堆顶元素,直到堆顶元素没有欠债为止。

计算中位数

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
        # # 方法一:
        # res = []
        # for i in range(len(nums) - k + 1):
        #     w = sorted(nums[i:i + k]) # 需要优化              
        #     res.append((w[(k - 1) // 2] + w[k // 2]) / 2)
        # return res

        # # 方法二:双堆对顶
        # dh = DualHeap(k)
        # for num in nums[:k]:
        #     dh.insert(num)
        
        # ans = [dh.getMedian()]
        # for i in range(k, len(nums)):
        #     dh.insert(nums[i])
        #     dh.erase(nums[i - k])
        #     ans.append(dh.getMedian())
        
        # return ans

        # 方法三:双堆对顶,简化版
        def get(k): # 中位数
            return -small[0] if k % 2 else (-small[0] + big[0]) / 2

        small, big = [], [] # 大堆,小堆
        d = defaultdict(int) # 延迟删除
        # 1、初始化大小堆,分割前 k 个元素
        for i in range(k): # 先把 k 个元素放入大堆
            heappush(small, -nums[i])
        for i in range(k // 2): # 一半较大的元素从大堆移到小堆
            heappush(big, -heappop(small))
        ans = [get(k)] # 第一个中位数
        # 2、滑动窗口
        for i in range(k, len(nums)):
            balance = 0
            # 2-1、记录将要删除的元素 
            left = nums[i - k] # 要删除的元素
            d[left] += 1 # 先记下要删除的元素            
            if small and left <= -small[0]: balance -= 1 # 在小堆
            else: balance += 1 # 在大堆
            # 2-2、确定当前元素加入那个堆
            if small and nums[i] <= -small[0]:
                heappush(small, -nums[i])
                balance += 1
            else:
                heappush(big, nums[i])
                balance -= 1
            # 2-3、平衡大小堆
            if balance > 0:
                heappush(big, -heappop(small))                
            if balance < 0:
                heappush(small, -heappop(big))     
            # 2-4、清理堆顶
            while small and d[-small[0]] > 0:
                d[-heappop(small)] -= 1
            while big and d[big[0]] > 0:
                d[heappop(big)] -= 1    
            # 2-5、计算中位数
            ans.append(get(k)) 
        return ans

class DualHeap:
    def __init__(self, k: int):        
        self.small = list() # 大根堆,维护较小的一半元素        
        self.large = list() # 小根堆,维护较大的一半元素
        # 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数
        self.delayed = Counter()
        self.k = k
        # small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素
        self.smallSize = 0
        self.largeSize = 0

    # 清理堆顶延迟删除的元素
    def prune(self, heap: List[int]):
        while heap:
            x = heap[0]
            if heap is self.small: x = -x
            if x in self.delayed:
                self.delayed[x] -= 1
                if self.delayed[x] == 0:
                    self.delayed.pop(x)
                heappop(heap)
            else: break
    
    # 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求
    def makeBalance(self):
        if self.smallSize > self.largeSize + 1: # small 比 large 元素多 2 个
            heappush(self.large, -heappop(self.small))            
            self.smallSize -= 1
            self.largeSize += 1            
            self.prune(self.small) # 清理
        elif self.smallSize < self.largeSize:
            # large 比 small 元素多 1 个
            heappush(self.small, -heappop(self.large))            
            self.smallSize += 1
            self.largeSize -= 1
            self.prune(self.large) # 清理

    def insert(self, num: int):
        if not self.small or num <= -self.small[0]:
            heappush(self.small, -num)
            self.smallSize += 1
        else:
            heappush(self.large, num)
            self.largeSize += 1
        self.makeBalance()

    def erase(self, num: int):
        self.delayed[num] += 1
        if num <= -self.small[0]:
            self.smallSize -= 1
            if num == -self.small[0]:
                self.prune(self.small)
        else:
            self.largeSize -= 1
            if num == self.large[0]:
                self.prune(self.large)
        self.makeBalance()

    def getMedian(self) -> float:
        return float(-self.small[0]) if self.k % 2 else (-self.small[0] + self.large[0]) / 2

453. 最小操作次数使数组元素相等

假设 需要 x 次操作,每次操作一定有 min 参与,最终每一个数都变成了 min + x。
s u m + x ∗ ( n − 1 ) = ( m i n + x ) ∗ n sum + x * (n - 1) = (min + x)* n sum+x(n1)=(min+x)n
即: x = s u m − m i n ∗ n x = sum - min * n x=summinn
也就是 n - 1 个元素增加 1,相对于使 1 个元素减少 1。即将所有元素减少到最小值所需的操作数。
在实现中,为避免溢出,逐个累加每个元素与数组中元素最小值的差。

class Solution:
    def minMoves(self, nums: List[int]) -> int:
        min_ = min(nums)
        return sum(x - min_ for x in nums)
        # return sum(nums) - min(nums)*len(nums)

462. 最少移动次数使数组元素相等 II

中位数性质:数组中所有数与中位数的绝对差之和最小。

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        nums.sort()
        half = len(nums) // 2
        median = (nums[half] + nums[~half])//2
        return sum(abs(median - x) for x in nums)

2448. 使数组相等的最小开销

1007. 行相等的最少多米诺旋转

1013. 将数组分成和相等的三个部分

1033. 移动石子直到连续

1040. 移动石子直到连续 II

1043. 分隔数组以得到最大和

1054. 距离相等的条形码

1060. 有序数组中的缺失元素

1074. 元素和为目标值的子矩阵数量

108. 将有序数组转换为二叉搜索树

1085. 最小元素各数位之和

1095. 山脉数组中查找目标值

1121. 将数组分成几个递增序列

1144. 递减元素使数组呈锯齿状

1146. 快照数组

1150. 检查一个数是否在数组中占绝大多数

1151. 最少交换次数来组合所有的 1

1157. 子数组中占绝大多数的元素

1161. 最大层内元素和

1186. 删除一次得到子数组最大和

1187. 使数组严格递增

1191. K 次串联后最大子数组之和

1198. 找出所有行中最小公共元素

1202. 交换字符串中的元素

1207. 独一无二的出现次数

1208. 尽可能使字符串相等

1210. 穿过迷宫的最少移动次数

1224. 最大相等频率

1243. 数组变换

1246. 删除回文子数组

1248. 统计「优美子数组」

1250. 检查「好数组」

1261. 在受污染的二叉树中查找元素

1283. 使结果不超过阈值的最小除数

1284. 转化为全零矩阵的最少反转次数

1287. 有序数组中出现次数超过25%的元素

1292. 元素和小于等于阈值的正方形的最大边长

1296. 划分数组为连续数字的集合

1297. 子串的最大出现次数

1299. 将每个元素替换为右侧最大元素

1300. 转变数组后最接近目标值的数组和

1305. 两棵二叉搜索树中的所有元素

1310. 子数组异或查询

1312. 让字符串成为回文串的最少插入次数

1318. 或运算的最小翻转次数

1319. 连通网络的操作次数

1326. 灌溉花园的最少水龙头数目

1330. 翻转子数组得到最大的数组值

1331. 数组序号转换

1338. 数组大小减半

1342. 将数字变成 0 的操作次数

1343. 大小为 K 且平均值大于等于阈值的子数组数目

1354. 多次求和构造目标数组

1368. 使网格图至少有一条有效路径的最小代价

1375. 二进制字符串前缀一致的次数

1385. 两个数组间的距离值

1389. 按既定顺序创建目标数组

1394. 找出数组中的幸运数

1408. 数组中的字符串匹配

1414. 和为 K 的最少斐波那契数字数目

1416. 恢复数组

1420. 生成数组

1426. 数元素

1437. 是否所有 1 都至少相隔 k 个元素

1438. 绝对差不超过限制的最长连续子数组

1441. 用栈操作构建数组

1442. 形成两个异或相等数组的三元组数目

1443. 收集树上所有苹果的最少时间

1460. 通过翻转子数组使两个数组相等

1464. 数组中两元素的最大乘积

1470. 重新排列数组

1471. 数组中的 k 个最强值

1477. 找两个和为目标值且不重叠的子数组

1481. 不同整数的最少数目

1482. 制作 m 束花所需的最少天数

1486. 数组异或操作

1493. 删掉一个元素以后全为 1 的最长子数组

1497. 检查数组对是否可以被 k 整除

1516. 移动 N 叉树的子树

152. 乘积最大子数组

1524. 和为奇数的子数组数目

1526. 形成目标数组的子数组最少增加次数

1529. 最少的后缀翻转次数

153. 寻找旋转排序数组中的最小值

1535. 找出数组游戏的赢家

1536. 排布二进制网格的最少交换次数

1538. 找出隐藏数组中出现次数最多的元素

154. 寻找旋转排序数组中的最小值 II

1541. 平衡括号字符串的最少插入次数

1550. 存在连续三个奇数的数组

1551. 使数组中所有元素相等的最小操作数

1553. 吃掉 N 个橘子的最少天数

1558. 得到目标数组的最少函数调用次数

1560. 圆形赛道上经过次数最多的扇区

1567. 乘积为正数的最长子数组长度

1568. 使陆地分离的最少天数

1569. 将子数组重新排序得到同一个二叉查找树的方案数

1572. 矩阵对角线元素的和

1574. 删除最短的子数组使剩余数组有序

1578. 使绳子变成彩色的最短时间

1590. 使数组和能被 P 整除

1593. 拆分字符串使唯一子字符串的数目最大

1611. 使整数变为 0 的最少操作次数

1619. 删除某些元素后的数组均值

1630. 等差子数组

1636. 按照频率将数组升序排序

1640. 能否连接形成数组

1646. 获取生成数组中的最大值

1647. 字符频次唯一的最小删除次数

1649. 通过指令创建有序数组

1653. 使字符串平衡的最少删除次数

1654. 到家的最少跳跃次数

1662. 检查两个字符串数组是否相等

1665. 完成所有任务的最少初始能量

167. 两数之和 II - 输入有序数组

1671. 得到山形数组的最少删除次数

1674. 使数组互补的最少操作次数

1685. 有序数组中差绝对值之和

1688. 比赛中的配对次数

1689. 十-二进制数的最少数目

169. 多数元素

1703. 得到连续 K 个 1 的最少相邻交换次数

1707. 与数组中元素的最大异或值

1708. 长度为 K 的最大子数组

1712. 将数组分成三个子数组的方案数

1713. 得到子序列的最少操作次数

1714. 数组中特殊等间距元素的和

1720. 解码异或后的数组

1733. 需要教语言的最少人数

1737. 满足三条件之一需改变的最少字符数

1743. 从相邻元素对还原数组

1748. 唯一元素的和

1752. 检查数组是否经排序和轮转得到

1758. 生成交替二进制字符串的最少操作数

1764. 通过连接另一个数组的子数组得到一个数组

1769. 移动所有球到每个盒子所需的最小操作数

1775. 通过最少操作次数使数组的和相等

1785. 构成特定和需要添加的最少元素

1787. 使所有区间的异或结果为零

1790. 仅执行一次字符串交换能否使两个字符串相等

1802. 有界数组中指定下标处的最大值

1806. 还原排列的最少操作步数

1814. 统计一个数组中好对子的数目

1822. 数组元素积的符号

1824. 最少侧跳次数

1827. 最少操作使数组递增

1830. 使字符串有序的最少操作次数

1836. 从未排序的链表中移除重复元素

1838. 最高频元素的频数

1846. 减小和重新排列数组后的最大元素

1848. 到目标元素的最小距离

1850. 邻位交换的最小次数

1856. 子数组最小乘积的最大值

1864. 构成交替字符串需要的最小交换次数

1877. 数组中最大数对和的最小值

1879. 两个数组最小的异或值之和

1883. 准时抵达会议现场的最小跳过休息次数

1887. 使数组元素相等的减少操作次数

1888. 使二进制字符串字符交替的最少反转次数

189. 轮转数组

1896. 反转表达式值的最少操作次数

1897. 重新分配字符使所有字符串都相等

1901. 找出顶峰元素 II

1909. 删除一个元素使数组严格递增

1920. 基于排列构建数组

1929. 数组串联

1936. 新增的最少台阶数

1940. 排序数组之间的最长公共子序列

1941. 检查是否所有字符出现次数相同

1950. 所有子数组最小值中的最大值

1957. 删除字符使字符串变好

1959. K 次调整数组大小浪费的最小总空间

1961. 检查字符串是否为数组前缀

1962. 移除石子使总数最小

1963. 使字符串平衡的最小交换次数

1966. 未排序数组中的可被二分搜索的数

1968. 构造元素不等于两相邻元素平均值的数组

1969. 数组元素的最小非零乘积

1974. 使用特殊打字机键入单词的最少时间

1981. 最小化目标值与所选元素的差

1982. 从子集的和还原数组

1983. 范围和相等的最宽索引对

1985. 找出数组中的第 K 大整数

1986. 完成任务的最少工作时间段

2007. 从双倍数组中还原原数组

2009. 使数组连续的最少操作数

2012. 数组美丽值求和

2016. 增量元素之间的最大差值

2022. 将一维数组转变成二维数组

2027. 转换字符串的最少操作次数

203. 移除链表元素

2031. 1 比 0 多的子数组个数

2032. 至少在两个数组中出现的值

2035. 将数组分成两个数组并最小化数组和的差

2037. 使每位学生都有座位的最少移动次数

2053. 数组中第 K 个独一无二的字符串

2056. 棋盘上有效移动组合的数目

2057. 值相等的最小索引

2068. 检查两个字符串是否几乎相等

2086. 从房屋收集雨水需要的最少水桶数

2089. 找出数组排序后的目标下标

209. 长度最小的子数组

2090. 半径为 k 的子数组平均值

2091. 从数组中移除最大值和最小值

2104. 子数组范围和

2108. 找出数组中的第一个回文字符串

2111. 使数组 K 递增的最少操作次数

2113. 查询删除和添加元素后的数组

2121. 相同元素的间隔之和

2122. 还原原数组

2123. 使矩阵中的 1 互不相邻的最小操作数

2134. 最少交换次数来组合所有的 1 II

2137. 通过倒水操作让所有的水桶所含水量相等

2139. 得到目标值的最少行动次数

2145. 统计隐藏数组数目

2148. 元素计数

2149. 按符号重排数组

215. 数组中的第K个最大元素

2150. 找出数组中的所有孤独数字

2152. 穿过所有点的所需最少直线数量

2161. 根据给定数字划分数组

2162. 设置时间的最少代价

2163. 删除元素后和的最小差值

2167. 移除所有载有违禁货物车厢所需的最少时间

217. 存在重复元素

2170. 使数组变成交替数组的最少操作数

2176. 统计数组中相等且可以被整除的数对

2179. 统计数组中好三元组数目

2186. 使两字符串互为字母异位词的最少步骤数

2187. 完成旅途的最少时间

2188. 完成比赛的最少时间

219. 存在重复元素 II

2190. 数组中紧跟 key 之后出现最频繁的数字

2193. 得到回文串的最少操作次数

2195. 向数组中追加 K 个整数

2197. 替换数组中的非互质数

220. 存在重复元素 III

2200. 找出数组中的所有 K 近邻下标

2202. K 次操作后最大化顶端元素

2206. 将数组划分成相等数对

2208. 将数组和减半的最少操作次数

2209. 用地毯覆盖后的最少白色砖块

2210. 统计数组中峰和谷的数量

2211. 统计道路上的碰撞次数

2216. 美化数组的最少删除数

2220. 转换数字的最少位翻转次数

2224. 转化时间需要的最少操作数

2244. 完成所有任务需要的最少轮数

2248. 多个数组求交集

2261. 含最多 K 个可整除元素的子数组

2273. 移除字母异位词后的结果数组

229. 多数元素 II

230. 二叉搜索树中第K小的元素

26. 删除有序数组中的重复项

27. 移除元素

283. 移动零

303. 区域和检索 - 数组不可变

307. 区域和检索 - 数组可修改

315. 计算右侧小于当前元素的个数

325. 和等于 k 的最长子数组长度

33. 搜索旋转排序数组

330. 按要求补齐数组

34. 在排序数组中查找元素的第一个和最后一个位置

346. 数据流中的移动平均值

347. 前 K 个高频元素

360. 有序转化数组

378. 有序矩阵中第 K 小的元素

380. O(1) 时间插入、删除和获取随机元素

381. O(1) 时间插入、删除和获取随机元素 - 允许重复

384. 打乱数组

421. 数组中两个数的最大异或值

442. 数组中重复的数据

448. 找到所有数组中消失的数字

453. 最小操作次数使数组元素相等

457. 环形数组是否存在循环

462. 最少移动次数使数组元素相等 II

462. 最少移动次数使数组元素相等 II

496. 下一个更大元素 I

503. 下一个更大元素 II

508. 出现次数最多的子树元素和

525. 连续数组

532. 数组中的 k-diff 数对

540. 有序数组中的单一元素

548. 将数组分割成和相等的子数组

556. 下一个更大元素 III

560. 和为 K 的子数组

561. 数组拆分 I

565. 数组嵌套

581. 最短无序连续子数组

624. 数组列表中的最大距离

629. K个逆序对数组

643. 子数组最大平均数 I

644. 子数组最大平均数 II

658. 找到 K 个最接近的元素

659. 分割数组为连续子序列

698. 划分为k个相等的子集

702. 搜索长度未知的有序数组

703. 数据流中的第 K 大元素

713. 乘积小于 K 的子数组

718. 最长重复子数组

795. 区间子数组个数

80. 删除有序数组中的重复项 II

801. 使序列递增的最小交换次数

81. 搜索旋转排序数组 II

82. 删除排序链表中的重复元素 II

83. 删除排序链表中的重复元素

842. 将数组拆分成斐波那契序列

845. 数组中的最长山脉

862. 和至少为 K 的最短子数组

871. 最低加油次数

88. 合并两个有序数组

898. 子数组按位或操作

905. 按奇偶排序数组

912. 排序数组

915. 分割数组

921. 使括号有效的最少添加

922. 按奇偶排序数组 II

930. 和相同的二元子数组

932. 漂亮数组

933. 最近的请求次数

941. 有效的山脉数组

945. 使数组唯一的最小增量

954. 二倍数对数组

961. 在长度 2N 的数组中找出重复 N 次的元素

964. 表示数字的最少运算符

972. 相等的有理数

974. 和可被 K 整除的子数组

978. 最长湍流子数组

989. 数组形式的整数加法

992. K 个不同整数的子数组

995. K 连续位的最小翻转次数

你可能感兴趣的:(算法,排序算法,算法,leetcode)