一、洛谷排序第一题<快速排序>
1、最初的快速排序
void qsort(int l,int r) //最普通最基本的快速排序
{
int i,j,mid,p;
i=l; j=r;
mid=a[(r+l)/2]; //找中间基准值
do
{
while(a[i]<mid)i++;
while (a[j]>mid)j++;
if(i<=j)
{
p=a[i];a[i]=a[j];a[j]=p;i++;j--;
}
}while(i<=j); //左右较基准值进行交换
if(l<j) qsort(l,j); //不断缩小左区间
if(i<r) qsort(i,r);} //不断缩小右区间
}
- 基准数位置。
基准数的位置在教科书中一般选为最左边或最右边,我一般习惯于选最左边。不同选择对执行没有较大的影响。
- 左右移动的方式
目前我所见到的移动的方式,主要有两种,
一是j(从右)找到一个比基准数(记作k)小的数,然后让i(从左)找到一个比k大的数,然后进行交换。直到i,j相遇开始分治处理;
void quicksort(int x,int y)
{
int i=x,j=y;
int k=a[x];
if(i>=j)
return;
while(i<j)
{
while(i<j&&a[j]>k)
j--;
if(i<j)
{
a[i]=a[j];
i++;
}
while(i<j&&a[i]<=k)
i++;
if(i<j)
{
a[j]=a[i];
j--;
}
}
a[i]=k;
quicksort(x,i-1);
quicksort(i+1,y);
}
另一种是 基准数记作k,基准数下标记作x
① i(从左)找到一个比k大的数,a[x]=a[i];
② j(从右)找到一个比k小的数,a[i]=a[j];
③ i(从左)找到一个比k大的数,a[j]=a[i];
④ 重复②③直到i==j;
⑤ 将a[i]赋值为基准数k;
第一种是快排的原始思想,交换->分治,但是借用了t来交换a[i]和a[j];
第二种的思想巧妙一些,没有用一次三句话操作,而是每次交换,都可以保证a[i]>a[j]。
void quicksort(int x,int y)
{
int i=x,j=y;
int k=a[x];
if(i>=j)
return;
while(i<j)
{
while(i<j&&a[j]>k)
j--;
if(i<j)
{
a[i]=a[j];
i++;
}
while(i<j&&a[i]<=k)
i++;
if(i<j)
{
a[j]=a[i];
j--;
}
}
a[i]=k;
quicksort(x,i-1);
quicksort(i+1,y);
}
以上谈的快速排序两种方法,大致上没有较大区别,只不过在处理交换操作时,一个老老实实用了交换,另一个用了较巧妙的“赋值”,总体上看复杂度不相上下,但是我想当数据较大的时候可能会还是会有所区别。 如有不对的地方,敬请指正。
2、数据结构找堆排
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<ctime>
#include<cstring>
using namespace std;
int n,a[100001],len;
int main()
{
scanf ("%d",&n);
for (int b=1;b<=n;++b)
{
scanf ("%d",&a[b]);
len=b;
while (len!=1)
{
if (a[len]<a[len/2])
{
int t=a[len/2];
a[len/2]=a[len];
a[len]=t;
}
else break;
len/=2;
}
}
for (int b=1;b<=n;++b)
{
printf ("%d ",a[1]);
a[1]=a[n-b+1];len=1;
while(len*2<=n-b)
{
int next=len*2;
if ((a[next]>a[next+1] && next<n-b))next++;
if (a[len]<=a[next])break;
int t=a[len];a[len]=a[next];a[next]=t;
len=next;
}
}
return 0;
}
3、map桶排序
#include<iostream>
#include<map>
using namespace std;
map<int,int>a;//创建一个桶,使用map不爆空间
int main(){
int n,t,x;
cin>>n;
for(int i=1;i<=n;i++){
cin>>t;
a[t]++;
/*这里使用了map的一个性质:如果还没有对t做过映射,
则访问a[t]会自动给t生成一个映射,
此时a[t]的值为第二个(key)类型的默认值(此处是0)*/
}
for(map<int,int>::iterator/*迭代器*/ it=a.begin();it!=a.end();it++){
x=it->second;
while(x--){
cout<<it->first<<' ';
}
}
}
二、书上的归并排序
void msort(int s,int t)
{
if(s==t)retrurn; //如果只有一个数字则返回
int mid=(s+t)/2;
msort(s,mid); //分解左序列
msort(mid,t); //分解右序列
int i=s,j=mid+1,k=s; //接下来合并
while(i<=mid&&j<=t)
if(a[i]<=a[j])
{
r[k]=a[i];k++;i++;
}
else
{
r[k]=a[j];k++;j++;
}
while(i<mid) //复制左边子序列剩余
{
r[k]=a[i];k++;i++;
}
while(j<=t) //复制右边子序列剩余
{
r[k]=a[j];k++;j++;
}
for(int i=s;i<=t;i++)
a[i]=r[i];
}
三、做题的总结
1、输入数字当作字符串时,只有在长度相同情况下可以直接比较大小,否则只能用高进度比较大小。
2、优化后的快速排序以及其他7大排序总结
http://ms.csdn.net/geek/252174