算法导论上机题一

1. Implement exercise 2.3-7.

请给出一个运行时间为θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。

问题分析:

       可以先将此问题分为两个部分:集合S排序和查找,查找可以用时间复杂度为θ(lgn) 的折半查找,因此,若要满足题目要求,排序算法的时间复杂度不可以超过θ(nlgn)。

       时间复杂度为θ(nlgn)的算法有快速排序、堆排序和归并排序,但快速排序在最坏情况下时间复杂度为θ(n*n),堆排序适用于大量数据的情况,在数据量较小的情况下堆排序并不提倡使用,因此采用归并排序算法。尽管它需要额外的空间,但好在题目没有要求。

       具体实现方法如下,首先将S排成有序集合,然后对S中的每一个元素si,判断x-si是否在集合S中(折半查找),如此即可满足题意。

源码:

#include<iostream>
using namespace std;

void M(int* A,int l,int q,int r){
	int n1=q-l+1;
	int n2=r-q;
	int L[n1+1],R[n2+1];
	int i,j;
	for(i=0;i<n1;i++){
		L[i]=A[l+i];
	}
	for(j=0;j<n2;j++){
		R[j]=A[q+j+1];
	}
	L[n1]=1000;
	R[n2]=1000;
	i=0;
	j=0;
	for(int k=l;k<=r;k++){
		if(L[i]<=R[j]){
			A[k]=L[i];
			i++;
		}
		else{
			A[k]=R[j];
			j++;
		}
	}
}
	
void MSORT(int* A,int l,int r){
	if(l<r){
		int q=(l+r)/2;
		MSORT(A,l,q);
		MSORT(A,q+1,r);
		M(A,l,q,r);
	}
}

bool binarysearch(int* A,int y,int min,int max){
	int mid;
	while(min<=max){
		mid=(min+max)/2;
		if(A[mid]==y){
			return true;
		}
		if(A[mid]>y){
			max=mid-1;
		}
		else{
			min=mid+1;
		}
	}
	return false;
}

bool checksum(int* A,int x){
	int n=16;
	for(int i=0;i<n;i++){
		if(A[i]>=0&&binarysearch(A,x-A[i],0,15)){
			return true;
		}
	}
	return false;
}

