希爾排序 一步步講解思路

2021-10-08 18:38:53 字數 4173 閱讀 8895

在理解希爾排序之前,我們先來看看什麼是插入排序。假設有乙個陣列要我們排序。為了方便理解,我們新建乙個陣列,這個陣列中的元素是已經排序好的。(如果理解不了,你就將他當為乙個陣列好了)。然後我們從要排序陣列中任意選取乙個數字(為了方便知曉陣列排序完畢,從陣列的第0項按順序選取)

步驟:第一輪:

待排序:

已排序:{}

從待排序中選取 6 放入到已排序陣列中的合適位置

第二輪:

待排序:

已排序:

從待排序中選取 4 放入到已排序陣列中的合適位置(為了方便檢視,先把待排序陣列中的6遮蔽)

第三輪:

待排序:

已排序:

從待排序中選取 3 放入到已排序陣列中的合適位置

第三輪:

待排序:

已排序:

從待排序中選取 5 放入到已排序陣列中的合適位置

最後一輪:

待排序:

已排序:

從待排序中選取 2 放入到已排序陣列中的合適位置

待排序:{}

已排序:

完成了

def

inser_sort

(nums)

:# 定義 已經排序好陣列的指標 和 尚未排序陣列的指標

sorted=0

unsorted =

1 length =

len(nums)

# 執行條件

while

sorted

< length and unsorted < length:

# 將尚未排序的元素挨個跟已排序數列中的元素比較

for i in

range(0

,sorted+1

):# 找到合適位置

if nums[i]

>= nums[unsorted]

:# 將未排序數字從後往前交換到已排序數列中的合適位置

for j in

range

(unsorted, i,-1

):nums[j]

, nums[j -1]

= nums[j -1]

, nums[j]

sorted+=1

unsorted +=

1return nums

if __name__ ==

"__main__"

: test =[3

,4,2

,1,6

,7,-

10]print

(inser_sort(test)

)

在將想法轉化為**的時候,其實是不用新建乙個陣列的,我們將總數列分成左數列和右數列。上文提及的已排序陣列是左數列,未排序則是右數列。**中#將尚未排序的元素挨個跟已排序數列中的元素比較就是步驟中放入到已排序陣列中的合適位置的體現。找到了就將他插入到這個位置,但我們都知道,陣列是不能執行插入操作的。為了在**中實現插入這個操作,我選擇使用多次交換實現插入(當然有其他方法,比如先將陣列向後移動,騰出乙個位置,然後再賦值)。比如總數組是[2,4,5,3],我們做的是:左陣列[2,4,5] 要將右陣列中的 3 插入其中。

步驟一:先計算出要交換的次數。

3到合適位置隔著乙個數字,所以要交換兩次。

步驟二:交換操作

3與5互換 [2,4,3,5]

然後是,3與4互換 [2,3,4,5]

搞定了。

希爾排序是比較」高階「的插入排序。思考一下,如果按照我們之前的演算法,將[5,4,3,2,1]排序為正序會怎麼樣,1要插入到最前面,要移動4次。倘若陣列很大,移動所花費的時間是巨大的。要是1直接跟第0項互換就好了,直接飛過去,而不是一步步換過去。

我們插入排序中,是為了方便記錄什麼時候停止,所以在按部就班按照順序,選取元素進行排序。但希爾排序不是了,他通過將數列進行分組,然後在使用插入排序,一定程度上為了減少的移動的次數。

希爾排序比插入排序就多了乙個東西,那就是分組:

拿乙個數枚舉例:[6,4,3,5,1,2]

第一步:將數列分成 6/2 = 3 組:[6, 5],[4, 1],[3,2]

第二步:對每乙個數列都使用插入排序(其實當元素只有兩個的時候,用什麼方法都差不多)

完成後總數列就為 [5,1,2,6,4,3]。(你瞅,如果按照之前的方法,位於末尾的2要移動4步才能到最終位置,但現在2離最終位置只有1步之遙)

第三步:將陣列分為兩組:[5,2,4]與[1,6,3](其實這裡的也可以一步到胃 一步到位,直接對總數列使用插入排序,因為我們之前分成3組,3/2 = 1,直接使用插入排序也是可以的)

第四步:再對每乙個數列使用插入排序[2,4,5]與[1,3,6],然後對映到總數列中[2,1,4,3,5,6]

第五步:不分組了,直接對總數列使用插入排序。

得到答案[1,2,3,4,5,6]

上述的分組,是看你想分成幾組就幾組,但這個分組情況會影響到演算法的執行效率,然而如何分合適的組,這是乙個深奧的話題,我也不清楚。一般而言,我是先將算出初始組數(陣列的長度 / 2取整),然後在將組數- 1 ,直到組數為1結束。

# 插入排序演算法

definser_sort

(nums, start, grid)

:# 定義 已經排序好陣列的指標 和 尚未排序陣列的指標

sorted

= start

unsorted = start + grid

length =

len(nums)

# 執行條件

while

sorted

< length and unsorted < length:

# 將尚未排序的元素挨個跟已排序數列中的元素比較

for i in

range

(start, unsorted, grid)

:# 找到合適位置

if nums[i]

>= nums[unsorted]

:# 將未排序數字從後往前交換到已排序數列中的合適位置

for j in

range

(unsorted, i,

-grid)

: nums[j]

, nums[j - grid]

= nums[j - grid]

, nums[j]

sorted

+= grid

unsorted += grid

return nums

# 希爾排序

defshell_sort

(nums)

: length =

len(nums)

# num_group 為 組數

num_group =

int(length /2)

# grid 為 元素間隔

grid = num_group

while num_group >=1:

# 對小組使用插入排序

for i in

range

(num_group)

: inser_sort(nums, i, grid)

print

(nums)

# 分組數-1

num_group -=

1# grid 為 元素間隔

grid = num_group

return nums

if __name__ ==

"__main__"

: test =[6

,4,3

,5,1

,2] shell_sort(test)

輸出結果為

[5,

1,2,

6,4,

3][2

,1,4

,3,5

,6][

1,2,

3,4,

5,6]

希爾演算法跟插入演算法的唯一區別就是多個了分組。思路分析中要將陣列分組,然後對各組使用插入演算法,我們如何用**實現呢?修改一下我們之前的插入演算法即可,其實修改的很少,將原來的間距從1修改為grid,然後開始的元素指標從0修改為start,這樣我們就可以實現在總數組中的分陣列進行插入排序了。當grid=1,start=0時,就是我們之前的插入演算法**。還有乙個問題如何表達我們的陣列分組呢?只需要知道該陣列的開始以及間隔就可以確定了這個分陣列。

一步步學ROS

最近因為看svo的 裡面用到catkin決定要好好看ros,年前學會基本操作。啟動節點 rosrun package name executable name 檢視節點 rosnode list 注 rosout 節點是乙個特殊的節點,通過 roscore 自動啟動 檢視特定節點的資訊 rosnod...

windows Thrift c 一步步搭建

1.thrift 原始碼路徑 2.libevent原始碼路徑 3.boost路徑 安裝 conan install boost 1.68.0 conan stable 4.openssl路徑 安裝 conan install openssl 1.1.1a conan stable conan安裝bo...

一步步啟動linux

可以一步一步啟動linux.在ubantu剛一啟動時,按c健即進入grub 提示符狀態,在此狀態下輸入 我用的是ubuntu 13 grub linux vmlinuz grub ls boot grub initrd boot initrd.img 3.11.0 15 generic grub b...