经典的各类排序总结

1. 插入排序

实现源码:

void InsertSort(int* arr, unsigned int len)
{
	//前置条件判断
	if (!arr || len <= 1)
	{
		return;
	}

	unsigned int i;
	unsigned int j;
	for (i = 1; i < len; i++)
	{
		j = i - 1;
		int x = arr[i];
		while (j >= 0 && arr[j] > x)
		{
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = x;
	}
}

性能分析:假设需要从小到大进行排序,则插入排序的最坏情形是原来的数组是按照从大到小的顺序排列的,这样需要的时间复杂度是:

                         

插入排序的最好情形是原来的数组已经从小到大排好序了,则时间复杂度是:

                         

接下来是插入排序的平均情况,其时间复杂度为:

                        

最后,插入排序是稳定的。                         

2. 折半插入排序

在前面插入排序中,由于每次在插入的过程中,都需要与前面已排好序的元素进行比较,但是需要都进行比较吗?考虑到已经排好序这个特点,所以在比较的时候可以采用折半的方式,这里给出折半插入排序的实现源码:

void InsertHalfSort(int* arr, unsigned int len)
{
	//前置条件判断
	if (!arr || len <= 1)
	{
		return;
	}

	unsigned int i,j,low,mid,high,upper;
	for (i = 1; i < len; i++)
	{
		int x = arr[i];

		//初始化指针low、mid、high
		low = 0;
		high = i - 1;
		mid = (low + high) / 2;

		//通过折半查找查询应该插入的位置
		while (low < high)
		{
			if (x == arr[mid])
			{
				break;
			}
			else if (x < arr[mid])
			{
				high = mid - 1;
			}
			else
			{
				low = mid + 1;
			}
			mid = (low + high) / 2;
		}

		if (arr[mid] <= x)
		{
			upper = mid + 1;
		}
		else
		{
			upper = mid;
		}

		for (j = i; j >= upper + 1; j--)
		{
			arr[j] = arr[j - 1];
		}
		arr[j] = x;
	}
}

该排序算法的平均时间复杂度为O(nlgn),最好情形依旧为O(n),且是稳定的。

3. 希尔排序

希尔排序是对插入排序的又一次成功的改良,适用于大量数据的排序,且代码简洁:

void ShellSort(int* arr, int len)
{
	int i, j, k;
	k = len / 2;
	while (k > 0)
	{
		for (i = k + 1; i < len; i++)
		{
			int x = arr[i];
			j = i - k;
			while (j >= 0 && arr[j] > x)
			{
				arr[j + k] = arr[j];
				j = j - k;
			}
			arr[j + k] = x;
		}
		k = k / 2;
	}
}

4. 归并排序

归并排序的关键是将两个已经排好序的数组合并成一个排序数组,以下是合并函数的实现源码:

//将两个有序的数组合并到一起
void Merge(int* arr, unsigned int begin, unsigned int mid, unsigned int high)
{
	unsigned int len1 = mid - begin + 1;
	unsigned int len2 = high - mid;

	//构造临时数组存储合并的结果
	int* tempArr = (int* )malloc((len1 + len2) * sizeof(int));
	
	//申请内存空间失败,则返回
	if (!tempArr)
	{
		return;
	}

	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int k =  0;
	while (i < len1 && j < len2)
	{
		if (arr[i + begin] > arr[j + mid + 1])
		{
			tempArr[k] = arr[j + mid + 1];
			j++;
		}
		else
		{
			tempArr[k] = arr[i + begin];
			i++;
		}
		k++;
	}

	if (i == len1)
	{
		while (j < len2)
		{
			tempArr[k] = arr[j + mid + 1];
			k++;
			j++;
		}
	}

	if (j == len2)
	{
		while (i < len1)
		{
			tempArr[k] = arr[i + begin];
			k++;
			i++;
		}
	}

	//将临时数组中的元素赋给原数组
	for (i = 0; i < len1 + len2; i++)
	{
		arr[i + begin] = tempArr[i];
	}

	//释放空间
	free(tempArr);
}

接下来只要进行递归归并排序即可:

//归并排序
void MergeSort(int* arr, unsigned int low, unsigned int high)
{
	if (low < high)
	{
		unsigned int mid = (low + high) / 2;
		MergeSort(arr, low, mid);
		MergeSort(arr, mid + 1, high);
		Merge(arr, low, mid, high);
	}
}

归并排序的时间复杂度为O(nlgn),且是稳定的。

5. 快速排序

快速排序和归并排序一样,采用了分治的思想。快速排序是每次选择一个主元,然后将小于或等于主元的元素放到主元的左边,其余的放到主元的右边。以下是实现源码:

//交换两个整数
void Swap(int* x, int* y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
}

int Split(int* arr, int low, int high)
{
	int i = low;			//指针i始终指向轴点
	int j = low + 1;		//指针j用于遍历数组
	while (j <= high)
	{
		if (arr[j] <= arr[i])		//如果小于或等于主元,则交换
		{
			i++;		//注意,这里是先增加指针i,然后交换
			Swap(&arr[i], &arr[j]);
		}

		j++;
	}

	Swap(&arr[i], &arr[low]);
	return i;
}

//快速排序
void QuickSort(int* arr, int low, int high)
{
	if (low >= high)
	{
		return;
	}

	int w = Split(arr, low, high);
	QuickSort(arr, low, w - 1);
	QuickSort(arr, w + 1, high);
}

快速排序的最坏情况是出现单边树的情况,此时快速排序的时间复杂度为O(n^2),其余情况下时间复杂度都是O(nlgn)。

6. 计数排序

如果已知序列中比元素x小的个数,则可以确定元素x在排序后的位置。这就是计数排序的基本思想,计数排序需要两个辅助数组B和C,B表示排好序的输出数组,C用于存储每个元素在原序列中比该元素小的个数,则有以下实现源码:

void CountSort(int* A, int* B, int* C, int len, int k)
{
	int i = 0;
	
	//将计数数组初始化为0
	for (i = 0; i <= k; i++)
	{
		C[i] = 0;
	}
	
	//计数数组取值为A[i]的元素个数
	for (i = 0; i < len; i++)
	{
		C[A[i]] = C[A[i]] + 1;
	}
	
	//计数数组取值为小于或等于i的元素个数,放在C[i]里面
	for (i = 1; i < len; i++)
	{
		C[i] = C[i] + C[i - 1];
	}
	
	for (i = len - 1; i >= 0; i--)
	{
		B[C[A[i]]] = A[i];
		C[A[i]] = C[A[i]] - 1;
	}
}

计数排序的有点是线性时间和稳定性,但是由于计数排序的运行时间与输入参数的取值规模有关,所以在有些情况下会达到平方级时间,所以具有一定的局限性。

7. 基数排序

8. 桶排序

9. 堆排序

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值