-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShellSort.java
More file actions
124 lines (114 loc) · 4.98 KB
/
ShellSort.java
File metadata and controls
124 lines (114 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* 插入排序----希尔排序:我们选择步长为: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)
* 注意所有排序都是从小到大排。
*/
public class ShellSort {
/**
* 排序算法的实现,对数组中指定的元素进行排序
* @param array 待排序的数组
* @param from 从哪里开始排序
* @param end 排到哪里
* @param c 比较器
*/
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 整个数组要排哪个元素止
* @param c 比较器
*/
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];
}
}
/**
* 测试
* @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 + " ");
}
}
}