希爾排序專欄

在排序算法的大家庭中,希爾排序(Shell Sort)以其獨特的 "分組插入" 思想占據著重要地位。它是對插入排序的創造性改進,通過引入 "增量分組" 策略,大幅提升了排序效率。本文將帶你深入理解希爾排序的工作原理,解析其 Java 實現代碼,并探討其時間復雜度特性。

什么是希爾排序?

希爾排序由計算機科學家 Donald Shell 于 1959 年提出,其核心思想是:將數組按一定間隔(增量)分為多個子序列,對每個子序列執行插入排序;然后逐步縮小間隔,重復上述過程;最后當間隔為 1 時,執行一次完整的插入排序,完成整個數組的排序

這種 "先宏觀調整,再微觀優化" 的策略,有效克服了插入排序在處理大規模逆序數組時的低效問題,讓元素能夠快速跨越較長距離移動到大致正確的位置。

希爾排序的 Java 實現

下面是一個完整的希爾排序實現代碼,我們將以此為基礎進行深入解析:

public class Shell {public static void main(String[] args) {Integer[] integers = new Integer[10];for (int i = 0; i < integers.length; i++) {integers[i] = (int) (Math.random() * 100);}System.out.println("排序前:");for (Integer integer : integers) {System.out.print(integer+" ");}sort(integers);System.out.println();System.out.println("排序后");for (Integer integer : integers) {System.out.print(integer+" ");}}public static void sort(Comparable[] a){//根據數組長度確定增長量的初始值int h = 1;while (h<a.length/2){h = 2*h+1;}//外層循環控制增長量while (h>=1){//內存循環將產出的數據與已排序的作比較for (int i = h; i < a.length; i++) {// 內層循環,將 a[i] 與前面間隔為 h 的元素比較for (int j = i; j >= h ; j -= h) {if ( greater(a[j - h], a[j])){exchange(a, j - h, j);}}}//增長量減半h = h/2;}}//比較v是否大于n;public static boolean greater(Comparable v,Comparable n){//調用comparable的方法//v大于n是返回 1,//v等于n時返回 0,//v小時n時返回 -1int i = v.compareTo(n);return i>0;}//交換方法public static void exchange(Comparable[] a,int i,int j){Comparable temp = a[i];a[i] = a[j];a[j] = temp;}}

希爾排序的工作原理詳解

讓我們逐步解析希爾排序的執行流程,理解其核心機制:

1. 增量序列的初始化

希爾排序的第一步是確定初始增量值 h:

int h = 1;
while (h < a.length / 2) {h = 2 * h + 1;
}

這段代碼生成的是2h+1 增量序列(1, 3, 7, 15, 31...),特點是每個增量都是前一個的 2 倍加 1。對于長度為 10 的數組,初始 h 值為 7(因為 7 < 10/2=5 不成立,最終 h=3? wait,這里計算有誤,正確計算應該是:當數組長度為 10 時,初始 h 從 1 開始:

  • 第一次循環:h=1 < 5 → h=2*1+1=3
  • 第二次循環:h=3 < 5 → h=2*3+1=7
  • 第三次循環:h=7 < 5 不成立,退出循環,初始 h=7

所以對于長度為 10 的數組,初始增量 h=7。

2. 多輪增量排序過程

希爾排序的核心是多輪不同增量的排序,每輪都包含三個層次的循環:

外層循環:控制增量變化
while (h >= 1) {// 排序邏輯...h = h / 2;  // 縮小增量
}

外層循環負責將增量 h 從初始值逐步縮小到 1,每輪排序后 h 都會減半(整數除法)。對于初始 h=7 的情況,增量變化序列為:7 → 3 → 1。

中層循環:遍歷待排序元素
for (int i = h; i < a.length; i++) {// 內層排序邏輯...
}

中層循環從索引 h 開始遍歷數組,確保我們處理的是每個子序列中除第一個元素外的所有元素。

內層循環:子序列插入排序
for (int j = i; j >= h; j -= h) {if (greater(a[j - h], a[j])) {exchange(a, j - h, j);}
}

這是希爾排序的關鍵部分,它對每個子序列執行插入排序:

  • j 從 i 開始,每次向前移動 h 步(在子序列內移動)
  • 比較當前元素與子序列中前一個元素(間隔 h)
  • 如果逆序則交換元素,直到找到正確位置

3. 實例演示:增量 h=3 時的排序過程

假設數組為[60, 30, 80, 40, 10, 90, 20, 50, 70](長度 9),當 h=3 時:

分組情況

數組按間隔 3 分為 3 個獨立子序列:

  • 第 1 組:[60, 40, 20](索引 0、3、6)
  • 第 2 組:[30, 10, 50](索引 1、4、7)
  • 第 3 組:[80, 90, 70](索引 2、5、8)
子序列排序過程(以第 1 組為例)
  1. 處理 i=3(元素 40):

    • j=3,比較 a [0](60)和 a [3](40)
    • 60>40,交換后組變為[40, 60, 20]
  2. 處理 i=6(元素 20):

    • j=6,比較 a [3](60)和 a [6](20)→ 交換,組變為[40, 20, 60]
    • j=3,比較 a [0](40)和 a [3](20)→ 交換,組變為[20, 40, 60]

經過本輪排序,第 1 組已完全有序。其他組也會執行相同邏輯,最終整個數組會更接近有序狀態。

希爾排序的性能分析

時間復雜度

希爾排序的時間復雜度分析較為復雜,它高度依賴于增量序列的選擇:

增量序列最壞情況時間復雜度平均情況時間復雜度
原始希爾增量(h/2)O(n2)O(n^1.5)
Hibbard 增量(2^k-1)O(n^(3/2))O(n^(5/4))
Knuth 增量(3h+1)O(n^(3/2))O(n log2n)
Sedgewick 增量O(n^1.3)O(n^1.2)

我們實現中使用的 2h+1 增量序列(類似 Knuth 增量)在最壞情況下的時間復雜度約為 O (n^(3/2)),遠好于插入排序的 O (n2)。

空間復雜度

希爾排序是一種原地排序算法,僅需要常數級別的額外空間:

  • 幾個變量用于控制循環(h、i、j)
  • 一個臨時變量用于元素交換

因此,希爾排序的空間復雜度為O(1)

穩定性

希爾排序是不穩定的排序算法,因為在不同增量的排序過程中,相等元素的相對位置可能會發生變化。

希爾排序的優化與應用場景

優化方向

  1. 選擇更優的增量序列:Sedgewick 增量序列在實際應用中性能更優,但實現稍復雜
  2. 添加提前退出機制:在內層循環中,當元素找到正確位置后可立即退出
    if (greater(a[j - h], a[j])) {exchange(a, j - h, j);
    } else {break;  // 已找到正確位置,提前退出
    }
    
  3. 緩存增量計算結果:對于大型數組,可預計算增量序列并存入數組

適用場景

希爾排序特別適合:

  • 中等規模的數據集(n=1000~10000)
  • 對空間復雜度要求嚴格的場景(O (1) 空間)
  • 嵌入式系統或內存受限的環境
  • 作為更復雜排序算法的子過程

在 Java 的 Arrays.sort () 方法中,希爾排序曾被用于處理中等規模的數組排序。

總結

希爾排序通過 "增量分組 + 插入排序" 的創新思想,成功突破了插入排序在大規模數據上的性能瓶頸。其核心優勢在于:

  1. 實現簡單,代碼簡潔易懂
  2. 原地排序,空間復雜度低(O (1))
  3. 對部分有序的數據效率更高
  4. 性能優于其他簡單排序算法(插入、選擇、冒泡)

本文解析的實現代碼使用 2h+1 增量序列,通過三層循環結構清晰地實現了希爾排序的核心邏輯。理解希爾排序的工作原理,不僅能幫助你在實際開發中選擇合適的排序算法,更能啟發你對算法優化思想的思考 —— 通過巧妙的策略調整,即使是簡單算法也能獲得顯著的性能提升。

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

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

相關文章

Android 歐盟網絡安全EN18031 要求對應的基本表格填寫

Android 歐盟網絡安全EN18031 要求對應的基本表格填寫 文章目錄Android 歐盟網絡安全EN18031 要求對應的基本表格填寫一、背景二、18031認證預填表格三、其他1、Android EN 18031 要求對應的基本表格小結2、EN 18031的要求表格內容填寫3、一定要做三方認證&#xff1f;4、歐盟網…

《Attention-driven GUI Grounding》論文精讀筆記

論文鏈接&#xff1a;[2412.10840] Attention-driven GUI Grounding: Leveraging Pretrained Multimodal Large Language Models without Fine-Tuning 摘要 近年來&#xff0c;多模態大型語言模型&#xff08;Multimodal Large Language Models&#xff0c;MLLMs&#xff09;的…

PIDGenRc函數中lpstrRpc的由來和InitializePidVariables函數的關系

第一部分&#xff1a;./base/ntsetup/syssetup/setupp.h:404:#define MAX_PID30_RPC 5BOOL InitializePidVariables() {//// Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid//Error RegOpenKeyEx( HKEY_LOCAL_MACHINE,((MiniSetup || OobeSetup) ? szFinalPidKeyNa…

Nginx學習筆記(七)——Nginx負載均衡

?? Nginx學習筆記&#xff08;七&#xff09;——Nginx負載均衡 &#x1f4cc; 一、負載均衡核心概念 架構定位&#xff1a; #mermaid-svg-00aCvwmJ40DHNd66 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-00aC…

MQ積壓如何處理

處理消息隊列&#xff08;MQ&#xff09;積壓是一個需要系統化分析的運維挑戰。下面我將結合常見原因&#xff0c;分步驟說明處理方案&#xff0c;并區分應急措施和根本解決方案&#xff1a;?一、快速診斷積壓原因&#xff08;核心&#xff01;&#xff09;???監控告警分析…

Unity與OpenGL中的材質系統詳解

引言 在現代3D圖形開發中&#xff0c;材質是定義物體外觀的核心元素。Unity引擎提供了強大且直觀的材質系統&#xff0c;使得開發者能夠輕松實現復雜的視覺效果。然而&#xff0c;對于熟悉OpenGL的開發者來說&#xff0c;理解Unity材質系統的工作原理以及如何在OpenGL中實現類…

k8s安裝DragonflyDB取代redis

數據庫類型線程模型吞吐量 (QPS)延遲 (μs)內存效率適用場景兼容性Memcached純內存鍵值存儲多線程100K - 500K10 - 100高緩存、會話存儲無原生密碼認證DragonflyDB多協議內存數據庫多線程1M50 - 200中高高吞吐緩存、Redis 替代兼容 RedisKeyDBRedis 多線程分支多線程500K - 1M5…

Horse3D游戲引擎研發筆記(五):在QtOpenGL環境下,仿three.js的BufferGeometry管理VAO和EBO繪制四邊形

一、背景介紹 在三維圖形渲染中&#xff0c;幾何形狀的管理是引擎的核心功能之一。Three.js通過BufferGeometry接口實現了對頂點數據和索引數據的高效管理&#xff0c;而OpenGL則通過頂點數組對象&#xff08;VAO&#xff09;和元素數組對象&#xff08;EBO&#xff09;來實現…

Ping32 與 IP-GUARD 深度對比:Ping32,引領企業數據安全新方向

在數字化時代&#xff0c;企業數據宛如珍貴的寶藏&#xff0c;是推動業務發展、保持競爭優勢的核心資產。但與此同時&#xff0c;數據安全威脅也如影隨形&#xff0c;內部員工的誤操作、惡意竊取&#xff0c;外部黑客的攻擊&#xff0c;都可能讓企業數據面臨泄露風險&#xff0…

洛谷 P2842 紙幣問題 1 -普及-

題目描述 某國有 nnn 種紙幣&#xff0c;每種紙幣面額為 aia_iai? 并且有無限張&#xff0c;現在要湊出 www 的金額&#xff0c;試問最少用多少張紙幣可以湊出來&#xff1f; 輸入格式 第一行兩個整數 n,wn,wn,w&#xff0c;分別表示紙幣的種數和要湊出的金額。 第二行一行 nn…

第十四節:物理引擎集成:Cannon.js入門

第十四節&#xff1a;物理引擎集成&#xff1a;Cannon.js入門 引言 物理引擎為3D世界注入真實感&#xff0c;讓物體遵循重力、碰撞和動量等物理規律。Cannon.js是Three.js生態中最強大的物理引擎之一&#xff0c;本文將深入解析其核心機制&#xff0c;并通過Vue3實現物理沙盒系…

二十四、Mybatis-基礎操作-刪除(預編譯SQL)

mybatis環境準備概述與注意事項&#xff08;springboot項目引入三項必要的起步依賴&#xff09;項目目錄結構mybatis基礎操作-刪除對應EmpMapper&#xff08;接口&#xff09;代碼 package com.itheima.mapper;import org.apache.ibatis.annotations.*;Mapper public interface…

JavaScript 核心基礎:類型檢測、DOM 操作與事件處理

JavaScript 作為松散類型語言&#xff0c;掌握類型檢測規則、DOM 元素獲取方式及事件處理邏輯&#xff0c;是寫出健壯代碼的基礎。本文系統梳理 JS 高頻基礎知識點&#xff0c;結合實戰場景解析原理與用法&#xff0c;幫你建立清晰的知識框架。 一、JS 數據類型與類型檢測&…

軟件開發過程中的維護活動

軟件開發過程中的維護活動軟件維護是軟件生命周期中持續時間最長、成本最高的階段&#xff0c;它并非簡單的“修理”&#xff0c;而是一系列旨在延長軟件生命周期、保持其價值和適應性的工程化活動。研究表明&#xff0c;軟件維護成本可占總成本的60%以上。理解并有效管理維護活…

STC8單片機驅動I2C屏幕:實現時間、日期與溫濕度顯示

STC8 單片機驅動 I2C 屏幕&#xff1a;實現時間、日期與溫濕度顯示 在單片機項目中&#xff0c;“數據可視化” 是核心需求之一 —— 將時間、溫濕度等關鍵信息實時顯示在屏幕上&#xff0c;能讓項目更具實用性。本文以STC8 系列單片機為核心&#xff0c;搭配 I2C 接口的 OLED…

基于SpringBoot+Vue的智能消費記賬系統(AI問答、WebSocket即時通訊、Echarts圖形化分析)

&#x1f388;系統亮點&#xff1a;AI問答、WebSocket即時通訊、Echarts圖形化分析&#xff1b;一.系統開發工具與環境搭建1.系統設計開發工具后端使用Java編程語言的Spring boot框架 項目架構&#xff1a;B/S架構 運行環境&#xff1a;win10/win11、jdk17前端&#xff1a; 技術…

[論文筆記] WiscKey: Separating Keys from Values in SSD-Conscious Storage

閱讀 WiscKey 論文時隨手記錄一些筆記。 這篇論文的核心思想理解起來還是很簡單的&#xff0c;但是具體涉及到實現還有一些想不明白的地方&#xff0c;后來看到 TiKV 的 Titan 實現也很有趣&#xff0c;索性把這些問題都記錄下來并拋出來。 本文中和論文相關的內容&#xff0…

week1-[循環嵌套]畫正方形

week1-[循環嵌套]畫正方形 題目描述 輸入一個正整數 nnn&#xff0c;請使用數字 000 到 999 拼成一個這樣的正方形圖案&#xff08;參考樣例輸入輸出&#xff09;&#xff1a;由上至下、由左至右依次由數字 000 到 999 填充。每次使用數字 999 填充后&#xff0c;將從頭使用數字…

在 Vue2 中使用 pdf.js + pdf-lib 實現 PDF 預覽、手寫簽名、文字批注與高保真導出

本文演示如何在前端&#xff08;Vue.js&#xff09;中結合 pdf.js、pdf-lib 與 Canvas 技術實現 PDF 預覽、圖片簽名、手寫批注、文字標注&#xff0c;并導出高保真 PDF。 先上demo截圖&#xff0c;后續會附上代碼倉庫地址&#xff08;目前還有部分問題暫未進行優化&#xff0…

tomcat 定時重啟

tomcat 定時重啟 定時重啟的目的是:修復內存泄漏等問題,tomcat 長時間未重啟,導致頁面卡頓,卡死,無法訪問,影響用戶訪問 1.編寫腳本 su - tomcat [tomcat@u1abomap02 ~]$ ls restart_tomcat_gosi.sh tomcat_gosi.log vi restart_tomcat_gosi.sh #!/bin/bash# 定義日志目…