排序(练习)

1.设数组A[n]中存放有n个不同的整数,且每个整数的值均为0~n-1。设计一个算法,在O(N)的时间内将A中所有整数按从小到大排好序。

解析:从i=0开始逐个检查A[i]。若A[i]≠i,交换A[i]与A[A[i]],将A[i]送入最终应在的位置;若A[i]=i,检查下一个A[i]直到A[i]检查完毕

void adjust(int A[],int n){
 	int i,j;
 	int count[maxsize],posit[maxsize],tmp[maxsize];
 	for(i=0;i<n;i++) count[i]=0;   //将count数组初始化
 	for(i=0;i<n;i++) count[A[i]]++;     //count中对应位置元素加1
 	posit[0]=0;    
 	for(i=1;i<n;i++) posit[i]=posit[i-1]+count[i-1];   //将posit数组按1,2,3,4.。赋值
 	for(i=0;i<n;i++) { j=A[i]; tmp[posit[j]++]=A[i];}   
 	for(i=0;i<n;i++) A[i]=tmp[i];  将tamp中的值依次赋值给A,相当于完成排序
	 
 }
 /*
例:{5,4,2,0,6,3,1,7}
*/

2.设计一个算法merge,利用直接插入排序思想实现二路归并排序。要求空间复杂度为O(1).设算法中参加归并的两个有序表的地址范围分别是left~mid和mid+1 ~right,归并后结果归并段放在原地。

解析:对于后一个表i=mid+1,mid+2……right从i-1开始,逆向检查,凡是比他大的向后移动一个位置,再把它插入,使之有序;

void merge_Insert(DataList& L,int left,int mid,int right){
    int i,j;
    DataType tmp;
    for(i=mid+1;i<=right;i++)  //循环检查后一个表的元素
        if(L.data[i]<L.data[i-1]){
            tmp=L.data[i];
            for(j=i-1;j>left;j--)逆向检查前一个表的元素
                if(L.data[j]>tmp)
                    L.data[j+1]=L.data[j]; //将大于插入元素的元素后移
                else break;                    //若小于或等于,找到插入位置
            L.data[j+1]=tmp;//插入
        }
}

3.编写算法,在用单链表存储的待排序数据序列上进行简单选择排序。

解析:每趟在原始链表摘下排序码最大的结点(几个排序码相等时为最前面的结点),把它插入到结果链表的最前端。由于原市链表摘下的排序码越来越小,在结果链表前端插入的排序码也越来越小,最后形成的结果链表中的结点按照排序码非递间的顺序有序连接;

void LinkSelectsort(LinkList& head){
    LinkNode *h=head->link,*p,*q,*r,*s;
    head->link=NULL;
    while(h!=NULL){//持续扫描原链表
        p=s=h; q=r=NULL; //指针s和q记忆最大结点和前驱
        while(p!=NULL){ //扫描原链表,寻找最大结点s
            if(p->data > s->data){s=p;r=q;} { s=p; r=q;} //寻找更大的并记忆它
            q=p;p=p->link;                   
        }
        if(s==h) h=h->link; //最大结点在原链表前端    
        else r->link=s->link;    //最大结点在原链表表内
        s->link=head->link; head->link=s;    //结点s擦汗如到结果链前端
}
}

4.设计一个算法使得在O(n)的时间内重排数组,将所有取负值的排序码排在所有取正值(非负)的排序码之前

解:参照快速排序的划分思想,可看作以0为枢轴队数组元素进行排序

void reArrange(DataList& L){
    int i=0,j=L.n-1;    DtaType tmp;
    while(i<j){
        while(i<j &&L.data[j]>=0) j--;
        while(i<j &&L.data[j]<0)  i++;
        if(i<j){
            tmp=L.data[i]; L.data[i]=L.data[j]; L.data[j]=tmp;
            i++;j--;
}
}
}

5.在一组无序元素中查找关键码第k(k>=1)小的元素。若查找成功,返回该元素的值,否则返回-1;

解析:为寻找第k小的元素,对序列做一趟划分,分成左右两个子序列,基准元素放到m位置,若m+1=k,则找到第k小的元素,返回其值即可;若m+1>k,则第k小元素要到m左子序列中去寻找;若m+1<k,则第k小的元素要到m右子序列中寻找。

DataType Find_kth(DataList& L,int k){
//k从1算起
    if(k<1 ||k>L.n) return -1;
    int left=0,right=L.n-1,m;
    while(1){
        m=Partition(L,left,right);
        if(m+1>k) right=m-1;  //第k个子序列在左子序列
        else if(m+1<k) left=m+1; //第k个子序列在右子序列
        else return L.data[m];
}
}

int Partition(DataList& L,int low,int high){
    int i=low,j=high;
    DataType pivot=L.data[low];
    while(i<j){
        while(i<j && L.data[j]>=pivot) j--; //反向寻找比基准与元素小的
        if(i<j) L.data[i++]=L.data[j];        //比基准元素小者 移动到低端
        while(i<j && L.data[i]<=pivot) i++;    //正向寻找比基准元素大的
        if(i<j) L.data[j--]=L.data[i];        //比基准元素大者移到高端
} 
     
}

//插入法
维护一个长度为k的数组A的有序数组,用于存储已知的K个较大的元素。然后遍历无序数组,每遍历到一个元素,和数组A中的最小元素进行比较,如果小于等于数组A中的最小元素,继续遍历;如果大于数组A中的最小元素,则插入到数组A中,并把曾经的最小元素"挤出去"。
//大根堆

6.设待排序元素存储于整数数组A[n]中,且n很大,设计算法,只要求得到前k个最大的元素,且并不要求做完全排序。时空复杂度尽可能低。

解:设排序区间的端点为low和high,对其做一趟划分,划分点i把排序区间分为两部分,判断右半部分的元素个数high-i是否等于k;

如果等于,则找到了最大的k个元素,返回位置i+1;

如果high-i<k,计算还需要再找最大元素个数k=k-high+i,把排序区间缩小到左半部分high=i,递归查找;

如果high-i>k,把排序区间缩小到右半部分low=i,递归查找

int findmax_k(int A[],int k,int low,int high){
    //函数返回第k大的元素在数组的位置,它后面就是数组中最大的k个元素
    int i,j;
    int tmp=A[low];
    while(i<j){
        while(j>i &&A[j]>=tmp) j--;
        if(j>i) { A[i]=A[j];i++ }
        while(i>j &&A[j]<=tmp) i++;
        if(i<j) { A[j]=A[i]; j-- }       
}
    A[i]tmp;
    if(high-i==k) return i+1;  //找到第k个最大元素
    else if(high-i>k)
        return findmax_k(A,k,i+1,high);  //到右半部分查找
    else return findmax_k(A,k-high+i,low,i); //到左半部分查找
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值