真正要让小孩子也能够看懂,后面会持续更新(视频/图文),越来越完善......
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;
}