排序算法-冒泡、插入、选择、归并、快速、计数五种排序原理及代码

原地排序算法,是特指空间复杂度是O(1)的排序算法。

排序算法的稳定性,指的是如果待排序的序列中存在值相等的元素,经给排序之后,相等元素之间原有的先后顺序不变。

排序算法test代码

int main() {
	int a[10] = { 12,54,69,35,85,6,75,11,65,55 };

	cout << "原数组为:";
	for (int i = 0;i < 10;++i)
		cout << a[i] << ' ';
	cout << endl << endl;

	//选择合适的排序算法
	CountingSort(a, 10);

	cout << "排序后为:";
	for (int i = 0;i < 10;++i)
		cout << a[i] << ' ';
	cout << endl;

	system("pause");
	return 0;
}

1冒泡排序(Bubble Sort)

第一,冒泡排序是原地排序算法吗?
冒泡排序不需要额外的存储空间,它的空间复杂度为O(1),是一个原地排序算法。

第二,冒泡排序是稳定的排序算法吗?
在冒泡排序中,只有a[j]<a[j-1]才会交换顺序,所以相同大小的元素在排序前后不会改变顺序,所以原地冒泡是稳定的排序算法。

第三,冒泡排序的时间复杂度是多少?
最好情况下的时间复杂度是O(n),最坏情况下的时间复杂度是O(n方);平均情况下的时间复杂度是O(n方)

void Bubble_Sort(int a[], int n) {
	if (n <= 1)
		return;

	for (int i = 1;i < n;++i) {
		bool flag = false;
		for (int j = 1;j < n-i+1;++j) {
			if (a[j] < a[j - 1]) {
				swap(a[j], a[j - 1]);
				flag = true;
			}
		}
		//假如没有交换说明已经有序了,不需要再排序了,提前退出
		if (flag = false)
			break;
	}
}

2插入排序(Insertion Sort)

一个有序的数组,我们往里面添加一个新的数据,只要在原来的有序数组中找到新数据应该存放的位置即可。

第一,插入排序是原地排序算法吗?
插入排序同样不需要额外的存储空间,因此它的空间复杂度为O(1),所以插入排序是一个原地排序算法。

第二,插入排序是稳定的排序算法吗?
因为插入排序中只有当待插入数据value<a[j]的时候才需要将数据后移,因此相同的数据在数组中的先后顺序不会发生改变,因此插入排序是一个稳定的排序算法。

第三,插入排序的时间复杂度是多少?
平均时间复杂度O(n方)

void Insertion_Sort(int a[], int n) {
	for (int i = 1;i < n;++i) {
		int temp = a[i];
		int j;
		for (j = i;j > 0;--j) {
			//数据后移
			if (temp < a[j - 1])
				a[j] = a[j - 1];
			else
				break;
		}
		//此时的j就是temp这个值应该存放的位置
		a[j] = temp;
	}
}

3选择排序(Selection Sort)

选择排序类似插入排序,但是插入排序是将数据后移直到找到新数据插入的位置,而选择排序是每次从未排序的区间中找到最小值,将其放到已排序区间的末尾。

选择排序是原地排序算法
选择排序不是稳定的排序算法,因为选择排序每次都要找剩余未排序元素中的最小值,并和前面已排序数据的后一个数据进行交换,这个交换就可能破坏了稳定了。
例如5,8,5,2,9,第一次找到最小元素2,与第一个5交换,那么稳定性就被破坏了。
平均时间复杂度为O(n方)。

void Selection_Sort(int a[], int n) {
	for (int i = 0;i < n;++i) {
		int min_val = INT_MAX;
		int min_index;
		for (int j = i;j < n;++j) {
			if (a[j] < min_val) {
				min_val = a[j];
				min_index = j;
			}
		}
		swap(a[i], a[min_index]);
	}
}

4归并排序(Merge Sort)

归并排序的原理

归并排序的核心思想是先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排序好的两部分合并在一起,这样整个数组就都有序了。也就是分而治之的思想。
分治这种解决问题的思想需要用到递归。

第一,归并排序是稳定的排序算法吗?
代码中a[i]<=a[j],先将排在前面的a[i]放进排序的数组temp中,所以排序后的数组,相同大小数据的先后顺序不会发生变化,即归并排序是稳定的排序算法。

第二,归并排序是原地排序算法吗?
归并排序不是原地排序算法,因为在并的过程中需要用到temp数组来保存排序后的数据,排序好以后再将temp拷贝至原数组中,temp就是额外的存储空间。空间复杂度为O(n)。

第三,归并排序的时间复杂度是多少?
无论是最好情况、最坏情况,还是平均情况,时间复杂度都是O(nlogn)。

//---------------归并排序------------------
void merge(int a[], int p, int q, int r) {
	//分开两个区间的起点i,j,临时数组起点k
	int i = p, j = q + 1, k = 0;

	//临时数组用来保存排序后的数组
	int* temp = new int[r - p + 1];

	while (i <= q && j <= r) {
		if (a[i] <= a[j])
			temp[k++] = a[i++];
		else
			temp[k++] = a[j++];
	}

	//剩余数据存放到temp中
	int start = i, end = q;
	if (j <= r) {
		start = j;
		end = r;
	}

	while (start <= end)
		temp[k++] = a[start++];

	//将数据拷贝到a中,一定要有等号,因为有r-p+1个数据
	for (int c = 0;c <= r-p;++c) {
		a[p+c] = temp[c];
	}
}

void merge_sort_c(int a[], int p, int r) {
	//终止条件
	if (p >= r)
		return;

	//分割点q
	int q = (p + r) / 2;

	//分
	merge_sort_c(a, p, q);
	merge_sort_c(a, q + 1, r);

	//并
	merge(a, p, q, r);
}

void merge_sort(int a[], int n) {
	merge_sort_c(a, 0, n - 1);
}
//------------------------------------------

5 快速排序(Quick Sort)

快速排序的原理

快排用的也是分治思想,但是思路完全不一样。快排的思想是,如果要排序数组中下标从p到r之间的一组数据,我们选择p到r之间的任意一个数据作为pivot。一般选最后一位作为pivot。

我们遍历p到r之间的数据,将小于pivot的放在左边,将大于pivot的放到右边,将pivot放到中间。经过这一步骤之后,数组p到r之间的数据就被分成了三个部分,前面p到q-1之间都是小于pivot的,中间是pivot,后面的q+1到r之间是大于pivot的。

根据分治、递归的处理思想,我们可以用递归排序下标从p到q-1之间的数据和下标从q+1到r之间的数据,直到区间缩小为1,就说明所有的数据都有序了。

快速排序的时间复杂度和归并排序一样,是O(nlogn)。


6 计数排序(Counting Sort)

void CountingSort(int a[], int n) {
	if (n <= 1)
		return;

	//桶的最大值
	int max = a[0];
	for (int i = 0;i < n;++i) {
		if (a[i] > max)
			max = a[i];
	}

	//初始化桶,桶的范围是0-max
	int* c = new int[max + 1];
	for (int i = 0;i <= max;++i)
		c[i] = 0;

	//计算数组a中小于等于c的下标的数据的个数
	for (int i = 0;i < n;++i) 
		c[a[i]]++;

	for (int i = 1;i <= max;++i)
		c[i] += c[i - 1];

	//开始计数排序,这一步是关键
	int* r = new int[n];
	for (int i = n - 1;i >= 0;--i) {
		int index = c[a[i]] - 1;
		r[index] = a[i];
		c[a[i]]--;
	}

	//排序好的数组r拷贝到数组a
	for (int i = 0;i < n;++i)
		a[i] = r[i];	
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值