算法:希尔排序

插入排序是把序列的首个元素看做是有序的序列,然后把序列后面的元素往这个有序序列插入,其增量是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;
        }
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值