
文章目录
- 一、排序的概念及其运用 
   
- 1.排序的概念
 - 2.常见的排序算法
 
 - 二、常见排序算法的实现 
   
- 1.插入排序 
     
- 1.直接插入排序
 - 2.希尔排序
 
 - 2.选择排序 
     
- 1.直接选择排序
 - 2.堆排序
 
 - 3.交换排序 
     
- 1.冒泡排序
 - 2.快速排序 
       
- 1.hoare版本
 - 2.挖坑法
 - 3.前后指针版本
 
 
 - 4.归并排序
 - 5.非比较排序
 
 - 1.插入排序 
     
 - 三、排序算法复杂度及其稳定性分析
 
一、排序的概念及其应用
1.排序的概念
排序:所谓排序,就是使一连串记录,按照其中的某个或某些关键字的大小,递增或者递减的排列起来的操作。
稳定性:在待排序的序列中,存在多个具有相同的值,若经过排序,使得这些值的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,在排序后,次序仍然保持不变,那就称这种排序算法稳定的,否则就称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程中的要求不能拿在内外存之间移动数据的排序。
2.常见的排序算法

二、常见排序算法的实现(跑排序算法OJ链接)
插入排序
插入排序的思想是一种简单的插入排序法,其基本思想是:将待排序的记录按照其值大小依次逐个插入到一个已经排好序的有序序列中,知道所有的记录插入完为止,得到一个新的有序序列
1.直接插入排序
//简单插入排序
void InsertSort(int* a, int n)
{
	//需要将n-2后面的元素插入排序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;//将end中后面一个元素插入到有序的区间[0,end]中
		int tmp = a[end + 1];//保存一下end后面一个元素的大小
		while (end >= 0)//将所插入的元素中的前面end个元素进行比较
		{
			if (tmp < a[end])//如果end后面一个元素的值比前面一个元素小的话往后移动
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;/*如果end此时的值比后面一个元素的值小的话则退出后进
				行赋值保证了此时数据能够全部插入
				而且有序*/
			}
		}
		a[end + 1] = tmp;/*将此时的tmp赋值
		(一种是因为这个有序区间的值都比后面这个大遍历完之后end为-1此时应该将tmp赋值
		第二种是因为此时break之后数值没有赋值)*/
	} 