int main(){
	int A[16],x;
	cout<<"Before sorting:"<<endl;
	for(int i=0;i<16;i++){
		A[i]=rand()%100;
		cout<<A[i]<<" ";
	}
	cout<<endl;
	cout<<"Input x:";
	cin>>x;
	MSORT(A,0,15);
	cout<<"After sorting:"<<endl;
	for(int i=0;i<16;i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;
	if(checksum(A,x)){
		cout<<"Find it!"<<endl;
	}
	else{
		cout<<"Do not exist!"<<endl;
	}
	system("pause");
	return 0;
}

2. Implement priority queue.

问题分析:

       以最大优先级队列为例:构建优先级队列即为在一串乱序数字中,第k次将第k大的数字找出来,因此不进行排序,直接构造最大堆,然后将堆的根依次extract,即为最大优先级队列。

       因为不对构建的最大堆进行排序,仅仅是extract根节点,所以建堆阶段时间复杂度要小于堆排序,而之后的heap_extract_max操作运行时间为ο(lgn),对n个元素,又堆中节点是逐渐减少,故时间复杂度小于ο(nlgn)。

源码:

#include<iostream>
using namespace std;

int length=16;
int heap_size;

int parent(int i){
	return (i-1)/2;
}

int left(int i){
	return 2*i+1;
}

int right(int i){
	return 2*i+2;
}

void exchange(int& a,int& b){
	int t;
	t=a;
	a=b;
	b=t;
}

void max_heapify(int* A,int i){
	int l=left(i);
	int r=right(i);
	int largest;
	if(l<heap_size&&A[l]>A[i])
		largest=l;
	else
		largest=i;
	if(r<heap_size&&A[r]>A[largest]){
		largest=r;
	}
	if(largest!=i){
		exchange(A[i],A[largest]);
		max_heapify(A,largest);
	}
}

void build_max_heap(int* A){
	heap_size=length; 
	for(int i=length/2-1;i>=0;i--){
		max_heapify(A,i);
	}
}

/*int heap_maximum(int* A){
	return A[0];
}*/

int heap_extract_max(int* A){
	if(heap_size<1){
		exit(-1);
	}
	int max=A[0];
	A[0]=A[heap_size-1];
	heap_size--;
	max_heapify(A,0);
	return max;
}

/*void heap_increase_key(int* A,int i, int key){
	if(key<A[i]){
		exit(-2);
	}
	A[i]=key;
	while(i>0&&A[parent(i)]<A[i]){
		exchange(A[i],A[parent(i)]);
		i=parent(i);
	}
}*/

/*void max_heap_insert(int* A,int key){
	A[heap_size]=-1000;
	heap_increase_key(A,heap_size,key);
}*/

int main(){
	int A[20];
	int n;
	cout<<"Before Heapify;"<<endl;
	for(int i=0;i<length;i++){
		A[i]=rand()%100;
		cout<<A[i]<<" ";
	}
	cout<<endl;
	build_max_heap(A);
	cout<<"After Heapify:"<<endl; 
	for(int i=0;i<length;i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;
	/*cout<<"Input the number you want to insert:"<<endl;
	cin>>n;
	max_heap_insert(A,n);
	for(int i=0;i<length+1;i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;*/
	cout<<"The priority queue is:"<<endl;
	for(int i=0;i<length;i++){
		cout<<heap_extract_max(A)<<" ";
	}
	cout<<endl;
	system("pause");
	return 0;
}

3. Implement Quicksort and answer the following questions.

(1) How many comparisons will Quicksort do on a list of n elements that all have the same value?

(2) What are the maximum and minimum number of comparisons will Quicksort do on a list of n elements, give an instance for maximum and minimum case respectively.

问题分析:

       此题目仅要求一个简单的快速排序实现,附带两个问题,在对快速排序理解的基础上并不难完成。

       作为对冒泡排序的一种改进,快速排序成功的将平均时间复杂度降为Ο(nlog n),每一趟排序它用分治法的策略把一个序列分为两个子序列,我们学习的快速排序是一个原地分割的版本,也就降低了空间复杂度。

源码:

#include<iostream>
using namespace std;

int n=0;
void exchange(int& a,int& b){
	int t;
	t=a;
	a=b;
	b=t;
}

int PARTITION(int* A,int l,int r){
	int x=A[r];
	int i=l-1;
	for(int j=l;j<=r-1;j++){
		if(A[j]<=x){
			n++;
			i++;
			exchange(A[i],A[j]);
		}
	}
	exchange(A[i+1],A[r]);
	return i+1;
}

void QUICKSORT(int* A,int l,int r){
	if(l<r){
		int q=PARTITION(A,l,r);
		QUICKSORT(A,l,q-1);
		QUICKSORT(A,q+1,r);
	}
}

int main(){
//	int A[7]={1,3,2,5,7,6,4};//best
	int A[7]={1,2,3,4,5,6,7};//worst
	cout<<"Before Sorting;"<<endl;
	for(int i=0;i<7;i++){
//		A[i]=11;
		cout<<A[i]<<" ";
	}
	cout<<endl;
	QUICKSORT(A,0,6);
	cout<<"After Sorting:"<<endl; 
	for(int i=0;i<7;i++){
		cout<<A[i]<<" ";
	}
	cout<<endl;
	cout<<"Count:"<<n<<endl;
	system("pause");
	return 0;
}

4. Give a divide and conquer algorithm for the following problem: you are given two sorted lists of size m and n, and are allowed unit time access to the ith element of each list. Give an O(lg m + lgn) time algorithm for computing the kth largest element in the union of the two lists. (For simplicity, you can assume that the elements of the two lists are distinct).

问题分析:

       假设两个有序的序列为从大到小排列。问题要求时间复杂度为O(lg m + lgn),并且采用分治的方式,因此可将两个序列(命名为A和B)分别从中间分开,子序列长度为m/2(设x)、n/2(设y)。这样A[x]和B[y]就是A和B中间的元素。

       设A[x]>B[y](同理可得A[x]<B[y]的情况),这样合并后A[x]之前的元素必然在B[y]之前,即B[y]之前至少有x+y+1个元素。若k<x+y+1,则可排除B序列的后半部分元素,也就是将B排除了一半。

       A[x]之后有m-x-1个元素,B[y]之后有n-y-1个元素,由于A[x]>B[y],则A[x]之后有至少(m-x-1)+(n-y-1)+1=(m+n)-(x+y+1)个元素。这样若k>x+y+1,则可将A[x]之前的元素排除,也就是将A排除了一半。

       每一次比较必将A或者B排除一半,根据分治方法,不难判断出算法的时间复杂度为O(lg m + lgn)。

源码:

#include<iostream>
using namespace std;

int A[8]={0,3,6,7,11,14,16,23};
int B[13]={1,4,5,10,15,19,24,26,27,28,31,35,40};

int searchk(int al,int ar,int bl,int br,int k){
	int am=(al+ar)/2;
	int bm=(bl+br)/2;
	if(al>ar) 
		return B[bl+k-1];    
    if(bl>br) 
		return A[al+k-1];
	if (A[am] <= B[bm]) {    
        if (k <= (am - al) + (bm - bl) + 1) {    
            return searchk(al, ar, bl, bm-1, k);    
        } else {    
            return searchk(am+1, ar, bl, br, k-(am-al)-1);    
        }    
    } else {    
        if (k <= (am - al) + (bm - bl) + 1) {    
            return searchk(al, am-1, bl, br, k);    
        } else {    
            return searchk(al, ar, bm+1, br, k-(bm-bl)-1);    
        }    
    }    
    return -1;    
}

int main(){
	int k;
	cout<<"INPUT K:";
	cin>>k;
	int n=searchk(0,7,0,12,22-k);
	cout<<"The "<<k<<"th largest element is "<<n<<endl;
	return 0;
}

转载于:https://my.oschina.net/zhangjinhui/blog/60338

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值