数据结构也到达了终点,最后的考试也步步紧逼。最后的时间里,有必要合理分配空闲时间,制定好学习计划,在考前充分复习,尽自己最大努力。这最后一章,介绍了与查找技术不同的排序技术,但同样的都是一些优秀的思想方法,思维上有些难度,不去深入理解,很容易出现细节方面问题。
一,排序的基本概念
1,稳定性的概念:同一数据重复出现,相对位置不发生变化则称稳定。
2,时间性能取决于比较次数+移动次数。
3,各类算法存储结构,顺序存储数组下标从1开始,0下标做辅助空间。
二,插入类的排序
1,直接插入排序,稳定
在插第i个记录时前i-1个已排好序
for (i=2; i<=n; i++) {
r[0]=r[i]; j=i-1;
while (r[0]<r[j]) { //r[0]做监视哨
r[j+1]=r[j];
j=j-1;
}
r[j+1]=r[0];
}
2,希尔排序(缩小增量排序),不稳定
将整个待排序记录分割成若干个子序列,在子序列内分别进行直接插入排序,待整个序列中的记录基本有序时,对全体记录进行直接插入排序。初始增量d=n/2(d个子序列同步进行)
for (d=n/2; d>=1; d=d/2){
for (i=d+1; i<=n; i++) {
r[0]=r[i];
j=i-d;
while (j>0 && r[0]<r[j])
{
r[j+d]=r[j]; //记录后移d个位置
j=j-d; //比较统一子序列的前一个记录
}
r[j+d]=r[0];
}
}
三,交换排序
1,相邻比序(冒泡),稳定
改进原版冒泡提高性能,减少比较次数,记录最后一次交换位置,下次交换到此结束,无交换时退出。
exchange=n;
while (exchange)
{
bound=exchange;
exchange=0;
for (j=1; j<bound; j++)
if (r[j]>r[j+1]) {
r[j]←→r[j+1];
exchange=j; //记录最后交换的位置
}
}
2,快速排序
首先选一个轴值(即比较的基准),通过一趟排序将待排序记录分割成独立的两部分,前一部分记录的关键码均小于或等于轴值,后一部分记录的关键码均大于或等于轴值,然后分别对这两部分重复上述方法,直到整个序列有序。可用二叉树描述,深度表示递归次数。
void QuickSort (int r[ ], int first, int end )
{
if (first < end) {
pivotpos = Partition (r, first, end ); //一次划分
//对前一个子序列进行快速排序
QuickSort (r, first, pivotpos-1);
//对后一个子序列进行快速排序
QuickSort (r, pivotpos+1, end );
}
}
int Partition(int r[ ], int first, int end)
{
i=first; j=end; //初始化
r[0]=r[i];
while (i<j)
{
while (i<j && r[0]<= r[j]) j--; //右侧扫描
if (i<j) {
r[i]=r[j]; i++; //将较小记录交换到前面
}
while (i<j && r[i]<= r[0]) i++; //左侧扫描
if (i<j) {
r[j]=r[i]; j--; //将较大记录交换到后面
}
}
r[i]=r[0];
retutn i; //i为轴值记录的最终位置
}
四,选择排序,无最好最坏情况之分。
1,简单选择排序,不稳定,时间复杂度永远是n方
基本思想:第i 趟在n-i+1(i=1,2,…,n-1)个记录中选取关键码最小的记录作为有序序列中的第i个记录。
for ( i=1; i<n; i++)
{
index=i; //index记录有序数组的最后位置
for (j=i+1; j<=n; j++)
if (r[j]<r[index]) index=j;
if (index!=i) r[i]<==>r[index];
}
2,堆排序,不稳定
堆是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子结点的值(称为小根堆),或每个结点的值都大于或等于其左右孩子结点的值(称为大根堆)。
要得到升序的序列则要构造大根堆
#include<bits/stdc++.h>
using namespace std;
int judge(int a[],int i,int j)
{
int k=i;
int l=2*k;
int t=a[k];
while(l<=j)
{
if(l<j&&a[l]<a[l+1])
l++;
if(t>a[l]) break;
else
{
a[k]=a[l];
k=l;
l=2*k;
}
}
a[k]=t;
}
void h(int a[],int n){
for(int j=n/2;j>=1;j--)
judge(a,j,n);
for(int i=1;i<n;i++)
{
//swap(a[1],a[n-i+1]);
int f=a[1];
a[1]=a[n-i+1];
a[n-i+1]=f;
judge(a,1,n-i);
}
}
int main()
{
int n,a[9999];
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
h(a,n);
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
}
五,归并排序(分治的思想)
将n个待排序的记录的序列看成n个长度为一的有序序列。
1,二路归并排序
归并排序的主要操作是归并,其主要思想是:将若干有序序列逐步归并,最终得到一个有序序列。 无法原地进行引入相同大小的数组存中间结果
#include<bits/stdc++.h>
using namespace std;
void judge(int a[],int b[],int s,int m,int t)
{
int i=s;
int j=m+1;
int k=s;
while(i<=m&&j<=t)
{
if(a[i]<=a[j]) b[k++]=a[i++];
else
b[k++]=a[j++];
}
if(i<=m)
while(i<=m)
{
b[k++]=a[i++];
}
else
while(j<=t)
b[k++]=a[j++];
}
void mp(int a[],int b[],int n,int h)
{
int i=1;
while(i<=n-2*h+1)
{
judge(a,b,i,i+h-1,i+2*h-1);
i+=2*h;
}
if(i<n-h+1) judge(a,b,i,i+h-1,n);
else for(int k=i; k<=n; k++)
b[k]=a[k];
}
void msort(int a[],int b[],int n)
{
int h=1;
while(h<n)
{
mp(a,b,n,h);
h=2*h;
mp(b,a,n,h);
h=2*h;
}
}
int main()
{
int n,a[9999],b[9999];
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
msort(a,b,n);
for(int i=1; i<=n; i++)
cout<<a[i]<<" ";
cout<<endl;
for(int i=1; i<=n; i++)
cout<<b[i]<<" ";
}
六,分配排序
基于分配和收集,不经过比较
1,桶式排序,稳定
int main(){
Node *s,*first; head *list;
int m;cin>>m;
list=new head[m];
for (int i=0;i<=m;i++){
list[i].first=NULL; list[i].rear=NULL;}
int n;
cin>>n;
first=NULL;
for( i=0;i<n;i++)
{ s=new Node; cin>>s->data; s->next=first; first=s; }
distribute(first,n,list);
collect(list,first,m);
Node *p=first;
while(p){
cout<<p->data<<"\t"; p=p->next; }
cout<<endl;
return 0;
}
2,基数排序,稳定
低位优先,尾插法
void distribute(Node *first, int n, head *list,int d){
Node *p,*q; p=first; int data, s,t;
while(p) {
data=p->data;
s=pow(10,d);t=s/10;data=data%s;data=data/t;
q=p->next;
if( list[data].first)
{list[data].rear->next=p; list[data].rear=p;}
else
list[data].first=list[data].rear=p;
list[data].rear->next=NULL;
p=q;
}
}
void collect( head *list, Node *&first,int m){
int i=0,j;
while(list[i].first==NULL) i++;
if(i>m) return;
first=list[i].first;
while(i<=m) {
j=i+1;
while(list[j].first==NULL) j++;
if(j>m) return;
list[i].rear->next=list[j].first;
i=j;
}
}
cin>>d;
for(i=1;i<=d;i++) //处理每一“位”(个位、十位...)
{
distribute(first,n,list ,i);
collect(list,first,m);
for (int i=0;i<=m;i++)
{
list[i].first=NULL;
list[i].rear=NULL;
}
}