八大排序算法总结

讲述难度从我个人角度从简单到难,从熟悉到陌生,比较重要的排序是堆排序和快速排序。

以下全都是C++代码实现,遵循的原则是最简洁。

1.冒泡排序

冒泡排序应该是最简单的排序了,高中的时候就已经练了滚瓜烂熟了。

冒泡排序的排序规则是:从最后一个数开始,每次与他上一个数进行比较,根据比较大小进行交换,接着向上移,直到遇到i,一轮比较完成,将未排序中最大(最小)的数冒泡到最上面。

vector<int> bubble_sort(vector<int> v)
{
	for(int i = 0; i < v.size()-1; i++){
		for(int j = v.size()-1; j > i; j--){
			if(v[j] < v[j-1]){
				int temp = v[j];
				v[j] = v[j-1];
				v[j-1] = temp;
			}
		}
	}
	return v;
}

2.选择排序

选择排序也是最基本的排序算法,也是高中和冒泡排序一起学的算法(强烈建议现在浙江高考信息技术增加快排,这两个排序太简单了,跟其他科目相比完全没有难度可言)

选择排序的排序方法是:将最左边未排序的数与后面所有数比较,选出最大(最小)的数,一轮比较完成。将最大(最小)的数移到了最左边。

vector<int> select_sort(vector<int> v)
{
	for(int i = 0; i < v.size()-1; i++){
		for(int j = i+1; j < v.size(); j++){
			if(v[i] > v[j]){
				int temp = v[j];
				v[j] = v[i];
				v[i] = temp;
			}
		}
	}
	return v;
}

3.插入排序

插入排序也算一种比较简单的算法,它在数据较少的时候有不错的效率,std::sort()在数据较小时使用插入排序而非快排。

算法步骤:
1)将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

vector<int> insert_sort(vector<int> v)
{
	for(int i = 0; i < v.size(); i++){
		int val = v[i];
		int j = i;
		for(; j > 0 && v[j-1] > val; j--){
			v[j] = v[j-1];
		}
		v[j] = val;
	}
	return v;
}

4.希尔排序

希尔排序又叫缩小增量排序。

设定一个初始步长,对元素进行分组的插入排序,然后逐渐缩小步长,每缩小一次步长,就会对同组元素进行一次插入排序,直到步长为0。

好处:刚开始步长最大,需要插入排序的元素少,速度快;而步长很小时元素都基本有序了,需要挪动的元素少,速度快。

我们还需要知道的是,希尔排序是对直接插入排序的一个改进,可以理解成先分组再直插排序。

vector<int> shell_sort(vector<int> v)
{
	int step = v.size()/2;
	while(step >= 1){
		for(int i = step; i < v.size(); i++){
			int j = i;
			int val = v[i];
			for(; j >= step && v[j-step] > val; j -= step){
				v[j] = v[j-step];
			}
			v[j] = val;
		}
		step /= 2;
	}
	return v;
}

5.归并排序

归并排序使用的是分治法,先将数组分为两部分,对两部分进行递归,这是分操作,知道数组个数为1以后执行归并再返回,依次返回执行归并操作,这是合操作。这就是分治法的思想。

void merge_sort(vector<int>& v, int l, int r)
{
	if(l == r)return;
	int mid = (l + r)/2;
	merge_sort(v, l, mid);
	merge_sort(v, mid+1, r);
	merge(v, l, r, mid);
}

void merge(vector<int>& v, int l, int r, int mid)
{
	vector<int> temp(r-l+1, 0);
	int index = 0;
	int left = l;
	int right = mid+1;
	while(left <= mid && right <= r){
		temp[index++] = v[left] < v[right] ? v[left++] : v[right++];
	}
	while(left <= mid){
		temp[index++] = v[left++];
	}
	while(right <= r){
		temp[index++] = v[right++];
	}
	index = 0;
	for(int i = l; i <= r; i++){
		v[i] = temp[index++]; 
	}
}

6.堆排序

通过前面的学习我们可以看到,如果构建一个二叉堆,最后每次从堆顶取出一个元素,那么最终取出元素就是有序的,不过如果要用来对数据按照从小到大排序,就不是构造小顶堆,而是大顶堆了,即堆顶元素大于等于其左右儿子节点。总结堆排序思路如下:

  1. 以O(N)时间复杂度构建N个元素的二叉堆。
  2. 以O(logN)时间复杂度删除一个堆顶元素,N个元素时间复杂度为O(NlogN)
  3. 由于删除一个堆顶元素时,就会空出一个位置,为了节省空间,将删除的堆顶元素放到数组末尾
  4. 当堆为空时,完成排序
  5. 由于数组元素从下标0开始,因此每个位置必须利用好。假设堆顶元素位置为i,那么左右儿子节点位置分别为2i+1,2i+2,父节点的位置为(i-1)/2
void heap_sort(vector<int>& v)
{
	for(int i = v.size()/2-1; i >= 0; i--){
		adjust_heap(v, i, v.size());
	}
	for(int i = v.size()-1; i > 0; i--){
		int temp = v[0];
		v[0] = v[i];
		v[i] = temp;
		adjust_heap(v, 0, i);
	}
}
void adjust_heap(vector<int>& v, int root, int length)
{
	int temp = v[root];
	while(root*2 + 1 < length){
		int child = root*2+1;
		if(child + 1 < length && v[child] < v[child+1]){
			child++;
		}
		if(temp < v[child]){
			v[root] = v[child];
		}else{
			break;
		}
		root = child;
	}
	v[root] = temp;
}

7.快速排序

中枢的选择是一大学问

void quick_sort(vector<int>& v, int left, int right)
{
	if(left >= right)
		return;
	int i = partition(v, left, right);
	quick_sort(v, left, i-1);
	quick_sort(v, i+1, right);
}
int partition(vector<int>& v, int left, int right)
{
	int pivot = v[left];
	int i = left+1;
	int j = right;
	while(1){
		while(i <= j && v[i] <= pivot){i++;}
		while(i <= j && v[j] >= pivot){j--;}
		if(i < j){
			int temp = v[i];
			v[i] = v[j];
			v[j] = temp;
		}else{
			break;
		}
	}
	v[left] = v[j];
	v[j] = pivot;
	return j;
}

8.基数排序

从个位数一位一位的进行排序

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值