算法学习之路(一):排序算法

算法学习之路(一):排序算法

写在文章之前

算法与数据结构是计算学科知识结构的核心与技术体系的基石,学好数据结构和算法对于程序设计能力的提高有着相当大的作用。在这一系列文章中,将会从最基础的部分开始,记录和展示我在学习数据结构和算法的过程中的收获和感悟,希望这些能够帮助更多的人学习算法,爱上算法。考虑到C/C++在算法竞赛中的普遍应用以及大多数学校以C/C++作为第一门语言,该系列文章的所有示例代码均采用C/C++。

排序算法

排序算法是所有算法中最基础也是最重要的部分之一,D.Knuth曾指出,四分之一以上的CPU时间都用于执行同一类型的计算:按照某种约定的次序,将给定的一组元素顺序排列。因此,我们不妨从排序算法开始我们的学习之路。

PS:以下算法示例均为非降序排序

1、选择排序(Selection Sort)

1.1 算法思路

选择排序是最简单的,也是最直接的算法。简单的说,就是每次从无序部分中选出最大的元素放在有序部分的末尾,直到无序部分为空。

1.2 算法思想

减而治之,每次处理一个数据,从而使待处理的数据规模单调递减,直至全部处理完毕。

1.3 算法效率

时间复杂度(平均):O( n 2 n^2 n2
时间复杂度(最坏):O( n 2 n^2 n2
时间复杂度(最好):O( n 2 n^2 n2
空间复杂度:O(1)
稳定性:不稳定

1.4 代码实现:

void SelectionSort(int a[], int length) {
	for (int i = 0; i < length - 1; ++i) {
		//每次循环后将第i小的元素放好
		int tmpMin = i;
		//记录第i个到第n-1个元素中,最小的元素的下标 
		for (int j = i + 1; j < length; ++j) {
			if (a[j] < a[tmpMin])
				tmpMin = j;
		}
		//下面将第i小的元素放在第i个位置上,并将原来第i个位置的元素挪到后面
		int tmp = a[i];
		a[i] = a[tmpMin];
		a[tmpMin] = tmp;
	}
}

2、插入排序(Insert Sort)

2.1 算法思路

插入排序多用于打扑克时对摸到的扑克牌进行排序,具体来说,就是每次从无序部分中抽出一个元素,并将其放到有序部分的适当位置,直至所有数据排序完毕。

2.2 算法思想

遍历,将所有数据遍历,放到适当位置。

2.3 算法效率

时间复杂度(平均):O( n 2 n^2 n2
时间复杂度(最坏):O( n 2 n^2 n2
时间复杂度(最好):O( n n n
空间复杂度:O( 1 1 1
稳定性:稳定

2.4 代码实现:

void InsertSort(int a[], int length) {
	for (int i = 0; i < length; ++i) {
		//a[i]是最左边的无序元素,每次循环将a[i]放到合适位置
		for (int j = 0; j < i; ++j) {
			if (a[j] > a[i]) {
				//要把a[i]放到位置j,原下标j到i-1的元素都往后移一位
				int tmp = a[i];
				for (int k = i; k > j; --k)
					a[k] = a[k - 1];
				a[j] = tmp;
				break;
			}
		}
	}
}

3、冒泡排序(Bubble Sort)

3.1 算法思路

使较大的“气泡”上浮,如果遇到更大的“气泡”,则令原“气泡”停止,换由更大的“气泡”上浮,使得每执行一轮都会使得最大的“气泡”完成上浮,待循环执行结束后,所有的“气泡”都位于适当位置。

3.2 算法思想

渐进优化,不断改善局部有序性从而最终实现整体的有序性。

3.3 算法效率

时间复杂度(平均):O( n 2 n^2 n2
时间复杂度(最坏):O( n 2 n^2 n2
时间复杂度(最好):O( n n n
空间复杂度:O( 1 1 1
稳定性:稳定

3.4 代码实现:

void BubbleSort(int a[], int length) {
	for (int i = 0; i < length - 1; ++i) {
		for (int j = 0; j < length - 1 - i; ++j) {
			//使大数上浮
			if (a[j] > a[j + 1]) {
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
}

4、希尔排序(Shell Sort)

4.1 算法思路

希尔排序是直接插入排序的改进版,也称缩小增量排序。希尔排序采用跳跃分组的策略,根据设定的增量根据下标将数据分为多组,对每组数据分别进行插入排序,然后不断减少增量(减少组数)再次排序,直到增量为1(所有数据变回1组),排序结束。

4.2 算法思想

渐进优化,不断改善局部有序性从而最终实现整体的有序性。

4.3 算法效率

时间复杂度(平均):O( n 1.3 n^{1.3} n1.3
时间复杂度(最坏):O( n 2 n^2 n2
时间复杂度(最好):O( n n n
空间复杂度:O( 1 1 1
稳定性:不稳定

4.4 代码实现:

void ShellSort(int a[], int length) {
	int increment = length;//初始化增量 
	int  temp = 0;
	do {
		//每次减小增量,直到增量为1 
		increment = increment / 3 + 1;
		for (int i = increment; i < length; ++i) {
			//对每个划分进行插入排序 
			if (a[i - increment] > a[i]) {
				temp = a[i];
				int j = i - increment;
				do {
					//移动元素并寻找位置 
					a[j + increment] = a[j];
					j -= increment;
				} while (j >= 0 && a[j] > temp);
				a[j + increment] = temp;//插入元素 
			}
		}
	} while (increment > 1);
}

5、归并排序(Merge Sort)

5.1 算法思路

将未排序的序列拆分为两个子序列,直到子序列有序,然后将两个有序的子序列合并为完全有序序列,直至完全合并。

5.2 算法思想

归并排序是分而治之思想的一个典型应用,先将序列拆分,使每个子序列有序,再使子序列段间有序,最终合并成为大的序列。

5.3 算法效率

时间复杂度(平均):O( n log ⁡ 2 n n\log_2n nlog2n
时间复杂度(最坏):O( n log ⁡ 2 n n\log_2n nlog2n
时间复杂度(最好):O( n log ⁡ 2 n n\log_2n nlog2n
空间复杂度:O( n n n
稳定性:稳定

5.4 代码实现:

void Merge(int a[], int length) {
	int temp[length];	//辅助数组 
	int b = 0;	//辅助数组的起始位置 
	int mid = length / 2;	//mid将数组从中间划分,前一半有序,后一半有序 
	int first = 0, second = mid;	//两个有序序列的起始位置 

	while (first < mid&&second < length) {
		if (a[first] <= a[second])	//比较两个序列 
			temp[b++] = a[first++];
		else
			temp[b++] = a[second++];
	}

	while (first < mid)		//将剩余子序列复制到辅助序列中 
		temp[b++] = a[first++];
	while (second < length)
		temp[b++] = a[second++];
	for (int i = 0; i < length; ++i)	//辅助序列复制到原序列 
		a[i] = temp[i];
}


void MergeSort(int a[], int length) {
	if (length <= 1)
		return;
	if (length > 1) {
		MergeSort(a, length / 2);	//对前半部分进行归并排序 
		MergeSort(a + length / 2, length - length / 2);		//对后半部分进行归并排序 
		Merge(a, length);	//归并两部分 
	}
}

6、快速排序(Quick Sort)

6.1 算法思路

用基准点将全部数据划分为比基准数大和比基准数小的两部分,然后对于每部分分别递归排序,直至每部分小于一个元素结束递归,最终完成排序。

6.2 算法思想

分治,利用基准点将数据分成两部分,从而对于每一个部分而言,减少其规模,达到分而治之的效果。

6.3 算法效率

时间复杂度(平均):O( n log ⁡ 2 n n\log_2n nlog2n
时间复杂度(最坏):O( n 2 n^2 n2
时间复杂度(最好):O( n log ⁡ 2 n n\log_2n nlog2n
空间复杂度:O( n log ⁡ 2 n n\log_2n nlog2n
稳定性:不稳定

6.4 代码实现:

void QuickSort(int a[], int left, int right) {
	int i = left, j = right;//设置左右指针 
	int pivot = a[left];//设置基准数 
	if (i >= j)//递归基 
		return;
	while (i < j)
	{
		while (i<j&&a[j]>pivot)//从右找到比基准小的数 
			j--;
		if (i < j)
		{
			a[i++] = a[j];//把找到的数放到基准数左侧然后令左指针右移
		}
		while (i < j&&a[i] <= pivot)//从左找到比基准大的数 
			i++;
		if (i < j)
		{
			a[j--] = a[i];//把找到的数放到基准数右侧(放在上一步移动的数留下的“坑”中)  
		}
	}
	a[i] = pivot;//把基准数插到合适位置 
	//分别对左右两侧进行快速排序 
	QuickSort(a, left, i - 1);
	QuickSort(a, i + 1, right);
}

挖坑待填:

7、堆排序(Heap Sort)
8、计数排序(Counting Sort)
9、桶排序(Bucket Sort)
10、基数排序(Radix Sort)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值