雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

排序 第K大等问题总结

Posted on 2013-10-29 19:08  huhuuu  阅读(463)  评论(0编辑  收藏  举报

在公司面试时,当场写排序比较多,虽然都是老掉牙的问题,还是要好好准备下

快速排序,以第一个元素为关键词比较,每次比较结束,关键词都会去到最终位置上

//7 3 2 9 8 3 4 6
//7 3 2 9 8 5 4 6
//7 5 2 9 8 3 4 6

#include<stdio.h>
int s[10999];
void mysort(int left,int right){
    if(left>=right)return;

    int mid,key=s[left];
    int ll=left,rr=right;
    while(ll<rr){
        while(s[rr]>=key&&ll<rr)rr--;
        s[ll]=s[rr];
        while(s[ll]<=key&&ll<rr)ll++;
        s[rr]=s[ll];
    }s[ll]=key;
    mysort(left,ll-1);
    mysort(ll+1,right);
}

int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int i,k;
        for(i=1;i<=n;i++){
            scanf("%d",&s[i]);
        }

        mysort(1,n);
        for(i=1;i<=n;i++){
            printf("%d\n",s[i]);
        }
    }

    return 0;
}
View Code

改了下,更通俗了些

#include<stdio.h>
#include<time.h>
#include<iostream>
using namespace std;

int shu[909]={0,5,2,9,7,3,6,4,8,1};

void quickSort(int left,int right){
    if(left>=right) return ;
    int ll=left,rr=right;
    int temp=shu[left];
    while(ll<rr){
        while(temp<=shu[rr]&&rr>left)rr--;
        while(temp>=shu[ll]&&ll<right)ll++;
        if(ll>=rr)break;
        swap(shu[ll],shu[rr]);
    }
    swap(shu[left],shu[rr]);
    quickSort(left,rr);
    quickSort(rr+1,right);
}

int main(){
    int i,n=100;
    for(i=1;i<=n;i++){
        shu[i]=rand()%1000;
        printf("%d ",shu[i]);
    }
    quickSort(1,n);
    
    for(i=1;i<=n;i++){
        printf("%d ",shu[i]);
    }
    getchar();
}
View Code

 

 

第k大的元素,其实就是在快速排序的基础上对递归做了个限制,就是对一边进行递归, 比如Kth的位置在key的左边,我们就递归(left,ll-1);反之(ll+1,right)

//7 3 2 9 8 3 4 6    1
//7 3 2 9 8 3 4 6    2
//7 3 2 9 8 3 4 6    3
//7 3 2 9 8 3 4 6    4
//7 3 2 9 8 3 4 6    7

#include<stdio.h>
int s[10999];

int findKth=0,kth;
void kth_element(int left,int right){
    if(left>=right)return;
    if(findKth==1){
        return ;
    }

    int mid,key=s[left];
    int ll=left,rr=right;
    while(ll<rr){
        while(s[rr]>=key&&ll<rr)rr--;
        s[ll]=s[rr];
        while(s[ll]<=key&&ll<rr)ll++;
        s[rr]=s[ll];
    }s[ll]=key;
    if(kth<ll)
        kth_element(left,ll-1);
    else if(kth>ll)
        kth_element(ll+1,right);
    else{
        findKth=1;
        return ;
    }
}


int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int i,k;
        for(i=1;i<=n;i++){
            scanf("%d",&s[i]);
        }
        scanf("%d",&kth);
        findKth=0;
        kth_element(1,n);
        printf("%d\n",s[kth]);

        //mysort(1,n);
        for(i=1;i<=n;i++){
            printf("%d ",s[i]);
        }printf("\n");
    }

    return 0;
}
View Code

改的适合自己的模板

#include<stdio.h>
#include<time.h>
#include<iostream>
using namespace std;

int shu[909]={0,5,2,9,7,3,6,4,8,1};
int key,ret;
int ok;

void Kth(int left,int right){
    if(ok==1)return;
    if(left>=right) return ;
    int ll=left,rr=right;
    int temp=shu[left];
    while(ll<rr){
        while(temp<=shu[rr]&&rr>left)rr--;
        while(temp>=shu[ll]&&ll<right)ll++;
        if(ll>=rr)break;
        swap(shu[ll],shu[rr]);
    }
    swap(shu[left],shu[rr]);
    if(rr==key){
        ok=1;
        ret=shu[rr];
        return ;
    }
    
    if(key<rr)
        Kth(left,rr);
    else
        Kth(rr+1,right);
}

int main(){
    int i,n=20;
    srand(time(NULL));
    for(i=1;i<=n;i++){
        shu[i]=rand()%1000;
        printf("%d ",shu[i]);
    }printf("\n");
    
    key=9;
    ok=0;
    Kth(1,n);
    
    for(i=1;i<=n;i++){
        printf("%d ",shu[i]);
    }printf("\n");
    printf("kth = %d\n",ret);
    getchar();
}
View Code

 

 

