常见排序算法(比较排序)及比较

#include<iostream>
using namespace std;
#include<assert.h>
 
 //稳定性:指两个相同数排序后位置是否变化
//冒泡排序思想:相邻两数据比较交换,外层循环控制次数,内层比较  
//void BubbleSort(int *a, size_t len)
//{
//	assert(a);
//	for (size_t i = 0; i < len - 1; ++i)
//	{   //相邻位置数据进行比较,每趟排序都会排出一个最大或最小数
//		for (size_t j = 0; j < len - i - 1; ++j)
//		{
//			if (a[j] > a[j + 1])
//			{
//				swap(a[j], a[j + 1]);
//			}
//		}
//	}
//}
//
//鸡尾酒排序思想:即就是双向冒泡排序,一趟排序就可以找到一个最大的和最小元素
void cooktail_sort(int *arr, size_t size)
{
	assert(arr);
	int tail = size - 1;
	int i, j ;
	for (i = 0; i < tail; ++i)
	{
		for (int j = tail; j>i; --j)
		{
			if (arr[j] < arr[j - 1])
			{
				swap(arr[j], arr[j - 1]);
			}
		}
		++i;
		for (j = i; j < tail; ++j)
		{
			if (arr[j]>arr[j + 1])
				swap(arr[j], arr[j + 1]);
		}
		--tail;
	}
}
//思想:将当前位置的下一个数据插入到前边以有序的块中,再将该数与前边有序的数据逐一比较。
//每插入一个该位置以前的数据都已有序
//void InsertSort(int *a, size_t len)//插入排序
//{
//	assert(a);
//	for (size_t i = 0; i < len-1; ++i)//当i=len-1时,tmp访问的位置越界
//	{
//		int end = i;
//		int tmp = a[end + 1];
//		while (end >= 0 && a[end]>tmp)//最后一次进去end=0位置要比
//		{
//			a[end + 1] = a[end];
//			--end;
//		}
//		a[end + 1] = tmp;
//	}
//}
//思想:将一个数组分成两半,再将每一半分半,递归类推,当分出来的只有一个数据时,可认为该小组组内已经有序,然后合并相邻小组,即先递归分解数列,在合并数列
void Mergesort(int *arr, int begin1, int end1, int begin2, int end2)
{
	//assert(arr);
	//if (begin1 >= end1 || begin2 >= end2)
	//	return;
	//int one = end1 - begin1;
	//int two = end2 - begin2;
	//int *L = new int[one];//开辟两个数组,一个保存前半部分,一个保存后半部分
	//int *R = new int[two];
	//int i = 0, j = 0;
	//for (; i < one; ++i)
	//{
	//	L[i] = arr[begin1 + i];
	//}
	//for (i=0; i < two; ++i)
	//{
	//	R[i] = arr[begin2 + i];
	//}
	//int index = begin1;
	//for (i = 0, j = 0; index < end2&&i<one&&j<two; ++index)
	//{
	//	if (L[i] <= R[j])
	//	{
	//		arr[index] = L[i];
	//		++i;
	//	}
	//	else 
	//	{
	//		arr[index] = R[j];
	//		++j;
	//	}
	//}
	//if (i < one)//如果一个子序已排完,将剩另一个余的数据直接连接到后边
	//{
	//	for (int k = i; k < one; ++k)
	//		arr[index++] = L[k];
	//}
	//else
	//{
	//	for (int k = j; k <two; ++k)
	//		arr[index++] = R[k];
	//}
	//delete[] L;
	//delete[] R;
}
//void _merge_sort(int *arr, int begin, int end)
//{
//	assert(arr);
//	if (begin + 1 < end)
//	{
//		int mid = begin + ((end - begin) >> 1);
//		_merge_sort(arr, begin, mid);
//		_merge_sort(arr, mid, end);
//		Mergesort(arr, begin, mid, mid, end);
//		//memcpy(src + begin, dst + begin, (end - begin)*sizeof(int));
//	}
//	else
//		return;
//}
//两个同样数组,将源数组按序放入目标数组中
void Mergesort(int *src,int *dst, int begin1,int end1,int begin2,int end2)
{
	assert(src&&dst);
	size_t index = begin1;//两个同样大小的数组
	while (begin1 < end1 && begin2 < end2)
	{
		if (src[begin1] < src[begin2])
		{
			dst[index++] = src[begin1++];
		}
		else
		{
			dst[index++] = src[begin2++];
		}
	}
	if (begin1 < end1)
	{
		while (begin1 < end1)
		{
			dst[index++] = src[begin1++];
		}
	}
	else
	{
		while (begin2 < end2)
		{
			dst[index++] = src[begin2++];
		}
	}
}