直接插入排序特性总结:
1.元素集合越接近有序,直接插入排序的效率越高。
2.时间复杂度(O(n^2)
3.空间复杂度(O(1))
4.稳定性:稳定
2.希尔排序(缩小增量排序)
排序思想:先选定一个整数,把待排序文件中所有记录分成小组,所有距离为记录的分在一个同一个小组内进行排序。然后,取,重复上述分组和排序的工作。当到达1时,所有记录在统一组内排好序。
1预排序(使接近有序)
2直接插入排序(有序)
	//希尔排序(存在gap的简单排序)
	void ShellSort(int* a, int n)
	{
		int gap = n;//定义一个步长
		while (gap > 1)//确保gap的最后一次等于1(最后一次为插入排序)
		{
			gap = gap / 3 + 1;//(无论gap是奇数还是偶数使得最后一次的gap为1)
            //gap=gap/2;
			for (int i = 0; i < n - gap; i++)
			{
				int end = i;
				int tmp = a[end + gap];
				while (end >= 0)
				{
					if (tmp < a[end])//如果end后面一个元素的值比前面一个元素小的话往后移动
					{
						a[end + 1] = a[end];
						end = end - gap;
					}
					else
					{
						break;/*如果end此时的值比后面一个元素的值小的话则退出后进
				行赋值保证了此时数据能够全部插入
				而且有序*/
					}
					a[end + gap] = tmp;
					/*将此时的tmp赋值
		(一种是因为这个有序区间的值都比后面这个大遍历完之后end为-1此时应该将tmp赋值
		第二种是因为此时break之后数值没有赋值)*/
				}
			}
		}
	} 
 

希尔排序的特性总结:
1.希尔排序是对直接排序的优化。
2.当gap>1时都是预排序,目的是让数组更接近于有序。当gap=1时。数组已经接近于有序了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3.希尔排序的时间复杂度不好计算,因为gap取值方法比较多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定;
4.稳定性:不稳定
选择排序
直接选择排序
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end) {
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; i++) {
			if (a[i] < a[mini])
				mini = i;
			if (a[i] > a[maxi])
				maxi = i;
		}
		Swap(&a[begin], &a[mini]);
		if (maxi == begin)
			maxi = mini;
		Swap(&a[end], &a[maxi]);
		begin++;
		end--;
	}
} 
 
 
堆排序
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child<size) {
		if (child+1<size&&a[child] < a[child + 1])
			child++;
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
		AdjustDown(a, n, i);
	int end = n - 1;
	while (end >= 0) {
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);
		end--;
	}
} 
交换排序
冒泡排序

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++) {
		int exchange = 0;
		for (int j = 1; j < n - i; j++) {
			if (a[j - 1] >a[j]) {
				Swap(&a[j], &a[j - 1]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
} 
 冒泡排序的特性总结:
快速排序
 //hoare版本
int PartSort1(int* a, int begin, int end)
{
	int left = begin, right = end;
	int keyi = left;
	while (left < right) {
		while (a[right] >= a[keyi] && left < right)
			--right;
		while (a[left] <= a[keyi] && left < right)
			++left;
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = left;
	return left;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >end)
		return;
	int mid = PartSort1(a, begin,end);
	QuickSort(a, begin, mid - 1);
	QuickSort(a, mid + 1, end);
}
	 
 
2.挖矿法

int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	int piti = begin;
	while (begin < end) {
		while (begin < end && a[end] >= a[piti])
			--end;
		Swap(&a[end], &a[piti]);
		piti = end;
		while (begin < end && a[begin] <= a[piti])
			++begin;
		Swap(&a[begin], &a[piti]);
		piti = begin;
	}
	a[piti] = key;
	return piti;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >end)
		return;
	int mid = PartSort2(a, begin,end);
	QuickSort(a, begin, mid - 1);
	QuickSort(a, mid + 1, end);
} 
 
 
3.前后指针法

int PartSort3(int* a, int begin, int end)
{
	int prev = begin;
	int cur = begin + 1;
	int keyi = begin;
	while (cur <= end) {
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >end)
		return;
	int mid = PartSort3(a, begin,end);
	QuickSort(a, begin, mid - 1);
	QuickSort(a, mid + 1, end);
}
	
 
int GetMidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid]) {
		if (a[mid] < a[end])
			return mid;
		else if (a[begin] < a[end])
			return end;
		else
			return begin;
	}
	else
	{
		if (a[mid] > a[end])
			return mid;
		else if (a[begin] < a[end])
			return begin;
		else
			return end;
	}
}
int PartSort3(int* a, int begin, int end)
{
	int prev = begin;
	int cur = begin + 1;
	int keyi = begin;
	int mid = GetMidIndex(a, begin, end);
	Swap(&a[mid], &a[keyi]);
	while (cur <= end) {
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}
void _QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	else if (end - begin > 10) {
		int keyi = PartSort3(a, begin, end);
		_QuickSort(a, begin, keyi - 1);
		_QuickSort(a, keyi + 1, end);
	}
	else
		InsertSort(a+begin, end-begin+1);
} 
   
 void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, end);
	StackPush(&st, begin);
	while (!StackEmpty(&st)) {
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);
		int keyi = PartSort3(a, left, right);
		if (keyi + 1 < right) {
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}
		if (left < keyi - 1) {
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}
	StackDestroy(&st);
} 
归并排序
  
void _MergeSort(int* a, int begin, int end,int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	_MergeSort(a,begin, mid,tmp);
	_MergeSort(a, mid + 1, end,tmp);
	int begin1 = begin, end1 = mid ;
	int begin2 = mid + 1, end2 = end;
	int i = begin1;
	while (begin1 <= end1 && begin2 <= end2) {
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];
	while (begin2 <= end2)
		tmp[i++] = a[begin2++];
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL) {
		printf("malloc fail!\n");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1,tmp);
	free(tmp);
} 
归并非递归
/*void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
		printf("malloc fail!\n");
		exit(-1);
	}
	int gap = 1;
	while (gap < n) {
		for (int i = 0; i < n; i += 2 * gap) {
			//[i,i+gap-1][i+gap,i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (end1 >= n) {
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n) {
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
				end2 = n - 1;
			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2) {
				if (a[begin1] < a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
		 }
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}
}*/
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
		printf("malloc fail!\n");
		exit(-1);
	}
	int gap = 1;
	while (gap < n) {
		for (int i = 0; i < n; i += 2 * gap) {
			//[i,i+gap-1][i+gap,i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (end1 >= n || begin2 >= n)
				break;
			else if (end2 >= n)
				end2 = n - 1;
			int m = end2 - begin1 + 1;
			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2) {
				if (a[begin1] < a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
			memcpy(a+i, tmp+i, sizeof(int) * m);
		 }
		
		gap *= 2;
	}
} 
 
非比较排序
  void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; i < n; i++) {
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}
	int range = max - min + 1;
	int* count = (int*)malloc(sizeof(int) * range);
	if (count == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	memset(count, 0, sizeof(int) * range);
	for (int i = 0; i < n; i++) {
		count[a[i]-min]++;
	}
	int j = 0;
	for (int i = 0; i < n; i++)
	{
		while (count[i]--)
			a[j++] = i + min;
	}
} 
计数排序的特性总结:
排序算法复杂度及稳定性分析

 








](https://img-blog.csdnimg.cn/9712415c3add452f943c523eadc6605a.png)










