数据结构与算法——希尔排序

1. 希尔排序基本原理

原理详解可以参考浙大的数据结构与算法

个人理解,选择排序当数据量很大,或者数据很混乱的时候,需要的进行大量的比较和移动,并且这其中由很多是重复的(这一次移动的元素在下一次仍然需要移动)。使用希尔排序最开始的比较,元素间隔大,需要操作的元素数量比较少;等到进行了几轮比较之后,元素间隔变小,需要比较的元素数量变多,但是由于之前的几轮比较,此时待比较的元素在整体处于较有序的状态,混乱程度比较低,因此即使元素数量变多,也不需要太多次的移动

2. C++实现

使用C++实现了基本的希尔排序算法,同时使用函数指针作为参数,可以给希尔排序传入不同的生成增量序列的函数。比如最简单的生成{N/2, (N/2)/2,..., 1}这样的增量序列的函数如下:

/**
 * @brief 生成希尔排序最一般的增量序列:{n/2, (n/2)/2,..., 1}
 * @param[in]   n  希尔排序待排序元素的个数,增量序列最大值为n/2
 * @param[out]  gaps  vector容器,用于存储生成的增量序列
 * @return
 */
 void getNormalGaps(int n, vector<int>& gaps) {
	 int i = 1;
	 gaps.clear();
	 int tmp = n;
	 while ((tmp /= 2) > 0) {
		 gaps.push_back(tmp);
		 i++;
	 }
	 reverse(gaps.begin(), gaps.end());
 }

生成Hibbard增量序列Dk=2^k-1, {1, 3, 7, 15, 31...}的函数如下:

 /**
 * @brief 生成Hibbard增量序列:Dk=2^k-1,{1, 3, 7, 15, 31...}
 * @param[in]   n  希尔排序待排序元素的个数,Hibbard增量序列最大值不超过n
 * @param[out]  gaps  vector容器,用于存储生成的Hibbard增量序列
 * @return
 */
 void getHibbardGaps(int n, vector<int>& gaps) {
	int i = 1;
	gaps.clear();
	int tmp = 0;
	while ((tmp = (1 << i) - 1) <= n) {
		gaps.push_back(tmp);
		i++;
	}
}

希尔排序的主函数如下:

/**
 * @brief 将vector容器中的元素进行排序,希尔排序,增量序列由函数*func生成
 * @param[in]  nums  要排序的vector
 * @param[in]  func  函数指针,用于生成希尔排序使用的增量序列
 * @return
 */
void shellSort(vector<int>& nums, void (*func)(int, vector<int>&)) {
	int n = nums.size();
	vector<int> gaps;
	(*func)(n, gaps);
	for (auto it = gaps.rbegin(); it != gaps.rend(); it++) {
		int gap = *it;
		for (int i = gap; i < n; i++) {
			int tmp = nums[i];
			int j;
			for (j = i - gap; j >= 0 && nums[j] > tmp; j -= gap) {
				nums[j + gap] = nums[j];
			}
			nums[j + gap] = tmp;
		}
	}
}

完整的带输入输出的示例代码如下。如果需要将函数写成模板,支持自定义数据类型以及自定义升序还是降序,可以参考之前插入排序算法的函数模板自定义数据类型需要重载比较运算符,或者自定义一个用于该类型比较函数对象

/**
 * @file ShellSort.cpp
 * @brief 希尔排序是在插入排序的基础上进行的改进
   插入排序当数据量比较小或者数据基本有序的时候,比较高效。当数据量大且状态混乱的时候,需要进行大量的比较和移动操作。
   希尔排序通过将待排数组按照增量进行分组,开始分组间隔较大,将数据量减少;后面间隔变小,但是通过前面的排序,数据的混乱程度降低,以此实现性能上的平衡
   增量序列的选取对希尔排序的效率的影响很大,最常用的增量序列为{N/2, (N/2)/2,..., 1}
 * @author 好好学习
 * @version v1
 * @date 2020-08-01
 */

#include <iostream>
#include <vector>
#include <ctime>
using namespace std;

void shellSort(vector<int>& nums);//希尔排序,使用默认的增量序列进行排序
void shellSort(vector<int>& nums, void(*func)(int, vector<int>&));//希尔排序,传入函数指针func,指定生成增量序列的方式
void getHibbardGaps(int n, vector<int>& gaps);//生成Hibbard增量序列的函数,Dk=2^k-1,{1, 3, 7, 15, 31...}
void getNormalGaps(int n, vector<int>& gaps);//生成一般增量序列的函数,{n/2, (n/2)/2,..., 1}
void printVector(vector<int>& nums);//辅助函数,用于打印vector中的元素
void generateIntVector(vector<int>& nums, int n);//辅助函数,用于构造vector<int>

