[Running] g++ test_sort_xier_2.3.cpp -std=c++14 -o test_sort_xier_2.3.exe && ./test_sort_xier_2.3.exe
100 99 8 7 200 3 1 43
3
7
max h 7
43 99 8 7 200 3 1 100
--b---
43 99 8 7 200 3 1 100
change h 2
---e--
8 99 43 7 200 3 1 100
8 7 43 99 200 3 1 100
8 7 43 99 200 3 1 100
8 3 43 7 200 99 1 100
1 3 8 7 43 99 200 100
1 3 8 7 43 99 200 100
--b---
1 3 8 7 43 99 200 100
change h 0
---e--
1 3 8 7 43 99 200 100
[Done] exited with code=0 in 0.609 seconds
//如果指定间隔是3,size是N
while(h < N/3 )
{
h = h * 3 +1 ;
}
for (int i = h ; i < N; i++ )
// j 可以为h,因为 a[0] 是可以的。
for ( int j = h; j >=h && less([a[j],a[j-h]];
j = j - h )
{
exchange( j , j -h );
}
#include
#include
#include
using Vec = std::vector<int>;
void show(Vec &v)
{
//for(auto &entry : v)
int i = 0;
int size = v.size();
for(;i<size;i++)
{
std::cout <<" "<< v[i];
}
std::cout <<std::endl;
}
bool less(int a, int b )
{
if(a <b )
{
return true;
}
return false;
}
bool exchange( int *a ,int *b)
{
if(a == nullptr || b == nullptr )
{
return false;
}
int tmp = *a;
*a = *b;
*b = tmp;
return true;
}
//希尔, 1 增量序列,比如 每次 1/2 1/3这样的
//2 每一组 分别进行插入排序 ,每次排序后,选用下一个增量序列的数组
void sort(Vec &v)
{
int N = v.size();
int h = 1;
#if 0
// h =1 时,N = 8,N/3为2时,h 第一次循环就变为了4,
while(h < N/3)
{
h = 3*h +1;
std::cout << h <<std::endl;
}
#else
while(h < N/2)
{
h = 2*h +1;
std::cout << h <<std::endl;
}
#endif
std::cout << " max h "<<h <<std::endl;
//增量h递减 插入排序
//排序是从最大增量 开始到 1 ,每次都要对每个子数组做排序
{
//啥时候结束?,h=1 时,做最后一次直接插入排序
//增量h最小是1 ,h 也是每次的跨度值
while(h>=1)
{
//对于每个增量值,第一个分量数组的起始位置是h
int i= h;
//增量值开始,向后(直到N)每个都与全面的h个值构成一个数组
//每个数组有h个元素,数组最后一个元素是从h开始++ , 下标就是i
for(; i < N; i++)
{
//从i开始构造数组,直到i 等于N结束
// a[i] 插入到 a[i-h] a[i-2*h] a[i-3*h] 中
int j = i ;
for(;j>=h && less(v[j],v[j-h]); j -=h )
{
exchange(&v[j],&v[j-h]);
}
show(v);
}
//增量序列的下一个
h = h/3;
std::cout <<"--b---"<<std::endl;
show(v);
std::cout << " change h "<<h <<std::endl;
std::cout <<"---e--"<<std::endl;
}
}
}
int main()
{
Vec v = {100,99,8,7,200,3,1,43};
show(v);
sort(v);
show(v);
return 0;
}
希尔排序研究
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
算法步骤
1)选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2)按增量序列个数k,对序列进行k 趟排序;
3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
/***************************************************************************
* @file main.cpp
* @author MISAYAONE
* @date 27 March 2017
* @remark 27 March 2017
* @theme Shell Sort
***************************************************************************/
#include
#include
#include
#include
using namespace std;
void Shell_sort(int a[],size_t n)
{
int i,j,k,group;
for (group = n/2; group > 0; group /= 2)//增量序列为n/2,n/4....直到1
{
for (i = 0; i < group; ++i)
{
for (j = i+group; j < n; j += group)
{
//对每个分组进行插入排序
if (a[j - group] > a[j])
{
int temp = a[j];
k = j - group;
while (k>=0 && a[k]>temp)
{
a[k+group] = a[k];
k -= group;
}
a[k] = temp;
}
}
}
}
}
int main(int argc, char**argv)
{
int a[10] = {1,51,6,2,8,2,564,1,65,6};
Shell_sort(a,10);
for (int i = 0; i < 10; ++i)
{
cout<<a[i]<<" ";
}
cin.get();
return 0;
}
步长的选择是希尔排序的重要部分。只要最终步长为 1 任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为 1 进行排序。当步长为 1 时,算法变为插入排序,这就保证了数据一定会被排序。
已知的最好步长串行是由 Sedgewick 提出的 (1, 5, 19, 41, 109,…),该步长的项来自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 这两个算式。这项研究也表明 “比较在希尔排序中是最主要的操作,而不是交换。” 用这样步长串行的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
最差时间复杂度 O(nlog2 n)
平均时间复杂度 依赖于步长间隔 O(nlog2 n)
最优时间复杂度 O(nlogn)
最差空间复杂度 O(n),辅助空间 O(1)
特点分析:不稳定算法unstable sort,in_place
程序略,需要O(n^2)次的比较
冒泡排序一趟扫描的结果是 H, C, Q, P, A, M, S, R, D, F, X ,Y ;
初始步长为4的希尔(shell)排序一趟的结果是 P, A, C, S, Q, D, F, X , R, H,M, Y ;
二路归并排序一趟扫描的结果是 H, Q, C, Y,A, P, M, S, D, R, F, X ;
快速排序一趟扫描的结果是 F, H, C, D, P, A, M, Q, R, S, Y,X ;
堆排序初始建堆的结果是 A, D, C, R, F, Q, M, S, Y,P, H, X 。
————————————————