插入排序
插入排序
基本思想:
每步将一个待排序的对象,按其排序码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
常用算法:
- 直接插入排序
- 折半插入排序(二分法)
- 希尔排序(缩小增量排序):Shell
直接插入排序
基本思想
每步将一个待排序的元素,按其排序码大小,插入到前面已经排好序的一组元素的适当位置上, 直到元素全部插入为止。
基本方法
当插入第i (i≥1) 个元素时,前面的V[0], V[1], …, V[i-1]已经排好序。这时,用V[i]的排序码与V[i-1], V[i-2], …的排序码顺序进行比较,插入位置即将V[i]插入,原来位置上的元素向后顺移。
复杂度和稳定性
时间复杂度:Ω (n)和O(n2)
空间复杂度:O(1)
稳定性:稳定
代码
static void insertSort(int[] nums){
for (int i = 1; i < nums.length; i++){
if (nums[i] < nums[i - 1]){
int tmp = nums[i];
int j = i - 1;
do{
nums[j + 1] = nums[j];
j--;
}while(j >= 0 && tmp < nums[j]);
nums[j + 1] = tmp;
}
}
}
折半插入排序
基本思想
设在顺序表中有一 个元素序列 V[0], V[1], …, V[n-1]。其中, V[0], V[1], …, V[i-1] 是已经排好序的元素。在插入V[i] 时, 利用折半搜索法寻找V[i] 的插入位置。
复杂度和稳定性
时间复杂度:Ω (n)和O(n2)
空间复杂度:O(1)
稳定性:稳定
折半插入和直接插入复杂度一样,但是折半插入一般情况下排序码比较次数较少,性能一般比直接排序快。
代码
static void binaryInsertSort(int[] nums) {
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
int tmp = nums[i];
int low = 0;
int high = i - 1;
while (low <= high) {
int mid = (low + high) / 2;
//最终nums[low]刚好>=tmp
if (nums[mid] < tmp)
low = mid + 1;
else
high = mid - 1;
}
for (int j = i; j > low; j--) {
nums[j] = nums[j - 1];
}
nums[low] = tmp;
}
}
}
希尔排序
基本思想
设待排序元素序列有 n 个元素, 首先取一个整数 gap < n 作为间隔,将全部元素分为 gap 个子序列,所有距离为 gap 的元素放在同一个子序列中,在每一个子序列中分别施行直接插入排序。
然后缩小间隔 gap, 例如取 gap = gap/2 + 1,重复上述的子序列划分和排序工作。直到最后取 gap == 1,将所有元素放在同一个序列中排序为止。
复杂度和稳定性
Knuth利用大量实验统计资料得出 : 当 n 很大时,排序码平均比较次数和元素平均移动次数大约在 n1.25 到 1.6*n1.25 的范围内。这是在利用直接插入排序作为子序列排序方法的情况下得到的。
稳定性:不稳定
代码
static void shellSort(int[] nums){
int gap = nums.length;
do{
gap = gap/3 + 1;
for (int i = gap; i < nums.length; i++){
if (nums[i] < nums[i - gap]){
int tmp = nums[i];
int j = i - gap;
do{
nums[j + gap] = nums[j];
j -= gap;
}while(j >= 0 && tmp < nums[j]);
nums[j + gap] = tmp;
}
}
}while (gap > 1);
}