排序算法八大排序总结

Posted zhaocx111222333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法八大排序总结相关的知识,希望对你有一定的参考价值。

#include<iostream>
#include<vector>
using namespace std;
//选择排序
//思路就是从前往后遍历,每个元素再次从本元素位置+1开始遍历,并和最小的交换
//用一个变量存最小值,注意要初始化为自身,因为就算不存在更小值也可以安全交换
//注意遇到相等的不要交换就可以保证稳定排序
//时间复杂度:O(N^2)--无论最好最坏都是
//空间复杂度:O(1)
void select_sort(vector<int>& v){
	
	for (size_t i = 0; i < v.size()-1; ++i){
		int idx = i;
		for (size_t j = i + 1; j < v.size(); ++j){
			if (v[j] < v[idx])//<=不稳定
				idx = j;
		}
		if (v[idx]!=v[i])//去掉且<=才不稳定
			swap(v[idx], v[i]);
	}
}




void create_heap(vector<int>& v){
	for (size_t i = 0; i < v.size(); ++i){
		//i为当前插入节点
		int cur = i;//当前插入节点可能会一直调整,所以要记录i
		int parent = cur / 2;
		while (v[cur]>v[parent]){
			swap(v[cur], v[parent]);
			cur = parent;
			parent = cur / 2;
		}
	}
}
void put_down(vector<int>& v,int root,int num){
	int left_child = root * 2 + 1;
	int right_child = root * 2 + 2;
	while (left_child < num){//如果左节点越界,右一定越界
		int child;
		if (v[left_child] < v[right_child] && right_child<num)//还需要判断右越界情况
			child = right_child;
		else
			child = left_child;//只有左节点,没有右节点就是左节点,或者左节点大
		
		//错误代码
		//int child = v[left_child]>v[right_child] ? left_child : right_child;
		if (v[root] < v[child]){
			swap(v[root], v[child]);
		}
		else
			break;
		root = child;
		left_child = root * 2 + 1;
		right_child = root * 2 + 2;
	}
}
//堆排序分为建堆+排序
//建堆就是在已确定数组的基础上开始调整,注意不是一边输入数据一边交换向上调整
//具体就是从0开始,和父节点比较并交换(等于就不交换),也是一个向上调整的过程(父节点=cur/2)
//需要注意的是排升序要建大堆,排降序建小堆
//因为建大堆最大的数据会在排序期间和最右子节点交换并固定,是一个先得到最大在逐级递减的过程,输出就是升序。
//
//排序的过程就是依次交换0和最大,堆顶向下调整,确定一个最右叶子结点
//这个最大节点从size-1一直到1,直到v[0]--v[1]交换就结束
//
//向下调整就是0号元素向下调整的过程,注意这个受到不断减小的最大值的影响(建议传参表示)
//逻辑就是若只有左节点就选择,都有就选择大的,都没有就跳过(用左节点做边界判断,再判断右是否存在和大小)
//确定孩子之后向下调整到cur大于等于孩子节点break
void heap_sort(vector<int>& v){
	//建堆O(N)
	//调整O(N*logN)--调整N-1次,每次lngN
	//时间复杂度O(N*logN+N)=O(N*logN)
	//不稳定
	//1.建堆
	create_heap(v);
	int sz = v.size();
	while (sz > 1){
		//2.交换0和最大
		swap(v[0], v[sz-1]);
		--sz;
		//3.向下调整0号元素
		put_down(v, 0, sz);
	}
}

//插入排序
//就是从1开始向后遍历,每个元素和前面的比较并让前面的向后覆盖,直到大于这个元素就放在他的下一个
//i为要插入的元素,j是已排序的最后一个,如果已排序的比待插入的大,后移
//平均时间复杂度:O(N^2)
//最坏情况复杂度:O(N^2)--反向有序
//最好情况复杂度:O(N)--有序
//空间复杂度:O(1)
//最多需要n(n−1)/2次比较
//最少需要n−1次比较
//稳定排序
//注意每一个要比较到比自己小的截止,要多算1个
void insert_sort(vector<int>& v){
	for (size_t i = 1; i < v.size(); ++i){
		int j = i - 1;
		//j是已排序的最后一个,第一次也就是v[0],看作有序
		int in_num = v[i];
		for (; j >= 0; j--){
			if (v[j]>in_num)
				v[j + 1] = v[j];
			else
				break;
		}
		//j在执行完循环后后会执行第三段--变成-1;这个--i,i--没影响
		v[j + 1] = in_num;
	}
}