int main()
{
	srand((int)time(0));
	//1. 构造vector<int>使用shellSort函数,按照默认的增量序列进行排序
	vector<int> nums1;
	generateIntVector(nums1, 10);
	cout << "Initial value of nums1: ";
	printVector(nums1);//打印vector元素初始值

	shellSort(nums1);

	cout << "Sorted value of nums1: ";
	printVector(nums1);//打印排序后的vector元素

	//2. 构造vector<int>使用重载的shellSort函数,按照传入参数指定的增量序列生成方法,生成排序所需的增量序列
	vector<int> nums2;
	generateIntVector(nums2, 10);

	cout << "Initial value of nums2: ";
	printVector(nums2);//打印vector元素初始值

	shellSort(nums2, getHibbardGaps);//第二个参数指定了希尔排序的增量序列使用getHibbardGaps函数生成的Hibbard序列

	cout << "Sorted value of nums1: ";
	printVector(nums2);//打印排序后的vector元素

	return 0;
}
/**
 * @brief 向标准输出流输出vecotr中的元素,元素之间以空格分割
 * @param[in]  nums  要打印的vector
 * @return
 */
void printVector(vector<int>& nums) {
	if (nums.empty()) {
		cout << "The vector is empty!" << endl;
		return;
	}
	cout << nums[0];
	for (int i = 1; i < nums.size(); i++)cout << " " << nums[i];
	cout << endl;
}
/**
 * @brief 生成n个int型随机数,填充到vector容器中
 * @param[in]   n  要填充的元素的个数
 * @param[out]  nums   要往里填充元素的容器
 * @return
 */
void generateIntVector(vector<int>& nums, int n) {
	int tmp = 0;
	for (int i = 0; i < n; i++) {
		tmp = rand() % 100;
		nums.push_back(tmp);
	}
}
/**
 * @brief 将vector容器中的元素进行排序,希尔排序,使用由getNormalGaps函数生成的默认增量序列,{N/2, (N/2)/2,..., 1}
 * @param[in]  nums  要排序的vector
 * @return
 */
void shellSort(vector<int>& nums) {
	int n = nums.size();
	vector<int> gaps;
	getNormalGaps(n, gaps);
	for (auto it = gaps.rbegin(); it != gaps.rend(); it++) {
		int gap = *it;
		for (int i = gap; i < n; i++) {
			int tmp = nums[i];
			int j;
			for (j = i - gap; j >= 0 && nums[j] > tmp; j -= gap) {
				nums[j + gap] = nums[j];
			}
			nums[j + gap] = tmp;
		}
	}
}

/**
 * @brief 将vector容器中的元素进行排序,希尔排序,增量序列由函数*func生成
 * @param[in]  nums  要排序的vector
 * @param[in]  func  函数指针,用于生成希尔排序使用的增量序列
 * @return
 */
void shellSort(vector<int>& nums, void (*func)(int, vector<int>&)) {
	int n = nums.size();
	vector<int> gaps;
	(*func)(n, gaps);
	for (auto it = gaps.rbegin(); it != gaps.rend(); it++) {
		int gap = *it;
		for (int i = gap; i < n; i++) {
			int tmp = nums[i];
			int j;
			for (j = i - gap; j >= 0 && nums[j] > tmp; j -= gap) {
				nums[j + gap] = nums[j];
			}
			nums[j + gap] = tmp;
		}
	}
}
/**
 * @brief 生成希尔排序最一般的增量序列:{n/2, (n/2)/2,..., 1}
 * @param[in]   n  希尔排序待排序元素的个数,增量序列最大值为n/2
 * @param[out]  gaps  vector容器,用于存储生成的增量序列
 * @return
 */
 void getNormalGaps(int n, vector<int>& gaps) {
	 int i = 1;
	 gaps.clear();
	 int tmp = n;
	 while ((tmp /= 2) > 0) {
		 gaps.push_back(tmp);
		 i++;
	 }
	 reverse(gaps.begin(), gaps.end());
 }

 /**
 * @brief 生成Hibbard增量序列:Dk=2^k-1,{1, 3, 7, 15, 31...}
 * @param[in]   n  希尔排序待排序元素的个数,Hibbard增量序列最大值不超过n
 * @param[out]  gaps  vector容器,用于存储生成的Hibbard增量序列
 * @return
 */
 void getHibbardGaps(int n, vector<int>& gaps) {
	int i = 1;
	gaps.clear();
	int tmp = 0;
	while ((tmp = (1 << i) - 1) <= n) {
		gaps.push_back(tmp);
		i++;
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值