经典排序面试题

经典排序面试题

案例一

已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的化,每个元素移动的距离不超过k,并且k相对于数组长度来说很小。请问选择什么方法对其排序比较好。

分析:

时间复杂度为O(N)的排序算法:计数排序、基数排序

不基于比较的排序算法的限制:不适用所有情况。

时间复杂读为O(N^2)的排序算法:

插入排序:插入排序的过程与原始顺序有关,每个元素移动距离不超过k。
对于本题来说,插入排序时间复杂度不会超过O(N*K)。

时间复杂读为O(N*logN)的排序算法

归并排序:与数组原始顺序无关;对于这道题快速排序、归并排序的时间复杂度为O(N*logN)。

答案:改进后的堆排序;思路是从0开始,调整一个(0,0+k)k大小的小顶堆,将顶放在位置0上,接着对位置1调整(1,1+k),直到n。其时间复杂度为,O(N*logK)。

#include<iostream>
#include<vector>
using namespace std;


void swap(int *a,int*b){
	int tmp=*a;
	*a=*b;
	*b=tmp;
}
vector<int> sortElement(vector<int> A, int n, int k) {
	for(int i=0;i<n;i++){
		//小顶堆,形成小顶堆,根就是最小,所以直接向后调整就行
		for(int j=k-1;j>=0;j--){
			int pre=(j-1)/2;
			if(k%2==0&&j%2==0){
				pre=(j-2)/2;
			}
			if(pre>=0&&j+i<n&&A[pre+i]>A[j+i]){
				swap(&A[pre+i],&A[j+i]);
			}
		}
	}
	return A;
}

