LeetCode算法日記 - Day 26: 歸并排序、交易逆序對的總數

目錄

1. 歸并排序

1.1 題目解析

1.2 解法

1.3 代碼實現

2. 交易逆序對的總數

2.1 題目解析

2.2 解法

2.3 代碼實現


1. 歸并排序

912. 排序數組 - 力扣(LeetCode)

給你一個整數數組?nums,請你將該數組升序排列。

你必須在?不使用任何內置函數?的情況下解決問題,時間復雜度為?O(nlog(n)),并且空間復雜度盡可能小。

    示例 1:

    輸入:nums = [5,2,3,1]
    輸出:[1,2,3,5]
    解釋:數組排序后,某些數字的位置沒有改變(例如,2 和 3),而其他數字的位置發生了改變(例如,1 和 5)。
    

    示例 2:

    輸入:nums = [5,1,1,2,0,0]
    輸出:[0,0,1,1,2,5]
    解釋:請注意,nums 的值不一定唯一。

    提示:

    • 1 <= nums.length <= 5 * 104
    • -5 * 104 <= nums[i] <= 5 * 104

    1.1 題目解析

    題目本質

    把整數數組 nums 升序排列。目標是在不調用內置排序的前提下,做到 O(n log n) 的時間復雜度,且額外空間盡量小

    常規解法

    • 直接用冒泡 / 插入 / 選擇:實現簡單,但時間復雜度 O(n^2),在 n=5*10^4 時會超時。

    • 快排(手寫):平均 O(n log n),最壞 O(n^2),實現要注意樞軸與不變式,否則容易退化。

    問題分析

    題目希望穩定達成 O(n log n)。穩定、可控且好實現的選擇是歸并排序

    • 分而治之(遞歸二分)保證 log n 層;

    • 每層合并代價 O(n)

    • 總體 O(n log n)

    • 代價是需要一段臨時數組 tmpO(n) 額外空間)。

    思路轉折

    要想時間穩定在 O(n log n) 且實現穩健 → 走歸并排序路線:

    • 先把左右兩段分別排好;

    • 合并時雙指針線性掃一遍,把更小的元素依次寫入 tmp;

    • 最后把 tmp 回寫到 nums[left..right]。

    預測:時間 O(n log n);空間 O(n)(單份 tmp 復用,已經是歸并常見的最小開銷版)。

    1.2 解法

    算法思想

    分治 + 合并?對區間 [left, right]:

    1. 劃分中點 mid = (right + left) / 2;

    2. 遞歸排好 [left, mid] 與 [mid+1, right];

    3. 用雙指針 cur1、cur2 合并到 tmp[0..];

    4. 把 tmp 覆蓋回 nums[left..right]。

    此實現是穩定排序:當相等時 nums[cur1] <= nums[cur2] 先取左邊,保持相對次序。

    i)在 sortArray 中創建一份長度為 nums.length 的 tmp,供所有遞歸復用;

    ii)mergeSort(nums, left, right):?

    • 遞歸終止:left >= right;

    • 分治:mergeSort(nums, left, mid) 與 mergeSort(nums, mid+1, right);

    • 合并:

      • cur1 = left, cur2 = mid+1, i = 0;

      • 當 cur1<=mid && cur2<=right:把較小的放入 tmp[i++];

      • 掃尾:把剩余的另一側元素依次放入 tmp;

      • 回寫:for (int j = left; j <= right; j++) nums[j] = tmp[j-left];

    易錯點

    • 合并時比較的是元素值:nums[cur1] <= nums[cur2],不是下標范圍。

    • 遞增 i 指針?tmp[i++] 。

    • 回寫時的偏移要用 j - left,因為 tmp 從 0 寫起。

    1.3 代碼實現

    class Solution {int[] tmp;public int[] sortArray(int[] nums) {tmp = new int[nums.length];mergeSort(nums, 0, nums.length - 1);return nums;    }public void mergeSort(int[] nums, int left, int right) {if (left >= right) {return;}int mid = (right + left) / 2;mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);int i = 0, cur1 = left, cur2 = mid + 1;while (cur1 <= mid && cur2 <= right) {tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];}while (cur1 <= mid)   tmp[i++] = nums[cur1++];while (cur2 <= right) tmp[i++] = nums[cur2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}}
    }
    

    復雜度分析

    • 時間復雜度:O(n log n)

    • 空間復雜度:O(n)

    2. 交易逆序對的總數

    LCR 170. 交易逆序對的總數 - 力扣(LeetCode)

    在股票交易中,如果前一天的股價高于后一天的股價,則可以認為存在一個「交易逆序對」。請設計一個程序,輸入一段時間內的股票交易記錄?record,返回其中存在的「交易逆序對」總數。

    示例 1:

    輸入:record = [9, 7, 5, 4, 6]
    輸出:8
    解釋:交易中的逆序對為 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。

    提示:

    0 <= record.length <= 50000

    2.1 題目解析

    題目本質

    這是典型的逆序對計數:給定數組 record,統計滿足 i < j 且 record[i] > record[j] 的有序對數量。


    核心抽象:用分治 + 合并(歸并排序),在合并兩段有序區間時批量統計跨段逆序對。

    常規解法

    雙重循環枚舉所有 (i, j),判斷 record[i] > record[j]。

    問題分析

    暴力是 O(n^2),n=5e4 時約 12.5 億次比較,不可接受。

    思路轉折

    要高效 → 需要在有序結構里批量計數 → 歸并合并時一次比較可累計一段:

    • 升序合并:若 nums[i] > nums[j](i∈[left,mid], j∈[mid+1,right]),則左側 [i..mid] 全部大于 nums[j],一次性加 mid - i + 1。

    • 降序合并:若 nums[i] > nums[j],則右側 [j..right] 全部小于 nums[i],一次性加 right - j + 1。

    預測:整體 O(n log n),可過最大規模。

    2.2 解法

    算法思想

    分治 + 合并計數?對 [left, right]:

    • 遞歸統計左區間 [left, mid] 總數、右區間 [mid+1, right] 總數;

    • 合并時統計跨跨區間總數

    • 總數 ret = 左區間 + 右區間??+ 跨區間。

    升序合并公式

    nums[i] > nums[j] ? ret += (mid - i + 1)。

    降序合并公式

    nums[i] > nums[j] ? ret += (right - j + 1)。

    兩種寫法都對;差別只在“誰先入 tmp ”以及“加哪一側的剩余長度”。

    i)遞歸把 [left, right] 拆到長度為 1;

    ii)分別統計左右段的逆序對;

    iii)合并兩段有序數組,同時批量統計跨段逆序對;

    iiii)回寫 tmp 到 nums[left..right];

    iiiii)返回計數。

    易錯點

    • 遞歸區間錯誤

    • 合并時比較的是“值”,不是下標范圍:if (nums[i] <= nums[j])。

    • 遞增臨時數組指針:tmp[k++]

    • 計數的“參照物”要和合并方向匹配

      • 升序合并:右更小時加左側剩余 mid - i + 1。

      • 降序合并:左更大時加右側剩余 right - j + 1。

    • 回寫用偏移:nums[j] = tmp[j-left],不要用已移動過的 left 做下標。j 遍歷原區間 [left..right],j-left 就是對應的 tmp 下標。

    2.3 代碼實現

    方法一:升序合并計數

    class Solution {int dest;         int[] tmp;public int reversePairs(int[] record) {if (record == null || record.length < 2) return 0;dest = 0;                                  tmp = new int[record.length];int result = mergeSort(record, 0, record.length - 1);return result;}public int mergeSort(int[] nums, int left, int right) {if (left >= right) return 0;int mid = left + (right - left) / 2;int ret = 0;ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) {     // 升序合并if (nums[cur1] <= nums[cur2]) {tmp[i++] = nums[cur1++];} else {// 右更小 → 左側 [cur1..mid] 都 > nums[cur2]ret += (mid - cur1 + 1);tmp[i++] = nums[cur2++];}}while (cur1 <= mid)   tmp[i++] = nums[cur1++];while (cur2 <= right) tmp[i++] = nums[cur2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}return ret;}
    }
    

    方法二:降序合并計數

    class Solution {int dest;          int[] tmp;public int reversePairs(int[] record) {if (record == null || record.length < 2) return 0;dest = 0;tmp = new int[record.length];int result = mergeSort(record, 0, record.length - 1);return result;}public int mergeSort(int[] nums, int left, int right) {if (left >= right) return 0;int mid = left + (right - left) / 2;int ret = 0;ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) {     // 降序合并if (nums[cur1] > nums[cur2]) {// 左更大 → 右側 [cur2..right] 都 < nums[cur1]ret += (right - cur2 + 1);tmp[i++] = nums[cur1++];} else {tmp[i++] = nums[cur2++];}}while (cur1 <= mid)   tmp[i++] = nums[cur1++];while (cur2 <= right) tmp[i++] = nums[cur2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}return ret;}
    }
    

    復雜度分析

    • 時間復雜度:O(n log n)

    • 空間復雜度:O(n)

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/web/94985.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/web/94985.shtml
    英文地址,請注明出處:http://en.pswp.cn/web/94985.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    C++(Qt)軟件調試---vcpkg安裝crashpad(34)

    C(Qt)軟件調試—vcpkg安裝crashpad&#xff08;34&#xff09; 文章目錄C(Qt)軟件調試---vcpkg安裝crashpad&#xff08;34&#xff09;[toc]1 概述&#x1f41c;2 環境配置3 qt使用crashpad庫捕獲異常4 cmake中添加crashpad5 相關地址&#x1f410;更多精彩內容&#x1f449;內…

    Kafka 副本同步異常與 ISR 收縮故障排查實錄

    背景 某高流量 Kafka 集群&#xff08;原 10G 網卡&#xff09;在切中心時頻繁觸發帶寬報警&#xff0c;擴容至 25G 網卡后出現副本同步異常&#xff1a; 操作流程&#xff1a;停機→升級網卡→重啟→觸發分區同步→切換首選 Leader現象&#xff1a; 寫入流量上升后&#xff0c…

    頂點 (VS)vs 片段(FS):OpenGL紋理滾動著色器的性能博弈與設計哲學

    一個微妙的選擇&#xff0c;影響整個應用性能表現在實時圖形渲染中&#xff0c;實現紋理滾動效果是一種常見需求。但當我們在頂點著色器和片段著色器之間做出不同實現選擇時&#xff0c;會對性能產生顯著影響。今天&#xff0c;我們將深入探討這兩種實現的差異&#xff0c;幫助…

    基于博客系統的自動化測試項目

    目錄 一、引言 二、項目背景 三、項目功能 1&#xff09;初始登錄界面 2&#xff09;博客首頁 3&#xff09;博客詳情頁 4&#xff09;博客編輯頁 四、測試工具 1&#xff09;基礎操作系統環境 2&#xff09;瀏覽器環境 3&#xff09;開發與測試工具環境 4&#xf…

    R 語言 eulerr 包繪制韋恩圖:比例精準

    在數據可視化中,韋恩圖是展示多組數據交集關系的常用工具,尤其在生物信息(如基因差異表達分析)、統計分析等領域高頻使用。但傳統繪圖工具常面臨橢圓比例失衡、數值顯示混亂、樣式調整繁瑣等問題,而 R 語言的eulerr包恰好能解決這些痛點 —— 它支持按數據比例自動適配圖形…

    CRYPT32!CryptMsgUpdate函數分析和asn.1 editor nt5inf.cat 的總覽信息

    0000: 30 83 09 69 2f ; SEQUENCE (9692f Bytes) 0005: 06 09 ; OBJECT_IDENTIFIER (9 Bytes) 0007: | 2a 86 48 86 f7 0d 01 07 02| ; "PKCS 7 已簽名 (1.2.840.113549.1.7.2)" 0010: …

    04數據庫約束實戰:從入門到精通

    感謝黑馬程序員提供的免費課程約束概念&#xff1a;約束是作用于表中字段上的規則&#xff0c;用于限制存儲在表中的數據。目的&#xff1a;保證數據庫中數據的正確、有效性和完整性。常見的幾種約束&#xff1a;注意&#xff1a;約束是作用于表中字段上的&#xff0c;可以在創…

    WPF+IOC學習記錄

    最近在學WPF&#xff0c;上一篇文章記錄了WPF的MVVM自己實現和用框架的區別&#xff08;WPFMVVM入門學習&#xff09;&#xff0c;接下這篇文章記錄一下在WPF中使用IOC&#xff0c;這里演示用的是微軟官方的DependencyInjection&#xff0c;也可以用其他的第三方框架。 項目源…

    從零開始學習單片機16

    STM32單片機STM32和51單片機的區別51單片機的外設資源少&#xff0c;寄存器少&#xff0c;運行速度慢&#xff0c;價格便宜&#xff0c;容易上手STM32單片機的外設資源更多&#xff0c;寄存器多&#xff0c;運行速度相對快&#xff0c;價格相對貴&#xff0c;上手相對較難STM32…

    [特殊字符]論一個 bug 如何經過千難萬險占領線上

    謹以此文獻給每一個曾與 Bug 搏斗、最終卻目睹它成功上線的你 本文旨在揭露 Bug 的狡猾&#xff0c;絕非鼓勵以下行為。若你照做&#xff0c;后果自負&#x1f436;每一個在線上逍遙法外的 Bug&#xff0c;都不是偶然。它是一場精心策劃的奇跡&#xff0c;是開發、聯調、測試、…

    Day12-python文件操作(二)

    目錄前言一、Excel文檔操作1.1、xlrd和xlwt庫1.2、openpyxl庫1.3、pandas庫總結前言 今天繼續學習文件操作相關內容&#xff0c;為后續辦公自動化打基礎。 一、Excel文檔操作 1.1、xlrd和xlwt庫 如果要兼容 Excel 2007 以前的版本&#xff0c;也就是xls格式的 Excel 文件&am…

    CollageIt:簡單易用的照片拼貼工具

    在數字圖像處理領域&#xff0c;制作照片拼貼是一種常見的創意表達方式。CollageIt作為一款體積小巧、簡單易用的照片拼貼工具&#xff0c;能夠幫助用戶輕松將多張圖片拼合成一張精美的拼貼畫。它不僅操作簡單&#xff0c;還支持多種圖片格式&#xff0c;確保用戶可以快速制作出…

    Java全棧工程師的實戰面試:從基礎到微服務的全面解析

    Java全棧工程師的實戰面試&#xff1a;從基礎到微服務的全面解析 一、開場介紹 面試官&#xff1a;你好&#xff0c;歡迎來到我們公司。我是今天的面試官&#xff0c;負責技術部分的評估。請先簡單介紹一下你自己。 應聘者&#xff1a;您好&#xff0c;我叫李明&#xff0c;25歲…

    驅動開發系列68 - GLSL編譯器實現 - 算數指令折疊及訪存優化

    一 : 指令合并概述 指令折疊的意思,原本一個語句會產生多條指令,通過折疊,可以刪除一些中間指令,減少指令數量,并且能夠減少寄存器占用。提高執行效率。 舉一個例子: MUL A, B, 4 ; A = B * 4MAD D, A, 2, F ; D = A * 2 + F MAD G, A, 3, I ; G …

    深入解析Qt節點編輯器框架:高級特性與性能優化(四)

    文章目錄一、高級交互特性&#xff1a;超越基礎操作的用戶體驗提升1. 節點組管理&#xff1a;折疊與嵌套的層級組織2. 智能連接線路由&#xff1a;避免交叉與視覺混亂3. 批量操作與快捷鍵&#xff1a;提升操作效率二、性能優化&#xff1a;應對大規模節點場景的核心策略1. 圖形…

    Python 入門操作指南

    引言 Python 是一種簡單易學卻功能強大的編程語言,廣泛應用于數據分析、人工智能、Web 開發等領域。對于初學者而言,掌握 Python 的入門操作是邁向編程世界的第一步。本文將以總分總的結構,系統介紹 Python 的安裝方法、推薦的開發工具、第一個 Python 程序示例,以及包管理…

    ZooKeeper 安裝配置

    前言 有時會需要安裝開源的大數據集群進行測評或者驗證問題&#xff0c;已經裝過很多遍了&#xff0c;所以想系統的總結整理一下各個組件的安裝部署&#xff0c;包括 Zookeeper、Hadoop、Hive、Spark 等。 版本 Zookeeper 3.5.6 3.8.4 3.9.3 初始化 包括主機名修改、SSH互…

    考研數據結構Part3——二叉樹知識點總結

    一、前言 二叉樹是一種特殊的樹形結構&#xff0c;每個節點最多有兩個子節點&#xff0c;分別稱為左子樹和右子樹。其特點是子樹有嚴格的左右之分&#xff0c;順序不可顛倒。從歷年真題來看&#xff0c;二叉樹的鏈式存儲實現、遍歷算法、屬性統計是高頻考點&#xff0c;常以選擇…

    網絡與信息安全有哪些崗位:(12)威脅分析師

    今天是七夕節&#xff0c;首先祝大家早遇良緣、有情人終成眷屬&#xff01;&#xff01;七夕節快樂、工作順利、學業有成~~ 想知道網絡與信息安全領域有哪些具體崗位嗎&#xff1f;此前我們已陸續介紹網絡安全工程師、滲透測試工程師、SOC 總監、SOC 工具運維工程師等核心角色&…

    mysql雙機熱備(主主模式)

    一、環境準備 主機名ip操作系統備注node01192.168.48.91CentOS Linux 7 (Core)mysql主庫node01192.168.48.92CentOS Linux 7 (Core)mysql主庫192.168.48.90漂移IP&#xff08;VIP&#xff09; centos7鏡像下載地址&#xff1a; https://mirrors.aliyun.com/centos/7.9.2009/…