2、C++信奥赛九大排序算法:冒泡排序、选择排序、插入排序、希尔排序、计数排序、桶排序、堆排序、归并排序、快速排序,真正要让小孩子也能够看懂,持续更新(视频/图文)......

真正要让小孩子也能够看懂,后面会持续更新(视频/图文),越来越完善......

    2、选择排序(从小到大排序) 


    思路:5 7 2 1 6
    第一轮假设第一个元素下标为最小值下标,去和后面的元素进行比较,
    找到比它小的,最小值下标更新,直到找到最小值,最后交换元素,确定第一个最小值,以此类推 
    时间复杂度:o(n^2) 
    空间复杂度:o(1)
    稳定性:不稳定 如 20 20 5,第一个20会和5交换,此时两个相同大小的20位置发生了改变 
    

	//选择排序(从小到大排序) 
	/*
	思路:5 7 2 1 6
	第一轮假设第一个元素下标为最小值下标,去和后面的元素进行比较,
	找到比它小的,最小值下标更新,直到找到最小值,最后交换元素,确定第一个最小值,以此类推 
	时间复杂度:o(n^2) 
	空间复杂度:o(1)
	稳定性:不稳定 如 20 20 5,第一个20会和5交换,此时两个相同大小的20位置发生了改变 
	*/ 
void selectSort(vector<int> &vec,size_t size){//在函数内部修改原始的 std::vector 要用& 
	for(int i=0;i<size;i++){
		int minindex=i;
		for(int j=i;j<size;j++){
			if(vec[minindex]>vec[j]){
				minindex=j;
			}
		}
		swap(vec[i],vec[minindex]);
	}
}

3、直接插入排序(从小到大排序) 
 

思想:(5) 7 2 1 6 
      (5 7) 2 1 6 
      (2 7  5) 1 6
分为有序和无序,第一次,第一个元素为有序,其他元素为无序,
1)(第二个元素)后面的和比它大直接加入有序尾巴,
2)如果比有序数组的某个数小,那么有序数组这个位置及其之后的元素往后移动一位,插入 
    时间复杂度:o(n^2) ,虽然有三个循环嵌套,但最内层循环是有if就是有限次,
    只有最坏情况才会每次都循环,所以算o(1) 
    空间复杂度:o(1)
    稳定性:稳定
 

#include <bits/stdc++.h>
using namespace std;

//直接插入排序(从小到大排序) 
/*
思想:(5) 7 2 1 6 
	  (5 7) 2 1 6 
	  (2 7  5) 1 6
分为有序和无序,第一次,第一个元素为有序,其他元素为无序,
1)(第二个元素)后面的和比它大直接加入有序尾巴,
2)如果比有序数组的某个数小,那么有序数组这个位置及其之后的元素往后移动一位,插入 
	时间复杂度:o(n^2) ,虽然有三个循环嵌套,但最内层循环是有if就是有限次,
	只有最坏情况才会每次都循环,所以算o(1) 
	空间复杂度:o(1)
	稳定性:稳定
*/ 
void insertSort(vector<int> &vec,size_t size){//在函数内部修改原始的 std::vector 要用& 
	for(int j=1;j<size;j++){//j是外循环,控制无序的指针 
		for(int i=0;i<j;i++){//i是内循环,控制有序的指针,遍历次数和j相关 
			if(vec[j]<vec[i]){//从左往右比较(已升序),无序<有序 
				int temp=vec[j];//先保存无序元素
				for(int k=j-1;k>=i;k--){//从尾巴开始往后移动 
					vec[k+1]=vec[k]; 
				} 
				vec[i]=temp;
			}
		}
	}
}
int main() {

	vector<int> a={5,7,2,1,6};
	//selectSort(a,a.size());
	insertSort(a,a.size());
	for(int i=0;i<a.size();i++){
		cout<<a.at(i)<<" ";
	}	
	return 0;
}

4、希尔排序

思想:5 7 2 1 6  gap=5/2=2  第一次分成{5,2}{7,1}{6} 排序{2,5}{1,7}{6}得到{2,1,5,7,6}
      2 1 5 7 6  gap=2/2=1  全组进行直接插入排序
1)使用增量gap=n/2进行分组,每次对每个小组进行直接插入排序 ,直到gap==1为止
    时间复杂度:o(n^2) ,情况好的时候o(n^1.?)
    空间复杂度:o(1)
    稳定性:不稳定,分组的时候相对位置会发送交换,组外不稳定,组内稳定
 


