来源
在讲解希尔排序之前,让我们先了解一下希尔排序的由来。其实希尔排序也是一种插入排序,它是由大神希尔发明的一种对简单插入排序的进行优化后的高级插入排序。也就是说希尔排序也是一种插入排序。让我们来了解一下简单排序存在的问题。
简单插入排序存在的问题
我们看简单的插入排序可能存在的问题.
数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1(最小), 这样的过程是:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
结论: 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响.
希尔排序的基本思想
希尔排序是怎么进行的优化的,也就是怎么使插入排序中较小的数经过更少次数的后移到达合适的位置。
请看专业解释:
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
如果你能看懂的话,算你厉害。反正我是没看懂,这里给出一些算法图片帮组你理解这个算法。
简单来说,在希尔排序中增加了一个叫做增量(gap)的东西,它的初始大小等于数组长度的一半,并且每次循环减少一半。它的作用就是对数组进行分组,将数组元素两两分组,并进行插入排序,这样的话就能在尽可能少的移动次数下完成排序操作。
具体实现
后来我们来进行具体的代码实现:
假设我们对数组arr[8,9,1,7,2,3,5,4,6,0], 进行希尔排序。根据前面所讲的知识。
- 先判断增量是否大于0
2)计算增量 = arr.length / 2 = 5
3)分组 8->3,9->5,1->4,7->6,2->0
3) 并且判断是否需要交换位置
第一轮
//根据length/2 = 5 ,步长 = 5
for(int i = 5; i<arr.length; i++) {
//如果当前元素大于加上步长后的那个元素,说明交换
for(int j=i-5;j>=0;j-=5) {
if(arr[j] > arr[j+5]) {
temp = arr[j];
arr[j]=arr[j+5];
arr[j+5]=temp;
}
}
}
System.out.println("希尔排序1轮后:"+Arrays.toString(arr));
第二轮
//步长 = 5/2 =2
for(int i = 2; i<arr.length; i++) {
for(int j=i-2;j>=0;j-=2) {
if(arr[j] > arr[j+2]) {
temp = arr[j];
arr[j]=arr[j+2];
arr[j+2]=temp;
}
}
}
System.out.println("希尔排序2轮后:"+Arrays.toString(arr));
第三轮
//步长 = 2/2 = 1
for(int i = 1; i<arr.length; i++) {
for(int j=i-1;j>=0;j-=1) {
if(arr[j] > arr[j+1]) {
temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
System.out.println("希尔排序3轮后:"+Arrays.toString(arr));
*/
运行结果
这里是我在elipse中的Java代码,懒得再敲一遍python。
这里给出python整合后的交换式希尔排序代码:
def shellSort(arr):
temp = 0
gap = len(arr)
while gap>1:
gap = int(gap/2)
for i in range(gap,len(arr),1):
for j in range(i-gap,-1,-gap):
if arr[j]>arr[j+gap]:
temp = arr[j]
arr[j] = arr[j+gap]
arr[j+gap] = temp
但是由于使用的是交换两个元素的值,类似于冒泡排序,时间效率很低。因此还需要改进成类似插入排序的先移动元素到合适位置、后插入元素的操作。
def shellSort2(arr):
temp = 0
gap = len(arr)
while gap>1:
gap = int(gap/2)
for i in range(gap,len(arr),1):
j = i
temp = arr[j]
if arr[j]<arr[j-gap]:
#先移位
while(j-gap>=0 and temp<arr[j-gap]):
arr[j] = arr[j-gap]
j -= gap
#后插入
arr[j] = temp
这是采用移动式交换位置的希尔排序代码。现在让我们来测试一下有没有提高时间效率。导入time 和 random 模块,创建一个有8000元素的随机数组。具体代码如下:
import time
import random
#arr = [8,9,1,7,2,3,5,4,6,0]
arr = [0 for i in range(8000)]
for i in range(8000):
arr[i]=random.randint(0,8000)
def shellSort(arr):
temp = 0
gap = len(arr)
while gap>1:
gap = int(gap/2)
for i in range(gap,len(arr),1):
for j in range(i-gap,-1,-gap):
if arr[j]>arr[j+gap]:
temp = arr[j]
arr[j] = arr[j+gap]
arr[j+gap] = temp
def shellSort2(arr):
temp = 0
gap = len(arr)
while gap>1:
gap = int(gap/2)
for i in range(gap,len(arr),1):
j = i
temp = arr[j]
if arr[j]<arr[j-gap]:
#先移位
while(j-gap>=0 and temp<arr[j-gap]):
arr[j] = arr[j-gap]
j -= gap
#后插入
arr[j] = temp
print("交换式希尔排序:")
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
shellSort(arr)
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
#print(arr)
print("插入式希尔排序:")
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
shellSort2(arr)
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
#print(arr)
运行结果如图:
可见,时间效率得到了很大的提高,由原先的9秒提升至不到1秒。好了分享完毕。