void _merge_sort(int *src, int *dst, int begin, int end)
{
	assert(src && dst);
	if (begin + 1 < end)
	{
		int mid = begin + ((end - begin) >> 1);
		_merge_sort(src, dst, begin, mid);
		_merge_sort(src, dst, mid , end);
		Mergesort(src, dst, begin, mid, mid, end);
		memcpy(src + begin, dst + begin, (end - begin)*sizeof(int));
	}
	else
		return;
}

void _Merge_sort(int* src, size_t size)
{
	int* dst = new int[size];
	_merge_sort(src, dst, 0, size);
	delete[] dst;
}

//思想:采用分治法思想,选定一个基数,通过一趟排序将要排序的数组一分为二,其中基数前的数据都比它小,基数后的数据都比它大,然后在将这两部分数据分别进行快排
int QSort(int *a, int left, int right)//快速排序
{
	assert(a);
	if (left >= right)
		return left;
	int key = a[right];
	int begin = left;
	int end = right-1;
	while (begin < end)
	{
		while (begin < end && a[begin] <= key)
			begin++;
		while (begin < end && a[end] > key)
			end--;
		if (begin < end)
			swap(a[begin], a[end]);
	}
	if (a[end] >= a[right])
		swap(a[end], a[right]);
	return end;
}

//void QuiSort(int* a, int  left, int right)//挖坑法
//{
//	assert(a);
//	if (right <= left)
//		return;
//	int tmp = a[left];
//	int begin = left;
//	int end = right;
//	while (begin < end)
//	{
//		while (begin < end&&a[end] >= tmp)
//			end--;
//		if (begin < end)
//		{
//			a[begin++] = a[end];
//		}
//		while (begin < end&&a[begin] <= tmp)
//			begin++;
//		if (begin < end)
//		{
//			a[end--] = a[begin];
//		}
//	}
//	a[begin] = tmp;
//	QuiSort(a, left, begin - 1);
//	QuiSort(a, begin + 1, right);
//}

void QuickSort(int *a, int left,int right)
{
	assert(a);
	if (left < right)
	{
		int mid = QSort(a, left, right);
		QuickSort(a, left, mid - 1);
		QuickSort(a, mid + 1, right);
	}
}

//思想:第一次查找最小元素所在位置的下标,与第一个元素交换,之后查找次小元素下标,与第二个元素交换,以此类推
//void SelectSort(int* a, size_t len)//选择排序
//{
//	assert(a);
//	size_t min_index ;
//	for (size_t i = 0; i < len; ++i)
//	{
//		min_index = i;
//		for (size_t j = i+1; j < len ; ++j)
//		{
//			if (a[min_index] >= a[j])
//			{
//				min_index = j;//找最小元素所在的下标
//			}
//		}
//		swap(a[min_index], a[i]);//让最小元素位于第i个位置
//	}
//}

//思想:将数组按某个增量gap分成若干组,每组中记录的下标相差gap,对每组中全部元素进行排序  //,然后用一个较小增量再进行上述循环排序,当增量减到1时,整个要排序的数被分成单个组,排序完成
void Shell_sort(int *a,size_t size)
{
	assert(a);
	int gap = size / 3 + 1;
	while (1)
	{
		for (int i = 0; i < size - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while ((a[end] > tmp)&&end >= 0)
			{
				a[end+gap] = a[end];
				end -= gap;
			}
			a[end + gap] = tmp;
		}
		if (gap == 1)
			break;
		gap = gap / 3 + 1;//保证gap最后为1时能执行
	}
}

void TestSelectSort()
{
	int a[10] = { 9, 1, 3, 4, 8, 6, 0, 2, 5, 0 };
	int len = sizeof(a) / sizeof(a[0]);
	cout << "before:";
	for (int i = 0; i < len; ++i)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	Shell_sort(a,len);
	//QuickSort(a, 0, 9);
	//SelectSort(a, 10);
	cout << "after: ";
	for (int i = 0; i < len; ++i)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}
void TestMergeSort()
{
	int a[10] = { 9, 1, 3, 4, 8, 6, 7, 2, 5, 0 };
	int len = sizeof(a) / sizeof(a[0]);
	cout << "before:";
	for (int i = 0; i < len; ++i)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	//_merge_sort(a,0, len);
	_Merge_sort(a, len);
	cout << "after: ";
	for (int i = 0; i < len; ++i)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}