根据堆的定义,堆是一个完全二叉树,手写了个大顶堆,其实堆就两个操作,入堆(需要向上调整),出堆(需要向下调整),堆排序就是依次把堆顶元素出堆

向上调整不需要选择,因为祖先只有一个;向下调整需要选择是从左子树还是右子树,因为儿子可能有两个

#include<iostream>
#include<cstdio>
#include<time.h>
using namespace std;
double test[10009],s[10009];

int head_size;
void head_push(int k,double temp){//入堆其实是一个向上调整的过程
    int n=k;
    s[k]=temp;
    while(n!=1){
        if(s[n/2]<s[n]){
            swap(s[n/2],s[n]);
        }
        n=n/2;
    }
    return ;
}

void head_pop(){//堆顶元素出堆其实是向下调整的过程
    int n=1;
    s[1]=s[head_size+1];
    while(n*2<=head_size){
        if(n*2+1<=head_size){//右儿子存在
            if(s[n]>=s[2*n+1]&&s[n]>=s[2*n])break;//父节点比两个儿子都大退出
            if(s[2*n+1]>s[2*n]){ //右儿子比左儿子大
                swap(s[n],s[2*n+1]);
                n=n*2+1;
            }else{ //左儿子比右儿子大
                swap(s[n],s[2*n]);
                n=n*2;
            }
        }
        else{//只存在左儿子
            if(s[n]>=s[2*n])break;//父节点比左儿子大退出
            swap(s[n],s[2*n]);
            n=n*2;
        }
    }
    return;
}

void headSort(double *input,int left,int right){
    int i;
    head_size=0;
    for(i=left;i<=right;i++){
        head_size++;
        head_push(head_size,input[i]);
    }
    
    for(i=left;i<=right;i++){
        input[i]=s[1];
        head_size--;
        head_pop();
    }
    
    int half=(left+right)>>1;
    double temp;
    for(i=left;i<=half;i++){//因为是大顶堆,所以要调整
        temp=input[i];input[i]=input[right-i+1];input[right-i+1]=temp;
    }

}

int main(){
    freopen(" data.txt","r",stdin);
    time(NULL);
    int i;

    for(i=1;i<=10000;i++){
        scanf("%lf",&test[i]);
    }
    headSort(test,1,10000);

    printf("第1个:%lf\n",test[1]);
    printf("第10个:%lf\n",test[10]);
    printf("第100个:%lf\n",test[100]);
    printf("第1000个:%lf\n",test[1000]);
    printf("第10000个:%lf\n",test[10000]);

    return 0;
}
View Code

//发现原来的堆排序写的有点罗嗦,改了一下

#include<iostream>
#include<stdio.h>
using namespace std;

int head_size;
int s[109]={0,2,8,1,3,9,4,7,6,5};
int temp[109];

//大顶堆
void shiftUp(int a[],int i){
    while(i>1&&a[i]>a[i/2]){
        swap(a[i],a[i/2]);
        i>>=1;
    }
}

void shiftDown(int a[],int i,int n){
    while((i*2)<=n){
        i<<=1;
        if((i+1)<=n && a[i+1]>a[i])i++;
        if(a[i]>a[i/2])swap(a[i],a[i/2]);
        else break;
    }
}

void headSort(int a[],int n){
    head_size=n;
    int i;
    for(i=1;i<=n;i++){
        shiftUp(s,i);
    }
    for(i=1;i<=n;i++){
        temp[i]=s[1];
        s[1]=s[n+1-i];
        head_size--;
        shiftDown(s,1,head_size);
    }

    for(i=1;i<=n;i++){
        s[i]=temp[i];
    }
}


int main(){
    int n=9,i;
    headSort(s,n);
    for(i=1;i<=n;i++){
        printf("%d\n",s[i]);
    }

    return 0;
}
View Code

 //这个数据量可以控制

#include<stdio.h>
#include<time.h>
#include<iostream>
using namespace std;

int shu[909]={0,5,2,9,7,3,6,4,8,1};
int newShu[909];

void shiftUp(int a[],int n){
    int i=n,next;
    while(i>1){
        next=i>>1;
        if(a[i]>a[next])
            swap(a[i],a[next]);
        i=next;
    }
}

void shiftDown(int a[],int n){
    int i=1;
    while((i<<1)<=n){
        i=i<<1;
        if((i+1)<=n&&a[i+1]>a[i])i++;
        if(a[i]>a[i/2])swap(a[i],a[i/2]);
    }
}

void headSort(int n){
    int i;
    for(i=1;i<=n;i++){
        shiftUp(shu,i);
    }
    for(i=1;i<=n;i++){
        newShu[i]=shu[1];
        shu[1]=shu[n-i+1];
        shiftDown(shu,n-i);
    }
}

int main(){
    int i,n=100;
    srand(time(NULL));
    for(i=1;i<=n;i++){
        shu[i]=rand()%1000;
        printf("%d ",shu[i]);
    }printf("\n");

    headSort(n);
    
    for(i=1;i<=n;i++){
        printf("%d ",newShu[i]);
    }printf("\n");

    getchar();
}
View Code

 

 

