插入排序是把序列的首个元素看做是有序的序列,然后把序列后面的元素往这个有序序列插入,其增量是1
希尔排序
希尔排序是直接插入排序的升级版‘
插入排序:就是在排序的过程中,把数组的每一个元素按照大小关系,插入到前面有序区的对应位置。
插入排序的评价时间复杂度是O(n^2),它的特点是:
- 在大多数元素已经有序的情况下,插入排序的工作量较小,甚至可以达到线性排序的效果
这个结论很明显,如果一个数组大部分元素都有序,那么数组中的元素自然不需要频繁地进行比较和交换。
- 在元素数量较少的情况下,插入排序的工作量较小
这个结论更加显而易见,插入排序的工作量和n的平方成正比,如果n比较小,那么排序的工作量自然要小得多。
我们可以先对原始数据进行预先处理,提高数据的有序程度:逐步分组进行粗调,然后直接插入排序,即希尔排序
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
C++
#include <iostream>
#include <list>
#include <vector>
#include <map>
template<typename T>
std::ostream& print(std::ostream &out,T const &val) {
return (out << val << " ");
}
template<typename T1,typename T2>
std::ostream& print(std::ostream &out,std::pair<T1,T2> const &val) {
return (out << "{" << val.first << " " << val.second << "} ");
}
template<template<typename,typename...> class TT,typename... Args>
std::ostream& operator<<(std::ostream &out,TT<Args...> const &cont) {
for(auto&& elem : cont) print(out,elem);
out << "\n";
return out;
}
void swap(int & i, int &j){
int t = i;
i = j;
j = t;
}
void insertSort(std::vector<int> &vec, int gap){
if(vec.empty() || vec.size() == 1){
return;
}
for(int i = gap; i < vec.size(); i++){
int backup = vec[i];
int pos = i - gap;
while (pos > -1 && backup < vec[pos]){
vec[pos + gap] = vec[pos];
pos = pos - gap;
}
vec[pos + gap] = backup;
}
}
void shell(std::vector<int> &vec){
if(vec.empty() || vec.size() == 1){
return;
}
for(int gap = vec.size() / 2; gap > 0; gap = gap / 2){
insertSort(vec, gap);
}
}
int main() {
std::vector<int> vec = {1,9,2,8,3,7,4,6,5,10, -1};
shell(vec);
std::cout << vec;
return 0;
}
golang
单线程
package main
import "fmt"
func ShellSort(arr []int)([]int) {
length := len(arr)
if length <= 1{
return arr
}else{
for gap := length / 2; gap > 0 ; gap = gap / 2 {
ShellSortStep(arr, gap);
}
return arr
}
}
func ShellSortStep(arr[]int, gap int) []int {
length := len(arr)
for i := gap ; i < length; i ++ {
backup := arr[i]
pos := i - gap
for pos > -1 && arr[pos] > backup {
arr[pos + gap] = arr[pos]
pos = pos - gap
}
arr[pos + gap] = backup
}
return arr
}
func main() {
arr := []int{23,19,81,79,89,83,17,48,55,26,16,1,46,95,10}
//fmt.Println(InsertSort(arr))
fmt.Println(ShellSort(arr))
}
func InsertSort(arr []int)[]int {
length := len(arr)
if length <= 1{
return arr
}else{
for i := 1 ; i < length; i++ {
backup := arr[i]
pos := i - 1
for pos > -1 && arr[pos] > backup {
arr[pos + 1] = arr[pos]
pos = pos - 1
}
arr[pos + 1] = backup
}
return arr
}
}
步长为2时java实现
实现1
package com.company;
public class Test_stack {
public static void ShellSort2(int []array){
// 逐步缩小步长,直至步长为0[也就是不能再进行分区了]
for (int step = array.length/2; step > 0; step = step / 2){
// 比较每个分组,直到比较完成:每一次步长改变,都是从0开始. 如果要开始下一组,直接i + 1即可开始下一组比较
for (int i = 0; i + step < array.length; i++){ // 切换分组
// 索引 [0, step], [step, step * 2], [step*3, step * 4] 直到 step * n >
// 这是插入排序
for ( int j = i + step; j < array.length && array[j] < array[j - step]; j = j + step){ // 比较每一个分组
if (array[j] < array[j - step]){ // 比较元素的前一个
int temp = array[j];
array[j] = array[j - step];
array[j - step] = temp;
}
}
}
// 因为它是分区排序,只能保证部分排序。所以不能只比较索引大的就能保证之前的索引位置都是有序的:因此需要从0开始
}
}
public static void main(String[] args) {
// int arrry[] = {1, 11, 22, 33, 45, 11, 66, 8, 10};
int[] arrry = { 13,14,94,33,82,25,59,94,65,23,45,27,73,25,39,10};
ShellSort2(arrry); // 传送的是地址
for (int i:arrry) {
System.out.print(i + "\t");
}
}
}
效率分析:虽然希尔排序是三层循环,最外层循环为log2(n)数量级,中间for循环是n数量级的,内循环远远低于n数量级,因为当分组较多时,组内元素较少;此循环次数少;当分组较少时,组内元素较多,但是此时已经接近有序,循环次数并不增长。因此,希尔排序的时间复杂度在O(nlog2(n))与O(n^2)
之间,大致为O(n^1.3)。
由于希尔排序对每个子序列单独比较,在比较时进行元素移动,有可能改变相同排序码元素的原始顺序。因此希尔排序时不稳定的。
改进二
public static int[] shellSort(int[] arr) {
if (arr == null || arr.length < 2){
return arr;
}
for (int step = arr.length / 2; step > 0; step = step / 2){
for (int i = 0; i + step < arr.length; i++){
insertSort(arr, i + step, step);
}
}
return arr;
}
public static void insertSort(int[] arr, int start, int step){
// 已经有序的数是: start - step
for (int i = start; i < arr.length; i = i + step){
if (arr[i] > arr[i - step]){
continue;
}
int value = arr[i];
int pos = i - step;
while (pos > -1 && arr[pos] > value && pos + step < arr.length){
arr[pos + step] = arr[pos];
pos = pos - step;
}
arr[pos + step] = value;
}
}