int main(){
	int ints[]={2,1,4,3,6,5,8,7,10,9,};
	vector<int> A(ints,ints+sizeof(ints)/sizeof(int));
	for(int i=0;i<A.size();i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;
	//vector<int> B=sortElement(A,10,5);
	vector<int> B=sortElement(A,10,2);
	for(int i=0;i<B.size();i++){
		cout<<B[i]<<" ";
	}
	cout<<endl;

	return 0;
}

案例二

判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。

如果没有空间复杂度限制,用哈希表实现。 时间和空间复杂度都是O(N)。

如限制空间复杂度,则应先排序,后判断,排好序后的重复元素就对相邻,方便判断。

考察经典排序算法,空间复杂度限制。O(1)的是堆排序,当不能使用递归的实现方式,因为递归法下堆排序的空间复杂度为O(logN),函数栈深度为logN,所以应该改为非递归版本的堆排序。

//堆来做
#include<iostream>
#include<vector>
using namespace std;

void swap(int*a,int *b){
	int tmp=*a;
	*a=*b;
	*b=tmp;
}
void heapSort(vector<int>&a,int n){
	for(int i=0;i<n;i++){
		//对n-i调整大顶堆
		for(int j=n-i-1;j>0;j--){
			int pre=(j-1)/2;
			if(j%2==0){
				pre=(j-2)/2;
			}
			if(a[j]>a[pre]){
				swap(&a[j],&a[pre]);
			}
		}
		swap(&a[0],&a[n-1-i]);
	}
}
bool checkDuplicate(vector<int> a, int n) {
	// write code here
	heapSort(a,n);
	for(int i=1;i<n;i++){
		if(a[i]==a[i-1]){
			return true;
		}
	}
	return false;
}
int main(){
	int v[]={4,6,1,3,5,6,2};
	//int v[]={1,1};
	vector<int> a(v,v+sizeof(v)/sizeof(int));
	cout<<checkDuplicate(a,a.size())<<endl;
	return 0;
}
//练习一下quicksort
#include<iostream>
#include<vector>
using namespace std;
void process(vector<int>&A,int left,int right){
	if(A.size()==0||left>=right){
		return;
	}
	//random
	int mid=(left+right)/2;
	vector<int>l,r;
	for(int i=left;i<=right;i++){
		if(i==mid){
			continue;
		}
		if(A[i]<A[mid]){
			l.push_back(A[i]);
		}else{
			r.push_back(A[i]);
		}
	}
	A[left+l.size()]=A[mid];
	for(int i=0;i<l.size();i++){
		A[left+i]=l[i];
	}
	for(int i=0;i<r.size();i++){
		A[left+l.size()+1+i]=r[i];
	}
	int pivot=left+l.size();
	process(A,left,pivot-1);
	process(A,pivot+1,right);
}
void quickSort(vector<int>&a,int n){
	process(a,0,n-1);
}
bool checkDuplicate(vector<int> a, int n) {
	// write code here
	quickSort(a,n);
	for(int i=1;i<n;i++){
		if(a[i]==a[i-1]){
			return true;
		}
	}
	return false;
}
int main(){
	int v[]={4,6,1,3,5,7,2};
	//int v[]={1,1};
	vector<int> a(v,v+sizeof(v)/sizeof(int));
	cout<<checkDuplicate(a,a.size())<<endl;
	return 0;
}

案例三

把两个有序数组合并为一个数组,第一个数组空间正好可以容纳两个数组的元素。

这个题目,直接用merge就可以。

#include<iostream>
using namespace std;
int* mergeAB(int* A, int* B, int n, int m) {
	//可以将下边三个循环放一块
	int i=n-1,j=m-1;
	int k=m+n;
	while(i>=0&&j>=0){
		if(A[i]>B[j]){
			A[--k]=A[i--];
		}else{
			A[--k]=B[j--];
		}
	}
	while(i>=0){
		A[--k]=A[i--];
	}
	while(j>=0){
		A[--k]=B[j--];
	}
	return A;
}
int main(){
	int A[]={1,2,4,6,7,0,0,0,0,0};
	int B[]={2,4,7,9};
	mergeAB(A,B,5,4);
	for(int i=0;i<9;i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;
	return 0;
}

案例四

荷兰国旗问题,只包含0,1,2的整数数组进行排序,要求使用交换、原地排序,而不是利用计数进行排序。

本题主要过程与快排划分过程类似,时间复杂度O(N),额外空间复杂度O(1)。

e.g.:1 1 0 0 2 1 1 0。

{0,0,0},1,1,1,1,{2}。

#include<iostream>
#include<vector>
using namespace std;
void swap(int *a,int *b){
	int tmp=*a;
	*a=*b;
	*b=tmp;
}
vector<int> sortThreeColor(vector<int> A, int n) {
	// write code here
	int l=0,r=0;
	for(int i=0;i+r<n;i++){
		if(A[i]==0){
			//左侧不需要移动,整个子区间,因为交换后,状体满足,直接走下一个即可
			swap(&A[i],&A[l++]);
		}else if(A[i]==2){
			//右侧交换后,一个未确定的元素交换到了本地,还需要判断一次
			swap(&A[i--],&A[n-1-r++]);
		}
	}
	return A;
}
int main(){
	int v[]={0,1,1,0,2,2};
	//int v[]={1,2,0,2};
	vector<int>A(v,v+sizeof(v)/sizeof(int));
	vector<int>B=sortThreeColor(A,A.size());
	for(int i=0;i<B.size();i++){
		cout<<B[i]<<" ";
	}
	cout<<endl;
	return 0;
}
#include<iostream>
#include<vector>
using namespace std;
void swap(int *a,int *b){
	int tmp=*a;
	*a=*b;
	*b=tmp;
}
vector<int> sortThreeColor(vector<int> A, int n) {
	// write code here
	int l=-1,r=n,i=0;
	while(i<r){
		if(A[i]==0){
			swap(&A[i++],&A[++l]);
		}else if(A[i]==2){
			swap(&A[i],&A[--r]);
		}else{
			i++;
		}
	}
	return A;
}
int main(){
	int v[]={0,1,1,0,2,2};
	//int v[]={1,2,0,2};
	vector<int>A(v,v+sizeof(v)/sizeof(int));
	vector<int>B=sortThreeColor(A,A.size());
	for(int i=0;i<B.size();i++){
		cout<<B[i]<<" ";
	}
	cout<<endl;
	return 0;
}

案例五

在行列都排好序的矩阵中找给定的数。

0 1 2 5
2 3 4 7
4 4 4 8
5 7 7 9

如果k为7,返回true;如果k为6,返回false。、

从右上角开始找,这样左边的是小于部分,右边的是大于部分,形成了一个二分结构,时间复杂度O(M+N)。

#include<iostream>
#include<vector>
using namespace std;
bool findX(vector<vector<int> > mat, int n, int m, int x) {
	int p=0,q=m-1;
	while(p<n&&q>=0){
		if(mat[p][q]>x){
			q--;
		}else if(mat[p][q]<x){
			p++;
		}else{
			return true;
		}
	}
	return  false;
}
int main(){
	int n=3,m=3,x=3;
	int v[][3]={
		{1,2,3},
		{4,5,6},
		{7,8,9},
	};
	vector<vector<int> >mat;
	for(int i=0;i<3;i++){
		vector<int> tmp(v[i],v[i]+sizeof(v[i])/sizeof(int));
		mat.push_back(tmp);
	}
	cout<<findX(mat,n,m,x)<<endl;
	return 0;
}

案例六

在给定数组中,找到需要排序的最短子数组长度。

e.g.[1,5,4,3,2,6,7]
返回4,因为只有[5,4,3,2]需要排序。

最优解时间复杂度O(N),额外空间复杂度O(1)。从左记录遍历过的最大值,记录后续小于最大值的最右位置,比如元素2所在的位置;从右记录遍历过的最小值,记录前边大于最小值的最左位置,比如元素5所在的位置。这样,【5,…,2]之间的范围就是所求。

#include<iostream>
#include<vector>
using namespace std;

int shortestSubsequence(vector<int> A, int n) {
	int p=0,q=n-1;
	int max=A[p],min=A[q];
	for(int i=1;i<n;i++){
		if(A[i]<max){
			p=i;
		}else{
			max=A[i];
		}
	}
	for(int i=n-2;i>=0;i--){
		if(A[i]>min){
			q=i;
		}else{
			min=A[i];
		}
	}
	return p<=q?0:p-q+1;
}
//[1,4,6,5,9,10],6
int main(){
	int v[]={1,4,6,1,9,10};
	vector<int> A(v,v+sizeof(v)/sizeof(int));
	cout<<shortestSubsequence(A,A.size())<<endl;
	return 0;
}

案例七

给定一个整型数组arr,返回如果排序自后,相邻两束的最大差值。
例如:数组[7 9 3 4 2 1 8]排序之后为[1 2 3 4 7 8 9],最大差值来自于4和7,所以返回3。
最优解时间复杂度O(N),额外空间复杂度O(N)。思想来自桶排序。

找到数组的最大值、最小值,然后等量分成n个区间,每个区间对应一个桶,这样必然在中间存在空桶;桶间差距必然大雨桶内元素差距,所以不用考虑同一个桶的相邻数,只用考虑桶间的相邻数,计算空桶两边非空桶的最小值和最大值,二者差值即是最大值。

#include<iostream>
#include<vector>
using namespace std;
//获得桶的编号
int get(int max,int min,int n,int x){
	if(max<=min){
		return 0;
	}
	return (x-min)*n/(max-min);
}
int maxGap(vector<int> A, int n) {
	//求最大最小值
	int min=A[0],max=A[0];
	for(int i=1;i<n;i++){
		if(A[i]<min){
			min=A[i];
		}
		if(A[i]>max){
			max=A[i];
		}
	}
	vector<int>tmp;
	vector<vector<int> >B;
	B.assign(n+1,tmp);
	//将数据放到桶中,桶中只记录最大最小两个值
	for(int i=0;i<n;i++){
		int p=get(max,min,n,A[i]);
		if(B[p].size()<2){
			B[p].push_back(A[i]);
		}else{
			int min=0,max=1;
			if(B[p][0]>B[p][1]){
				min=1;max=0;
			}
			//更新桶中最大最小值
			if(A[i]<B[p][min]){
				B[p][min]=A[i];
			}else if(A[i]>B[p][max]){
				B[p][max]=A[i];
			}
		}
	}
	//求桶间隔最大值
	int flag=0,pre=0,MAX=0;
	for(int i=0;i<=n;i++){
		if(B[i].size()==0){
			flag=1;
		}else{
			//求前边桶中最大值
			int preMax=B[pre][0];
			if(B[pre].size()>1&&B[pre][1]>B[pre][0]){
				preMax=B[pre][1];
			}
			//求当前桶中最小值
			int imin=B[i][0];
			if(B[i].size()>1&&B[i][1]<B[i][0]){
				imin=B[i][1];
			}
			if(imin-preMax>MAX){
				MAX=imin-preMax;
			}
			pre=i;
		}
	}
	return MAX;
}
int main(){
	//int v[]={1,2,5,8,9,};
	int v[]={9312,424,8256,2497,4291,8654};//3965
	//int v[]={7778,9763,347,8793,4297}; 
	vector<int>A(v,v+sizeof(v)/sizeof(int));
	cout<<maxGap(A,A.size())<<endl;
	return 0;
}
已标记关键词 清除标记
相关推荐
题目: 已知文件中存有全国英语六级历年来的成绩(千万级别,考生分数都是正整数,最高710分),每一行都是一个人的姓名、考号和成绩,请你对考生的成绩从高到低进行排序,输出到另一个文件中。 格式 如下: 李四,201008823,678; 张三,201007432,356; 王五,201322233,464; 排序后: 李四,201008823,678; 王五,201322233,464; 王五,201322233,464; 要求:使空间复杂度和时间复杂度尽可能低,希望可以达到时间复杂度为O(n)。按目前数据量预计计算机内存足够。可以用伪代码或说明思路 刚注册,没有分可悬赏,但应该是个不错的题。。 以下是我的思路,但是不能完全解答出来,请高手提提建议: 1.首先这个排序的依据是分数,而英语四级考试分数有范围0-710分。题目希望时间复杂度尽可能低。排除依据比较的排序算法如快速排序、选择排序,应该选择线性排序算法。 2.再因为是千万级的数据,可以考虑使用桶排序,建立一个有711大小的桶(数组),遍历整个文档,根据分数大小在相应的桶号上计数。 3.根据上面两步,基本是可以实现单纯的成绩排序。而且时间复杂度为O(n),空间复杂度也就是多一个桶数组。 4.困难:问题是,基于计数的桶排序的对象如果是纯数字,好说。但是题目是带有姓名、考号和成绩三个属性的对象。怎么利用桶排序呢? 谢谢!
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页