希尔排序是插入排序的一种变形,是步长不短缩小的插入排序

#include<iostream>
#include<cstdio>
#include<time.h>
using namespace std;
double test[10009] ;

void shellsort(double *s,int left,int right) {//希尔排序是插入排序的一种变形,步长逐渐缩小的插入排序
    for (int step = right/ 2;step>0; step/=2) {
       for (int i = step; i <=right; i++) {
           double temp = s[i];
           int j = i;
           while (j >= step && temp < s[j - step]) {
              s[j] = s[j - step];
              j -= step;
           }
           s[j] = temp;
       }
    }
}
int main(){
    freopen(" data.txt","r",stdin);
    time(NULL);
    int i;

    for(i=1;i<=10000;i++){
        scanf("%lf",&test[i]);
    }
    shellsort(test,1,10000);

    printf("第1个:%lf\n",test[1]);
    printf("第10个:%lf\n",test[10]);
    printf("第100个:%lf\n",test[100]);
    printf("第1000个:%lf\n",test[1000]);
    printf("第10000个:%lf\n",test[10000]);

    return 0;
}
View Code

 /*插入排序与希尔排序的实例比较*/

希尔排序有时被叫做缩减增量排序(diminishing increment sort),使用一个序列h1,h2,h3……这样一个增量序列。只要h1=1时,任何增量序列都是可以的。但有些可能更好。对于希尔排序为什么会比直接插入排序快的原因,我们可以来看一个比较极端的例子:

假如对于一个数组{87654321}以从小到大的顺序来排。直接插入排序显然是很悲剧的了。

它的每次排序结果是这样的:

7, 8, 6, 5, 4, 3, 2, 1

 

6, 7, 8, 5, 4, 3, 2, 1

 

5, 6, 7, 8, 4, 3, 2, 1

 

4, 5, 6, 7, 8, 3, 2, 1

 

3, 4, 5, 6, 7, 8, 2, 1

 

2, 3, 4, 5, 6, 7, 8, 1

 

1, 2, 3, 4, 5, 6, 7, 8

 

然后我们来看看Shell排序会怎样处理,一开始步长为4

数组分为8, 7, 6, 5和4, 3, 2, 1

首先是8和4进行比较,交换位置。

变成了4, 7, 6, 5和8, 3, 2, 1

同理7和3,6和2,5和1也是样的,所以当步长为4时的结果是:

4, 3, 2, 1, 8, 7, 6, 5

可以看到,大的数都在后边了。

接下来的步长为2

这一步过程就多了很多:

一开始是4和2进行比较,交换,得到:

2, 3, 4, 1, 8, 7, 6, 5

3和1比较,交换,得到:

2, 1, 4, 3, 8, 7, 6, 5

接下来是4和8,3和7,这两个比较没有元素交换。接下来8和6,7和5就需要交换了。所以步长为2时的结果就是:

2, 1, 4, 3, 6, 5, 8, 7

可以明显地感觉到,数组变得“基本有序”了。

接下来的步长1,变成了直接插入排序。手动模拟一下就可以发现,元素的交换次数只有四次!这是相当可观的。也由此我们可以得到一个基本的事实:对于基本有序的数组,使用直接插入排序的效率是很高的!
转自:http://www.cnblogs.com/yjiyjige/archive/2013/08/13/3256138.html
View Code

 

归并排序,思考将两个有序数列变成一个有序数列的过程,归并就是由小到大重复这个过程,最终使数组有序

#include<iostream>
#include<cstdio>
#include<time.h>
using namespace std;
double test[10009],temp[10009];

void unsort(double *s,int left,int right){

    int mid=(left+right)>>1;
    if(mid-left>=1)unsort(s,left,mid);
    if(right-mid>=2)unsort(s,mid+1,right);
    
    int i,p1=left,p2=mid+1;
    for(i=left;i<=right;i++){//两个有序数组变成一个有序数组
        if(p1>mid){
            temp[i]=s[p2];
            p2++;
            continue;
        }
        if(p2>right){
            temp[i]=s[p1];
            p1++;
            continue;
        }
        if(s[p1]<=s[p2]){
            temp[i]=s[p1];
            p1++;
        }else{
            temp[i]=s[p2];
            p2++;
        }
    }
    for(i=left;i<=right;i++){ //转移
        s[i]=temp[i];
    }
}

int main(){
    freopen(" data.txt","r",stdin);
    time(NULL);
    int i;

    for(i=1;i<=10000;i++){
        scanf("%lf",&test[i]);
    }
    unsort(test,1,10000);

    printf("第1个:%lf\n",test[1]);
    printf("第10个:%lf\n",test[10]);
    printf("第100个:%lf\n",test[100]);
    printf("第1000个:%lf\n",test[1000]);
    printf("第10000个:%lf\n",test[10000]);

    return 0;
}
View Code