算法:归并排序
时间复杂度:nlog(n) 原理:我不知道
空间复杂度:我不知道 原理:我不知道
重难点:
理解好临时数组temp的作用:是把有序的数据覆盖回原先的数组(后面详细说明)
代码(c++):
源码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],temp[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return ;
int mid = l + r>>1;
quick_sort(q, l, mid), quick_sort(q, mid + 1, r);
int i = l, j= mid + 1, k = 0;
while(i <= mid && j <= r)
if(q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
while(i<=mid)temp[k++] = q[i++];
while(j<=r)temp[k++] = q[j++];
for(i=l,j=0;i<=r;i++,j++)q[i] = temp[j];
}
int main()
{
int x;
cin>>x;
for(int i=0;i<x;i++) cin>>a[i];
quick_sort(a,0,x-1);
for(int i=0;i<x;i++) cout<<a[i]<<" ";
return 0;
}
注释码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;//const是关键字,修饰一个变量后,这个变量的值就不能在后文中发生任何修改
//比如 const int a=1;那么a++就是错的
//static也是一个关键字,修饰一个变量后,这个变量的数值会永久改变,常用于
//函数运行后希望保持改变后的数值;
//比如static int a=1;a++之后,a==2;这个值未发生改变时是永久性的。
int a[N],temp[N];
void quick_sort(int q[],int l,int r)
{
if(l>=r) return ;//当递推到只有一个元素时,那就没办法排序(或者说是不用排序),就返回
int mid = l + r>>1;//'>>'位运算,表示'/2'(除2)一次;同理'<<1'表示'*2'一次;而'>>2'表
//示/2两次(/4);
quick_sort(q, l, mid), quick_sort(q, mid + 1, r);//如果不是单个元素,那就死命分
int i = l, j= mid + 1, k = 0;//因为最后一次的时候会在本函数的第一条语句返回,所以执行这句
// 话的必然是最后一个递推的前面的函数(倒数第二,三,
//四。。。。。次);
//之所以强调倒数第几次,是因为后面就直接当作排完序进行存储和
//覆盖操作了。看不懂的先看看文末说明,再回来看代码。
while(i <= mid && j <= r)//用临时数组temp存储有序的(部分)元素,把小的放前面,大的放后面
if(q[i] <= q[j]) temp[k++] = q[i++];// 为什么不用括号?if/else天生一对。
else temp[k++] = q[j++];
while(i<=mid)temp[k++] = q[i++];//防止有某一半的存完了,但是另一半的还没存完,那就说明没
//存的部分比存完的那部分的最后一个都大,那就只要单独对没存
//完数据放到temp后面;
while(j<=r)temp[k++] = q[j++];//因为上文说的是(i <= mid && j <= r);所以录取后当j==但是
//i<r是,可能存在q[j]<q[i],所以temp录取了q[j];那么这
//时候j++了,j就>r了;所以也不用写说明if/else。
for(i=l,j=0;i<=r;i++,j++)q[i] = temp[j];//开始覆盖,不懂得看文末
}
int main()
{
int x;
cin>>x;
for(int i=0;i<x;i++) cin>>a[i];
quick_sort(a,0,x-1);
for(int i=0;i<x;i++) cout<<a[i]<<" ";
return 0;
}
下面是对为什么执行完“quick_sort(q, l, mid), quick_sort(q, mid + 1, r);” 语句就完成了排序的详细说明
误区:
①真的以为就是执行了 quick_sort(q, l, mid), quick_sort(q, mid + 1, r); 这一条完成了排序。
②没有理解到 元素所在位置 和 元素的值 不是绑死的
③不是只有定义成指针的变量才能充当指针的作用,函数中的i,j就是充当着指针的作用!只不过它们记录的是数组下标。
写的很琐碎,建议自己拿纸笔画图试试
假设: q[0]=7 q[1]=5 q[2]=76 q[3]=99 q[4]=123
先运行第一次quick_sort(q, l, mid), quick_sort(q, mid + 1, r);
下面我都用大括号表示分块了
那么数据就被分为两块
{ q[0]=7 q[1]=5 q[2]=76 } { q[3]=99 q[4]=123 }
第二次运行quick_sort(q, l, mid), quick_sort(q, mid + 1, r);
那么数据被分为四块
{ q[0]=7 q[1]=5 } { q[2]=76 } { q[3]=99 } { q[4]=123 }
前面说过了,只有一个元素的会被返回,所以这里就只剩下{ q[0]=7 q[1]=5 }要分,其他的我省略
第三次运行quick_sort(q, l, mid), quick_sort(q, mid + 1, r);
{ q[0]=7 } { q[1]=5 }
然后这两个因为也是一个元素,也被返回了;
怎么理解返回?其实就是把之前分开后变成单个的那一步拿回来。
也就是
{ q[0]=7 } { q[1]=5 } 回归变成了 { q[0]=7 q[1]=5 }
但是这里的!!!!表示!!!发生了变化,原先左侧的块用i表示地址(下标),右侧的用j表示地址(下标)
所以在下面这个代码中,才会有i,和j的拆分
while(i <= mid && j <= r)
if(q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
这个时候i表是q[0]=7的下标0,(即i==0)
同理,j表是q[1]=5的下标1,(即j==1)
所以代码运行后temp[0]==5;
temp[1]==7;
之后还有两个细节while:
while(i<=mid)temp[k++] = q[i++];
while(j<=r)temp[k++] = q[j++];
之后再说,这里用不到。
然后运行最后一个语句
for(i=l,j=0;i<=r;i++,j++)q[i] = temp[j];
这里的i=l其实是l=0,因为这里是最左边的递归块,要是再中间的位置,那l也要应当是最左侧的地址。
这个语句使得(//原先q[0]==7,q[1]==5)
q[0]=5
q[1]==7
之后又从
!!!!{ q[0]=7 q[1]=5 } { q[2]=76 }
!!!! 回归到了
!!!! { q[0]=5 q[1]=7 q[2]=76 }
!!!! !!!!!注意!!!!!!!!
!!!! 这里就是由于第一次回归,使得前两个位置的元素发生了改变
!!!! 同样的,这里的下标表示也是发生了改变(i==0,j==2)
然后运行while:
while(i <= mid && j <= r)
if(q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
!!!!!!!!!!!!怕没看到,每使用一次j或i,都会让其值自加一次!!!!!!!
因为
q[i(i==0)]=5 < q[j(j==2)]=76
所以temp[0]==q[i(i==0)]=5 //用了一次i,所以i自加了,所以后面的i==1而不是i==0
同理
q[i(i==1)]=7 < q[j(j==2)]=76
所以temp[i(i==1)]==q[i(i==1)]=7;//用了一次i,所以i自加了,所以后面的i==2而不是i==1
这个时候i==2了,不满足while(i <= mid && j <= r) 的i<mid(mid==1)
但这个时候q[j(j==2)]=76还没有录用,怎么办?
这时候就是两个while的细节作用
while(i<=mid)temp[k++] = q[i++];//不满足,直接掠过
while(j<=r)temp[k++] = q[j++];//满足,赋值(覆盖)
这个时候
temp[0]==5
temp[1]==7
temp{2]==76
然后,再次进行最后一个语句
for(i=l,j=0;i<=r;i++,j++)q[i] = temp[j];
所以
q[0]==5
q[1]==7
q{2]==76