//希尔排序(从小到大排序) /缩小增量排序
/*
思想:5 7 2 1 6  gap=5/2=2  第一次分成{5,2}{7,1}{6} 排序{2,5}{1,7}{6}得到{2,1,5,7,6}
	  2 1 5 7 6  gap=2/2=1  全组进行直接插入排序
1)使用增量gap=n/2进行分组,每次对每个小组进行直接插入排序 ,直到gap==1为止
	时间复杂度:o(n^2) ,情况好的时候o(n^1.?)
	空间复杂度:o(1)
	稳定性:不稳定,分组的时候相对位置会发送交换,组外不稳定,组内稳定
*/
void ShellInsert(vector <int> &vec,int start,int gap) { //start分组的起始位置,gap间距
	for(int j=start+gap; j<vec.size(); j+=gap) { //j是外循环,控制无序的指针
		for(int i=start; i<j; i+=gap) { //i是内循环,控制有序的指针,遍历次数和j相关
			if(vec[j]<vec[i]) { //从左往右比较(已升序),无序<有序
				int temp=vec[j];//先保存无序元素
				for(int k=j-gap; k>=i; k-=gap) { //从尾巴开始往后移动
					vec[k+gap]=vec[k];
				}
				vec[i]=temp;
			}
		}
	}
}
void ShellSort(vector <int> &vec,size_t n) {
	for(int gap=n/2; gap>=1; gap/=2) { //分组次数
		//通过gap组内排序
		for(int i=0; i<gap; i+=gap) {
			//调用插入排序
			ShellInsert(vec,i,gap);
		}
	}

}

5、计数排序


{5,7,2,1,6}
新数组    0 1 1 0 0 1 1 1 原数组元素出现次数 
新数组下标0 1 2 3 4 5 6 7 

思路:将原数组中最大值作为最大下标,建立一个数组,借助数组来保存原数组中每个数字出现的次数, 
从头顺序输出下标,出现几次输出几次,0次那么不输出。 
 

void CountSort(vector <int> &vec) {
	//1.先找最大值 
	int maxx=vec[0];
	for(int i=1; i<vec.size(); i++) {
		if(vec[i]>maxx) {
			maxx=vec[i];
		}
	}
	//数组中找最大值方法2int maxx=*max_element(vec.begin(),vec.end()) 正常返回的是一个迭代器,
	//使用的是数组,max_element 返回的是指针类型
	//2.根据最大值开辟计数数组 
	vector <int> temp(maxx);
	//3.将原数组放入新数组并计数 
	for(int i=0; i<vec.size(); i++) {
		temp[vec[i]]++;
	}
	vec.clear();//清空原数组 
	for(int i=0; i<=maxx; i++) {
		while(temp[i]!=0){
			vec.push_back(i); //把i加入原数组尾巴 
			temp[i]--;
		}
	}

}

补充知识:

/*
1. int *countArr = new int [maxx + 1];
这行代码的作用是动态分配一个整数数组。
new int [maxx + 1]:使用 new 运算符来动态分配内大小为 maxx + 1 的整数数组。
int *countArr:该指针指向数组的首地址。
2. memset(countArr, 0, sizeof(int) * (maxx + 1));
把数组中的所有元素初始化为 0。
memset: <cstring> 作用是将指定内存块的每个字节都设置为特定的值。
countArr:置的内存块的起始地址
0:表示要设置的值。由于是按字节设置,这里把每个字节都设置为 0,对于整数来说,这就相当于把整数初始化为 0。
sizeof(int) * (maxx + 1):这是第三个参数,设置的内存块的大小(以字节为单位)。
*/ 

6、桶排序

{5,7,2,1,6} 6/5+1=2
5-min+1/num

思路:找出最大值max和最小值min,用max-min/n+1得到桶的个数,每个桶的大小为n
因为有可能都在一个桶里面,之后每个桶内部进行排序
 

void BucketSort(vector<int> &vec) {
	int maxx=*max_element(vec.begin(),vec.end());
	int minx=*min_element(vec.begin(),vec.end());
	int num=(maxx-minx)/vec.size()+1;//桶的个数
	//定义桶,其实是二维数组
	vector<vector<int>> Bucket(num);
	//将原数组元素放入对应桶里面 
	for(int i=0; i<vec.size(); i++) {
		int bindex=(vec[i]-minx+1)/vec.size();//元素所对应桶下标
		Bucket[bindex].push_back(vec[i]);
	}
	for(int i=0; i<Bucket.size(); i++) {//对桶内部元素进行排序 
		sort(Bucket[i].begin(),Bucket[i].end());
	}
	vec.clear();//清空原数组元素 
	//把数字元素放到对应桶内
	for(int i=0; i<Bucket.size(); i++) {//桶数 
		for(int j=0; j<Bucket[i].size(); j++) {//桶内大小 
			vec.push_back(Bucket[i][j]);  
		}
	}


}

