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); //到左半部分查找
}