//堆排序思想:先建成一个大堆或小堆,堆顶元素是最大(最小),让堆顶元素与最后一个元素交换,数组长度-1,然后向下调整一次,重复上述循环
//template<class T>
//class Heap
//{
//public:
//	Heap(T* a, size_t size)
//	{
//		for (size_t i = 0; i < size; ++i)
//		{
//			_array.push_back(a[i]);
//		}
//		for (int i = (_array.size() - 2) / 2; i >= 0;--i)
//		{
//			AdjustDown(i,_array.size());
//		}
//	}
//
//	void AdjustDown(int root,int size)
//	{
//		size_t lchild = 2 * root + 1;
//		while (lchild < size)
//		{
//			if ((lchild + 1 < size) && _array[lchild + 1] < _array[lchild])
//			{
//				lchild++;
//			}
//			if (_array[lchild] < _array[root])
//			{
//				swap(_array[lchild], _array[root]);
//				root=lchild;
//				lchild = 2 * root + 1;
//			}
//			else
//				break;
//		}
//	}
//
//	void AdjustUp(size_t child)
//	{
//		size_t root = (child - 1) / 2;
//		while (child > 0)//若root和child为size_t型,永远都不会小于0,因此不能用它为循环条件
//		{
//			if (_array[child] < _array[root])
//			{
//				swap(_array[child], _array[root]);
//				child = root;
//				root = (child - 1) / 2;
//			}
//			else
//				break;
//		}
//	}
//
//	void push_elem(const T&x)
//	{
//		_array.push_back(x);
//		AdjustUp(_array.size() - 1);
//	}
//
//	void Pop_elem()
//	{
//		swap(_array[0], _array[(_array.size() - 1]));
//		_array.pop_back();//将堆顶元素与最后一个元素交换并删除,再进行向下调整
//		AdjustDown(0);
//	}
//
//	void Heap_Sort()
//	{
//		int size = _array.size();
//		while (size>0)
//		{
//			swap(_array[0], _array[size-1]);
//			cout << _array[size - 1] << " ";
//			//_array.pop_back();
//			AdjustDown(0,size-1);
//			--size;
//		}
//	}
//	void Display()
//	{
//		cout << "heap is:";
//		for (int i = 0; i < _array.size(); ++i)
//		{
//			cout << _array[i] << " ";
//		}
//		cout << endl;
//	}
//protected:
//	vector<T> _array;
//
//};
//

算法的适用场景及比较:

比较排序:(1)插入(直接插入、希尔排序)、(2)选择(选择排序、堆排序)、(3)交换(冒泡排序、快排)(4)外排序(归并)

1)时间复杂度:

平均性能为O(N^2):插入、选择、冒泡

数据规模小时:直接插入排序较好

数据规模大时:冒泡排序时间代价最高

平均性能为O(NlgN):堆、快速、归并

数据规模大时:适用堆排序(例:在一千万个数中找最小的前100个数)

数据规模小时:快速排序较好,当小到一定区间使用插入排序

希尔排序平均时间复杂度为O(N^1.3)

稳定性指的是两个相同的数排序后位置是否变化,若无变化则稳定

2).稳定性分析:

稳定:冒泡、插入、归并

不稳定:选择、希尔、堆、快排

时间: 2024-10-01 03:20:18

常见排序算法(比较排序)及比较的相关文章

经典排序算法 - 桶排序Bucket sort

经典排序算法 - 桶排序Bucket sort 补充说明三点 1,桶排序是稳定的 2,桶排序是常见排序里最快的一种,比快排还要快-大多数情况下 3,桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法 我自己的理解哈,可能与网上说的有一些出入,大体都是同样的原理 无序数组有个要求,就是成员隶属于固定(有限的)的区间,如范围为[0-9](考试分数为1-100等) 例如待排数字[6 2 4 1 5 9] 准备10个空桶,最大数个空桶 [6 2 4 1 5 9]           待排

(转载)经典排序算法-希尔排序

经典排序算法 - 希尔排序Shell sort 希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组, 就是每

经典排序算法 - 图书馆排序(Library Sort)