//希尔排序--升级插入排序(多了一层for用来传递间隔)
//平均时间复杂度:O(N*logN)~O(N^2)
//最坏情况复杂度:O(N^2)
//最好情况复杂度:O(N*logN)
//空间复杂度:O(1)
//不稳定排序
void shell_sort(vector<int>& v)
{	//初始间隔,每次少一半
	for (int interval = v.size() / 2; interval > 0; interval /= 2)
	{
		for (size_t i = interval; i < v.size(); ++i)//++i
		//从v[interval]开始往后遍历,将遍历到的数据与其小组进行插入排序
		//第一个相当于有序,i是待插入的
		{
			int insert_num = v[i], j;	//v[i]代表第一个,把他存起来等前面的覆盖
			for (j = i - interval; j >= 0; j -= interval)//i-interval代表已排序最后一个
			{
				if (v[j] > insert_num)
					v[j + interval] = v[j];
				else
					break;
			}
			v[j + interval] = insert_num;
		}
	}
}

void bubble_sort(vector<int>& v){
	for (size_t i = 0; i < v.size() - 1; ++i){
		for (size_t j = 0; j < v.size() - 1 - i; ++j){
			if (v[j]>v[j + 1]){
				swap(v[j], v[j + 1]);
			}
		}
	}
}

//归并排序
void come(vector<int>& arr, int begin, int mid, int end){
	vector<int> tmp = arr;
	int begin1 = begin;		
	int end1 = mid;
	int begin2 = mid + 1;	
	int end2 = end;
	int idx = begin;
	while (begin1 <= end1&&begin2 <= end2){		
		if (arr[begin1] <= arr[begin2])			
			tmp[idx++] = arr[begin1++];			
		else
			tmp[idx++] = arr[begin2++];			
	}

	if (begin1 <= end1)		
		while (begin1<=end1){
			tmp[idx++] = arr[begin1++];
		}

	if (begin2 <= end2)
		while (begin2<=end2){
			tmp[idx++] = arr[begin2++];
		}
	arr = tmp;
}

//递归
void _merge_sort(vector<int>& arr,int begin,int end){
	if (begin >= end)	
		return;

	int mid = begin + (end - begin) / 2;		

	_merge_sort(arr, begin, mid);
	_merge_sort(arr, mid + 1, end);
	
	come(arr, begin, mid, end);
}

void merge_sort(vector<int>& arr){
	int n = arr.size();
	//子序列的步长
	int step = 1;		
	while (step < n){		
		for (int idx = 0; idx < n; idx += 2 * step){  
			//[begin,mid]  [mid+1,end]
			int begin = idx;
			int mid = idx + step - 1;

			//不存在第二个子序列,直接跳过
			if (mid >= n - 1)
				continue;	
			int end = idx + 2 * step - 1;
			//判断第二个子序列是否越界
			//假如是9个元素,最顶层step为8,还是会进入循环
			//但是最右多了一个,证明有第二序列不会continue
			//但是最后一步合并8+1的时候end越界,就会出错(最后一个是随机数)
			//最顶层也算是特殊情况
			if (end > n)
				end = n - 1;
			come(arr, begin, mid, end);
		}
		//更新步长
		step *= 2;
	}
	//递归
	//int end = arr.size() - 1;
	//_merge_sort(arr, 0, end);
}

//计数排序
//1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
//2. 时间复杂度:O(MAX(N, 范围))
//3. 空间复杂度:O(范围)
//4. 稳定性:稳定
void count_sort(vector<int>& v){
	int big = v[0], small = v[0];
	for (auto& e : v){
		if (e > big)
			big = e;
		if (e < small){
			small = e;
		}
	}
	vector<int> count(big - small+1, 0);//注意要多一个内容
	for (auto&e : v){
		count[e - small]++;				//把最小的放在0,依次递增,count数组中存放的是(num-small)数字的个数
	}
	int idx = 0;
	for (size_t i = 0; i < count.size(); ++i){
		while (count[i]--){				//再次输出到v数组里
			v[idx++] = i+small;			//遍历count数组,每次while直到不存在
		}								//注意插入的是索引+最小值
	}
}

void test(){
	vector<int> v = { 4, 6, 9, 7, 2, 5, 3, 1,8 };
	shell_sort(v);
	//bubble_sort(v);
	//insert_sort(v);
	//heap_sort(v);
	//select_sort(v);
	//_merge_sort(v,0,8);
	//count_sort(v);
	for (auto &e : v){
		cout << e << endl;
		cout << "---";
	}
}
int main(){
	test();

	system("pause");
	return 0;
}

快排:链接: link.

以上是关于排序算法八大排序总结的主要内容,如果未能解决你的问题,请参考以下文章

数据结构初阶第九篇——八大经典排序算法总结(图解+动图演示+代码实现+八大排序比较)

八大排序算法总结:快速排序

八大排序算法总结

八大排序算法C语言过程图解+代码实现(插入,希尔,选择,堆排,冒泡,快排,归并,计数)

糊涂算法之「八大排序」总结——用两万字,8张动图,450行代码跨过排序这道坎(建议收藏)

八大排序算法总结