希尔排序(Shell Sort)
插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。
希尔排序基本思想:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序
;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法。
给定实例的shell排序的排序过程
假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
5,3,1
缩小增量法
属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序
排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止
初始:d=5
49 38 65 97 76 13 27 49* 55 04
49 13
|-------------------|
38 27
|-------------------|
65 49*
|-------------------|
97 55
|-------------------|
76 04
|-------------------|
一趟结果
13 27 49* 55 04 49 38 65 97 76
d=3
13 27 49* 55 04 49 38 65 97 76
13 55 38 76
|------------|------------|------------|
27 04 65
|------------|------------|
49* 49 97
|------------|------------|
二趟结果
13 04 49* 38 27 49 55 65 97 76
d=1
13 04 49* 38 27 49 55 65 97 76
|----|----|----|----|----|----|----|----|----|
三趟结果
04 13 27 38 49* 49 55 65 76 97
算法分析:
不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法,
在此算法基础之上增加了一个新的特性,提高了效率。希尔排序的时间复杂度为 O(N*(logN)2), 没有快速排序算法快
O(N*(logN)),因此中等大小规模表现良好,对规模非常大的数据排序不是
最优选择。但是比O(N2)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。
此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏 的情况下执行的效率会非常差。
专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快, 再改成快速排序这样更高级的排序算法.
本质上讲,希尔排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当N值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。
当N值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。
稳定性:
稳定性
:
排序前一个序列中,如果出现N个与关键字相同的数据,那么排序后仍然按照原先序列的排列顺序排列,就说这个算法是稳定的,反之就是不稳定的。通俗地讲就是
能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj,
Ai原来在位置前,排序后Ai还是要在Aj位置前。
希尔分析
:
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,
插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元
素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
Java 代码实现:
package test.suanfa;
/**
* 插入排序----希尔排序:我们选择步长为:15,7,3,1 我们选择步长公式为:2^k-1,2^(k-1)-1,……,15,7,3,1
* (2^4-1,2^3-1,2^2-1,2^1-1) 注意所有排序都是从小到大排。
*
* @author TSW
*/
public class ShellSort {
/**
* 测试
* @param args
*/
public static void main(String[] args) {
Integer[] intgArr = { 5, 9, 1, 4, 8, 2, 6, 3, 7, 0 };
ShellSort shellSort = new ShellSort();
shellSort.sort(intgArr, 0, intgArr.length - 1);
for (Integer intObj : intgArr) {
System.out.print(intObj + " ");
}
}
/**
* 排序算法的实现,对数组中指定的元素进行排序
* @param array 待排序的数组
* @param from 从哪里开始排序
* @param end 排到哪里
*/
public void sort(Integer[] array, int from, int end) {
// 初始步长,实质为每轮的分组数
int step = initialStep(end - from + 1);
// 第一层循环是对排序轮次进行循环。(step + 1) / 2 - 1 为下一轮步长值
for (; step >= 1; step = (step + 1) / 2 - 1) {
// 对每轮里的每个分组进行循环
for (int groupIndex = 0; groupIndex < step; groupIndex++) {
// 对每组进行直接插入排序
insertSort(array, groupIndex, step, end);
}
}
}
/**
* 直接插入排序实现
* @param array 待排序数组
* @param groupIndex 对每轮的哪一组进行排序
* @param step 步长
* @param end 整个数组要排哪个元素止
*/
public void insertSort(Integer[] array, int groupIndex, int step, int end) {
int startIndex = groupIndex;// 从哪里开始排序
int endIndex = startIndex;// 排到哪里
/*
*
* 排到哪里需要计算得到,从开始排序元素开始,以step步长,可求得下元素是否在数组范围内,
*
* 如果在数组范围内,则继续循环,直到索引超现数组范围
*/
while ((endIndex + step) <= end) {
endIndex += step;
}
// i为每小组里的第二个元素开始
for (int i = groupIndex + step; i <= end; i += step) {
for (int j = groupIndex; j < i; j += step) {
Integer insertedElem = array[i];
// 从有序数组中最一个元素开始查找第一个大于待插入的元素
if ((array[j].compareTo(insertedElem)) >= 0) {
// 找到插入点后,从插入点开始向后所有元素后移一位
move(array, j, i - step, step);
array[j] = insertedElem;
break;
}
}
}
}
/**
* 根据数组长度求初始步长
*
* 我们选择步长的公式为:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 减一即为该步长序列,k 为排序轮次
*
* 初始步长:step = 2^k-1
* 初始步长约束条件:step < len - 1 初始步长的值要小于数组长度还要减一的值
* (因 为第一轮分组时尽量不要分为一组,除非数组本身的长度就小于等于4)
*
* 由上面两个关系试可以得知:2^k - 1 < len - 1 关系式,其中k为轮次,如果把2^k 表 达式
* 转换成step 表达式,则2^k-1 可使用(step + 1)*2-1 替换(因为step+1 相当于第k-1
* 轮的步长,所以在step+1 基础上乘以2 就相当于2^k 了),即步长与数组长度的关系不等式为
* (step + 1)*2 - 1 < len -1
*
* @param len 数组长度
* @return
*/
public static int initialStep(int len) {
/*
*
* 初始值设置为步长公式中的最小步长,从最小步长推导出最长初始步长值,即按照以下公式来推:
*
* 1,3,7,15,...,2^(k-1)-1,2^k-1
*
* 如果数组长度小于等于4时,步长为1,即长度小于等于4的数组不用分组,此时直接退化为直接插入排序
*/
int step = 1;
// 试探下一个步长是否满足条件,如果满足条件,则步长置为下一步长
while ((step + 1) * 2 - 1 < len - 1) {
step = (step + 1) * 2 - 1;
}
System.out.println("初始步长: " + step);
return step;
}
/**
* 以指定的步长将数组元素后移,步长指定每个元素间的间隔
* @param array 待排序数组
* @param startIndex 从哪里开始移
* @param endIndex 到哪个元素止
* @param step 步长
*/
protected final void move(Integer[] array, int startIndex, int endIndex, int step) {
for (int i = endIndex; i >= startIndex; i -= step) {
array[i + step] = array[i];
}
}
}
分享到:
相关推荐
本文将详细探讨标题所提及的几种排序算法:合并排序、插入排序、希尔排序、快速排序、冒泡排序以及桶排序,并结合Java语言的实现进行解析。 1. **合并排序(Merge Sort)**: 合并排序是一种基于分治策略的排序算法...
经典排序算法 - 希尔排序Shell sort 经典排序算法 - 堆排序Heap sort序 经典排序算法 - 地精排序Gnome Sort 经典排序算法 - 奇偶排序Odd-even sort 经典排序算法 - 梳排序Comb sort 经典排序算法 - 耐心排序...
在提供的文件中,我们可以看到有四种经典的排序算法的Java实现:插入排序、冒泡排序、选择排序以及希尔排序。 **插入排序**: 插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据...
这八大排序算法包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序以及计数排序。 1. 冒泡排序(Bubble Sort): 冒泡排序是最简单的排序算法之一,通过不断地交换相邻两个元素的位置,使较...
在上面的代码中,shellSort 函数接受一个整数数组作为输入,并使用希尔排序算法对其进行排序。外部循环使用一个间隔变量 gap,初始值为数组长度的一半,每次循环将 gap 除以 2,直到 gap 为 1。内部循环从第 gap 个...
ShellSort,又称希尔排序,是由美国计算机科学家Donald Shell于1959年提出的一种改进的插入排序算法。它通过将待排序的元素按照一定的间隔分组,然后对每组进行插入排序,随着间隔逐渐减小,最终达到整体有序。这种...
本资源包含了多种经典的排序算法实现,包括选择排序、冒泡排序、希尔排序、插入排序和归并排序。下面将详细介绍这些排序算法及其原理。 1. **选择排序(Selection Sort)** - **基本思想**:每次遍历数组,找到...
根据提供的文件信息,我们可以归纳总结出以下几个主要的排序算法及其JAVA代码实现: ### 1. 插入排序(Insert Sort) 插入排序是一种简单的排序方法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序...
6. 希尔排序(Shell Sort) 希尔排序是插入排序的一种更高效的改进版本,通过将待排序的数组元素按某个增量分组,然后对每组使用直接插入排序算法排序。随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时...
以上介绍了四种常见的排序算法:插入排序、冒泡排序、选择排序以及希尔排序,并给出了它们在Java中的具体实现。这四种算法各有特点,适用于不同的应用场景。插入排序适合部分有序的数据集;冒泡排序虽然简单但效率较...
在C++编程中,实现各种排序算法能够帮助理解它们的工作原理,并且可以对比不同算法在不同情况下的性能。以下是几种常见的排序算法的详细说明: 1. 直接插入排序(Insertion Sort): 直接插入排序是一种简单的排序...
本文将详述Java语言实现的六种经典排序算法:冒泡排序、选择排序、插入排序、归并排序、希尔排序以及快速排序。这些排序算法各有特点,适用于不同的场景。 1. 冒泡排序(Bubble Sort) 冒泡排序是一种简单的排序...
在计算机科学中,排序算法是数据处理的重要组成部分,主要用于对一组数据进行有序排列。...同时,Java的Collections.sort()和Arrays.sort()方法在底层使用了高效的排序算法,通常无需手动实现排序,除非有特殊需求。
希尔排序(Shell Sort)是一种基于插入排序的快速排序算法,由Donald Shell于1959年提出。它的主要思想是将待排序的数据按照一个增量序列分成若干个子序列,然后对每个子序列进行插入排序,最后再进行一次全局的插入...