7、堆排序

思路:建立大根堆,要求每个父节点>子节点中最大的,不符合要调整,
每次确定最大值为vec[0]把它跟最后一个数组元素交换
父节点floor(i-1/2)
左孩子l=2*i+1,右孩子=r=2*i+2
最后一个父节点=n/2-1 ,n是结点个数

//堆排序

void Build_Max_Heap(vector<int> &vec,int start,int end) { //起始和最后位置
	int cur=start;//局部大根堆父节点
	int l=2*cur+1;//左孩子

	for(; l<=end; cur=l,l=2*cur+1) { //能够往下找,此时父节点cur变成孩子所在位置
		if(l<end&&vec[l]<vec[l+1])l++;//左孩子<右孩子,指针指向右孩子,!!!l<end防止越界 
		if(vec[l]>vec[cur])swap(vec[l],vec[cur]) ;//孩子>父节点,交换
		else break;//跳出,从最后一个父节点开始构建,所以只要符合大根堆条件就跳出循环
	}
}
void HeapSort(vector<int> &vec) {
	for(int i=vec.size()/2-1; i>=0; i--) {
		Build_Max_Heap(vec,i,vec.size()-1);//构建一次大根堆
	}
	for(int i=vec.size()-1; i>0; i--) {//i不用==0,只剩一个元素 
		swap(vec[0],vec[i]); //元素交换
		Build_Max_Heap(vec,0,i-1);//第二次构建大根堆是从vec[0]开始往下,!!!这里是0不是i 
	}

}

8、快速排序

//二分查找,递归,快速排序
/*{5,7,2,1,6,20,25}-->1 2 5 6 7 20 25转有序先,再二分
递归:1)自己调用自己 2)有结束条件,先进后出类似栈,层层调用上一层,等结束后执行当前层

二分查找:必须是有序的,有target目标值,有mid=right-left/2,left指针开始,right指针结尾
用target和下标为mid的数组元素比较,
如果left>right返回-1;如果target>vec[mid],那么往右边找;小于的话往左边找;如果相等,则返回mid;如果都没有返回-1

快速排序:
思路:设第一个元素为基准tmp,借助两个指针i和j,i开头,j结尾
用vec[j]和tmp比较,循环比较,在i<j前提下,
如果vec[j]>tmp,让j--往左继续,
如果vec[j]<tmp,把vec[i++]=vec[j]此时i++,

用vec[i]和基准tmp比较,循环比较,在i<j前提下,
如果 vec[i]<tmp,让i++往右继续 ,
如果vec[i]>tmp,把vec[j--]=vec[i] 此时j--,

*/

void QucikSort(vector<int> &vec,int l,int r) {
	if(l>=r) {//等于号不要少,说明已经找到基准位置,直接结束 
		return;
	}
	int tmp=vec[l];
	int i=l,j=r;
	while(i<j) {
		while(i<j&&vec[j]>tmp) {
			j--;
		}
		if(i<j) { //这里要写i<j,因为上面j可能--,否则当i==j的时候执行vec[i++]=vec[j]
			//i会++>j那么就不知道基准要放在i还是j的位置
			vec[i++]=vec[j];
		}
		while(i<j&&vec[i]<tmp) {
			i++;
		}
		if(i<j) { //这里要写i<j,因为上面j可能--,否则当i==j的时候执行vec[i++]=vec[j]
			//i会++>j那么就不知道基准要放在i还是j的位置
			vec[j--]=vec[i];
		}
	}
	//这里i==j,把基准放到对应位置, 
	vec[i]=tmp; 
	//才找到一个基准位置,往左接着递归调用快排 
	QucikSort(vec,l,i-1);
	//往右接着递归调用快排
	QucikSort(vec,i+1,r);
}
int BinarySearch(vector<int> &vec,int left,int right,int target) {
	int mid=left+right/2;//取中间值 
	if(left>right) {
		return -1;
	}
	if(target==vec[mid]) {
		return mid;
	}
	if(target>vec[mid]) {
		return BinarySearch(vec,mid+1,right,target);
	}
	if(target<vec[mid]) {
		return BinarySearch(vec,left,mid-1,target);
	}

	return -1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值