常用排序算法

一、排序的稳定性

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的,否则称为不稳定的。

二、插入排序

1.直接插入排序

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int temp = a[i + 1];
		while (end>=0)
		{
			if (a[end]>temp)
			{
				a[end+1]=a[end];
				end--;
			}
			else
				break;
		}
		a[end + 1] = temp;
	}
}

2.希尔排序

void ShellSort(int* a, int n)
{
	// gap > 1  预排序
	// gap == 1 直接插入排序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//gap = gap / 2;

		// 间隔为gap的多组并排
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}

			a[end + gap] = tmp;
		}
	}
}

三、选择排序

1.直接选择排序

这里单趟找出了最大和最小,分别放到数组最外两个位置,注意begin和max重合的场景

void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		// 选出 [begin, end]
		int mini = begin, maxi = begin;
		for (int i = begin; i <= end; ++i)
		{
			if (a[i] > a[maxi])
				maxi = i;

			if (a[i] < a[mini])
				mini = i;
		}

		Swap(&a[begin], &a[mini]);

		// 如果begin和maxi重叠,需要修正一下新的maxi的位置
		if (begin == maxi)
		{
			maxi = mini;
		}

		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

2.堆排序

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child+1 < n && a[child+1] > a[child])
		{
			++child;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}

// 堆排序 O(N*logN)
void HeapSort(int* a, int n)
{
	// 建大堆  O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

四、交换排序

1.冒泡排序

void BubbleSort(int* a, int n)
{
	for (int end = n; end > 0; --end)
	{
		int exchange = 0;
		for (int i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}

		if (exchange == 0)
		{
			break;
		}
	}
}

2.快速排序

//三数取中
int GetMidIndex(int* a, int left, int right)
{
	//int mid = (left + right) / 2;
	int mid = left + (right - left) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else  //(a[left] > a[mid])
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}

}
//挖坑法
int PartSort(int* a, int left, int right)
{
	int midi = GetMidIndex(a, left, right);
	Swap(&a[left], &a[midi]);

	int key = a[left];
	int hole = left;
	while (left < right)
	{
		// 右边去找小,填到左边的坑里面
		while (left < right && a[right] >= key)
		{
			--right;
		}

		// 把右边找的小的,填到左边的坑,自己形成新的坑
		a[hole] = a[right];
		hole = right;

		// 左边去找大,填到右边的坑里面
		while (left < right && a[left] <= key)
		{
			++left;
		}

		// 把左边找的大的,填到右边的坑,自己形成新的坑
		a[hole] = a[left];
		hole = left;
	}

	a[hole] = key;
	return hole;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	int keyi = PartSort(a, left, right);
	//PrintArray(a+left,right-left + 1);

	// [left, keyi-1] keyi [keyi+1, right]
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi + 1, right);
}



三数取中是为了避免有序的情况,快速排序复杂度分析

快速排序非递归

思路:
在栈中存区间下标

void QuickSortNonR(int* a, int left, int right) {
	stack<int> st;
	
	st.push(left);
	st.push(right);
    
	while (!st.empty()) {
		int end = st.top();
		st.pop();
		int begin = st.top();
		st.pop();
			int div = PartSort(a, begin, end);
		if (begin < div - 1) {
			st.push(begin);
			st.push(div - 1);
		}
		if (div + 1 < end) {
			st.push(div+1);
			st.push(end);
		}
	}
}

五、归并排序

递归版本:

void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;

	int mid = (right + left) / 2;
	// [left, mid][mid+1, right]
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid+1, right, tmp);
	// 归并
	int begin1 = left, end1 = mid;
	int begin2 = mid+1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}

	while(begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}

	// 归并后的结果,拷贝回到原数组
	for (int i = left; i <= right; ++i)
	{
		a[i] = tmp[i];
	}
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

非递归:
难点:如何避免越界

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	int groupNum = 1;
	while (groupNum < n)
	{
		for (int i = 0; i < n; i += 2 * groupNum)
		{
			// [begin1][end1] [begin2,end2] 
			// 归并
			int begin1 = i, end1 = i + groupNum - 1;
			int begin2 = i + groupNum, end2 = i + groupNum * 2 - 1;
			int index = begin1;

			// 数组数据个数,并不一定是按整数倍,所以划分的分组可能越界或者不存在
			// 1、[begin2,end2] 不存在, 修正为一个不存在的区间
			if (begin2 >= n)
			{
				begin2 = n + 1;
				end2 = n;
			}

			// 2、end1越界,修正一下
			if (end1 >= n)
			{
				end1 = n - 1;
			}

			// 3、end2越界,需要修正后归并
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		// 拷贝回原数组
		for (int i = 0; i < n; ++i)
		{
			a[i] = tmp[i];
		}

		groupNum *= 2;

		//PrintArray(a, n);
	}
	
	free(tmp);
}

六、非比较排序

计数排序

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*)calloc(range, sizeof(int));

	// 统计次数
	for (int i = 0; i < n; ++i)
	{
		count[a[i] - min]++;
	}

	// 根据count数组排序
	int i = 0;
	for (int j = 0; j < range; ++j)
	{
		while (count[j]--)
		{
			a[i++] = j + min;
		}
	}
}

七、复杂度比较

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南无故人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值