经典排序算法 - 图书馆排序(Library Sort) 思路简介,大概意思是说,排列图书时,如果在每本书之间留一定的空隙,那么在进行插入时就有可能会少移动一些书,说白了就是在插入排序的基础上,给书与书之间留一定的空隙,这个空隙越大,需要移动的书就越少,这是它的思路,用空间换时间 看红线标的那句话知道,这个空隙留多大,你自己定 图书馆排序的关键是分配空间,分配完空间后直接使用插入排序即可 进行空间分配的过程 这个我实在是找不到相关的资料,没准就是平均分配嘞 进行插入排序的过程 举例待排数组[ 0

经典排序算法 - 选择排序Selection sort

经典排序算法 - 选择排序Selection sort 顾名思意,就是直接从待排序数组里选择一个最小(或最大)的数字,每次都拿一个最小数字出来, 顺序放入新数组,直到全部拿完 再简单点,对着一群数组说,你们谁最小出列,站到最后边 然后继续对剩余的无序数组说,你们谁最小出列,站到最后边 再继续刚才的操作,一直到最后一个,继续站到最后边,现在数组有序了,从小到大 举例 先说看每步的状态变化,后边介绍细节,现有无序数组[6 2 4 1 5 9] 第一趟找到最小数1,放到最前边(与首位数字交换) 交换前

经典排序算法 - 鸡尾酒排序Cocktail sort

经典排序算法 - 鸡尾酒排序Cocktail sort 鸡尾酒排序基于冒泡排序,双向循环 还是看例子吧,给定待排数组[2 3 4 5 1] 第一趟过去时的每一步 第一步迭代,2 < 3不换 [2 3 4 5 1] 第二步迭代,3 < 4不换 [2 3 4 5 1] 第三步迭代,4 < 5不换 [2 3 4 5 1] 第四步迭代,5 > 1交换 [2 3 4 1 5] 第一趟回来时的第一步,鸡尾酒一次到头后就回返回来,再到头后再过去,来回比,一个来回能排两个数字 第五步迭代,1 &l

经典排序算法 - 希尔排序Shell sort

经典排序算法 - 希尔排序Shell sort 希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组, 就是每

经典排序算法 - 地精排序Gnome Sort

经典排序算法 - 地精排序Gnome Sort 号称最简单的排序算法,只有一层循环,默认情况下前进冒泡,一旦遇到冒泡的情况发生就往回冒,直到把这个数字放好为止 直接看它排序的过程,待排数组[6 2 4 1 5 9] 先设计一个标识i=0然后从头开始判断,什么时候(i < 6)不成立,什么时候排序结束, 所以,如何控制i的值是这个算法的关键 例如待排数组: [6 2 4 1 5 9] [0 1 2 3 4 5] 看一下具体的排序过程 [ i = 0 ]时啥也不干,先让i自增1,达到值为1才开始真正

经典排序算法 - 奇偶排序Odd-even sort

经典排序算法 - 奇偶排序Odd-even sort 又一个比较性质的排序,基本思路是奇数列排一趟序,偶数列排一趟序,再奇数排,再偶数排,直到全部有序 举例吧, 待排数组[6 2 4 1 5 9] 第一次比较奇数列,奇数列与它的邻居偶数列比较,如6和2比,4和1比,5和9比 [6 2 4 1 5 9] 交换后变成 [2 6 1 4 5 9] 第二次比较偶数列,即6和1比,5和5比 [2 6 1 4 5 9] 交换后变成 [2 1 6 4 5 9] 第三趟又是奇数列,选择的是2,6,5分别与它们的

经典排序算法 - 梳排序Comb sort

经典排序算法 - 梳排序Comb sort 梳排序还是基于冒泡排序,与冒泡不同的是,梳排序比较的是固定距离处的数的比较和交换,类似希尔那样 这个固定距离是待排数组长度除以1.3得到近似值,下次则以上次得到的近似值再除以1.3,直到距离小至3时,以1递减 不太好描述,还是看例子吧 假设待数组[8 4 3 7 6 5 2 1] 待排数组长度为8,而8÷1.3=6,则比较8和2,4和1,并做交换 [8 4 3 7 6 5 2 1] [8 4 3 7 6 5 2 1] 交换后的结果为 [2 1 3 7

经典排序算法 - 耐心排序Patience Sorting

经典排序算法 - 耐心排序Patience Sorting 这个排序的关键在建桶和入桶规则上 建桶规则:如果没有桶,新建一个桶;如果不符合入桶规则那么新建一个桶 入桶规则:只要比桶里最上边的数字小即可入桶,如果有多个桶可入,那么按照从左到右的顺序入桶即可 举个例子,待排数组[6 4 5 1 8 7 2 3] 第一步,取数字6出来,此时一个桶没有,根据建桶规则1新建桶,将把自己放进去,为了表述方便该桶命名为桶1或者1号桶 第二步,取数字4出来,由于4符合桶1的入桶规则,所以入桶1,并放置在6上边,