栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

257-希尔&快排&归并&堆排-性能测试

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

257-希尔&快排&归并&堆排-性能测试

希尔&快排&归并&堆排-性能测试代码
#include 
#include 
using namespace std;

//堆的下沉调整
void siftDown(int arr[], int i, int size)
{
	int val = arr[i];
	while (i < size / 2)
	{
		int child = 2 * i + 1;
		if (child + 1 < size && arr[child + 1] > arr[child])
		{
			child = child + 1;
		}

		if (arr[child] > val)
		{
			arr[i] = arr[child];
			i = child;  // i继续指向它的孩子,继续调整
		}
		else
		{
			break;
		}
	}
	arr[i] = val;
}

//堆排序
void HeapSort(int arr[], int size)
{
	int n = size - 1;
	//从第一个非叶子节点
	for (int i = (n - 1) / 2; i >= 0; i--)
	{
		siftDown(arr, i, size);
	}

	//把堆顶元素和末尾元素进行交换,从堆顶开始进行下沉操作
	for (int i = n; i > 0; i--)
	{
		int tmp = arr[0];
		arr[0] = arr[i];
		arr[i] = tmp;

		siftDown(arr, 0, i);//第三个参数,参与调整的元素的个数
	}
}

//归并过程函数  O(n)
void Merge(int arr[], int l, int m, int r, int* p)
{
	int idx = 0;
	int i = l;
	int j = m + 1;

	while (i <= m && j <= r)
	{
		if (arr[i] <= arr[j])
		{
			p[idx++] = arr[i++];
		}
		else
		{
			p[idx++] = arr[j++];
		}
	}

	while (i <= m)
	{
		p[idx++] = arr[i++];
	}

	while (j <= r)
	{
		p[idx++] = arr[j++];
	}

	//再把合并好的大段有序的结果,拷贝到原始arr数组[l,r]区间内
	for (i = l, j = 0; i <= r; i++, j++)
	{
		arr[i] = p[j];
	}
}

//归并排序递归接口
void MergeSort(int arr[], int begin, int end, int* p)
{
	//递归结束的条件
	if (begin >= end)
	{
		return;
	}

	int mid = (begin + end) / 2;
	// 先递
	MergeSort(arr, begin, mid, p);
	MergeSort(arr, mid + 1, end, p);
	// 再归并  [begin, mid]  [mid+1, end] 把两个小段有序的序列,合并成大段有序的序列
	Merge(arr, begin, mid, end, p);
}

//归并排序
void MergeSort(int arr[], int size)
{
	int* p = new int[size];  // O(n)
	MergeSort(arr, 0, size - 1, p);
	delete[]p;
}

//快排分割处理函数
int Partation(int arr[], int l, int r)
{
	//选择基准数的优化:“三数取中”法   arr[l]   arr[r]   arr[(l+r)/2]  
	//记录基准数
	int val = arr[l];

	//一次快排处理   时间:O(n) * O(logn) = O(nlogn)    空间:O(logn) 递归的深度所占用的栈内存
	while (l < r)
	{
		while (l < r && arr[r] > val)
		{
			r--;
		}

		if (l < r)
		{
			arr[l] = arr[r];
			l++;
		}

		while (l < r && arr[l] < val)
		{
			l++;
		}

		if (l < r)
		{
			arr[r] = arr[l];
			r--;
		}
	}

	//l == r的位置,就是放基准数的位置
	arr[l] = val;
	return l;
}

//快排的递归接口
void QuickSort(int arr[], int begin, int end)
{
	if (begin >= end)//快排递归结束的条件
	{
		return;
	}

	//优化一:当[begin, end]序列的元素个数小到指定数量,采用插入排序
	//if (end - begin <= 50)
	//{
		// InsertSort(arr, begin, end);
		//return;
	//}

	//在[begin, end]区间的元素做一次快排分割处理
	int pos = Partation(arr, begin, end);

	//对基准数的左边和右边的序列,再分别进行快排
	QuickSort(arr, begin, pos - 1);
	QuickSort(arr, pos + 1, end);
}

//快速排序
void QuickSort(int arr[], int size)
{
	return QuickSort(arr, 0, size - 1);
}

//希尔排序
void ShellSort(int arr[], int size)
{
	for (int gap = size / 2; gap > 0; gap /= 2) // 100W 19 1000W 24
	{
		for (int i = gap; i < size; i++) // O(n)
		{
			int val = arr[i];
			int j = i - gap;
			for (; j >= 0; j -= gap) // O(n)
			{
				if (arr[j] <= val)
				{
					break;
				}
				arr[j + gap] = arr[j];
			}
			arr[j + gap] = val;
		}
	}
}

int main()
{
	cout << RAND_MAX << endl;

	const int COUNT = 100000000;
	int* arr = new int[COUNT];
	int* brr = new int[COUNT];
	

	srand(time(NULL));

	//0 - 32767    32768 - 32768+32767 
	for (int i = 0; i < COUNT; i++)
	{
		int val = rand() % COUNT;  // 0 - 32767
		arr[i] = val;
	}

	clock_t begin, end;

	memcpy(brr, arr, COUNT * sizeof(int));

	begin = clock();
	QuickSort(brr, COUNT);//快排 
	end = clock();
	cout << "QuickSort spend:" << (end - begin) * 1.0 / CLOCKS_PER_SEC << "s" << endl;

	memcpy(brr, arr, COUNT * sizeof(int));

	begin = clock();
	MergeSort(brr, COUNT);//归并 
	end = clock();
	cout << "MergeSort spend:" << (end - begin) * 1.0 / CLOCKS_PER_SEC << "s" << endl;

	memcpy(brr, arr, COUNT * sizeof(int));

	begin = clock();
	ShellSort(brr, COUNT);//希尔 
	end = clock();
	cout << "ShellSort spend:" << (end - begin) * 1.0 / CLOCKS_PER_SEC << "s" << endl;

	memcpy(brr, arr, COUNT * sizeof(int));

	begin = clock();
	HeapSort(brr, COUNT);//堆排 
	end = clock();
	cout << "HeapSort spend:" << (end - begin) * 1.0 / CLOCKS_PER_SEC << "s" << endl;
}

测试结果


我们测出:

  • 快排的速度是最快的,其次是归并排序,然后是希尔排序,最后是堆排序

归并和快排:

  • 归并和快排的处理操作很相似,但是归并要开辟额外的内存空间,存放当前合并的有序的序列,然后拷贝到原始的序列中,这就是归并比快排慢一点点的地方。快排的最坏时间复杂度是O(n^2),但是场景是有序的序列,现实中数据量很大,有序的概率是非常低的。

希尔排序是对插入排序的优化,尤其是序列趋于有序的情况下,效率很高,但是在乱序的场景下,效率低于快排和归并排序。

堆排序和快排:

堆排排序的在最坏最好和平均,时间复杂度都是O(nlogn),但是排序是不稳定的,但是它和快排差很多,为什么?

  • CPU在从内存上加载数据的时候,并不是指令操作什么数据,它就加载什么数据;
  • 往往是根据程序的局部性运行原理,当你不管是访问指令,还是访问数据,访问当前这一条指令或者数据,接下来很有可能访问相邻的指令或者数据;
  • 所以,当CPU从内存上加载指令或者数据的时候,它会把这个指令或者数据以及相邻的指令或者数据都加载到CPU里面,当前用到的会放入CPU寄存器或者直接进入CPU的逻辑运算单元进行运算,马上会用到的都会放到CPU的缓存里,当下一次再去取相应指令或者数据的时候,如果缓存中有,就直接从缓存取,不用去内存取,缓存取的效率高于内存取。


也就是说,进行加载的时候,堆排更多是从内存取,而快排和归并排序是更多从缓存取。(缓存只是内存连续的一段数据,加载到CPU上。)

大根堆的末尾节点本来值就比较小,然后每一趟都和堆顶元素交换,然后因为值本身就小,然后下沉肯定很多,要进行很多次的比较,要进行很多次的交换

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/917204.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号