今天在一个OJ上做了一个叫“Advanced Heap Sort”的题,题的解决算法没什么难的,但是对时间复杂度有要求,用正常的算法实现,都会超时,所以我就把这个题拿过来分享一下。
问题:
题目内容: 有两个序列S1和S2,各有N个元素。当我们在S1,S2各取一个数字时,总共会有N*N这么多可能的”和”(sum)。请找出这N*N这么多和里最小的N个值,并将它们加总后输出。输入格式: 只有一笔测资。测试资料第一行为一个正整数N。第二行有N个数字,以空白隔开,代表序列S1。第二行有N个数字,以空白隔开,代表序列S2。数字范围: 0 < N < 10000输出格式: 输出一行,N个最小的可能的和的加总。输入样例: 5 1 3 5 7 9 2 4 6 8 10输出样例: 27时间限制:100ms内存限制:128000kb
算法分析:
这个题目既然是要求用堆排序,所以首先想到的时,建立一个N²的数组,通过两层循环,将所有的结果和填写到N²的大数组中,然后通过最小堆排序,将前N个进行和运算就可以了。但是这个题N的范围到10000,所以堆的大小是100000000,那么这个算法的时间复杂度就是O(n²)+ O(nlogN)+ O(nlogN) + O(n),可见时间复杂度和空间复杂度都很高,不能满足题目要求。
所以,这里需要先将两个数列时行从小到大的排序,然后,建立一个N大的固定的堆,先填入N的数,并经进行最大堆排序,排序完成后,结果也就出来了。这里还有一个关键就是往堆里插入的值,原来是要插入N²个,因为现在将两个数列进行了从大到小的排序,所以满足插入的数列为:从第一个数组元素到(N/1) + (N/2) + (N/3) + .... + (N/N) 【因为A[1] + B[i], 0 <= i <= (N-1)/2都是答案的话(也就是前N小的),那么,A[0] + B[i]也是答案。因为A[0] + B[i] <= A[1] + B[i]】 ,通过这种方式进行时间复杂度的提升,再就是在进行两个数列的排序时,要使用归并法时行排序。
这样的话,空间复杂度是N,时间复杂度是O(N log N) + O(log N) + O(NlogN * logN) + O(N log N),提升了不少,通过测试,完胜。
算法代码:
#include <stdio.h>int arrRst[10000]; //堆 int arrMer[10000]; //归并排序的临时数组 int n = 0; // 堆游标 //归并函数 void merge(int *arr, int start, int mid, int end){int i, j, k;i = k = start;j = mid + 1;while((i <= mid) && (j <= end)){if(arr[i] <= arr[j]){arrMer[k++] = arr[i++];}else{arrMer[k++] = arr[j++];}}while(i <= mid){arrMer[k++] = arr[i++];}while(j <= end){arrMer[k++] = arr[j++];}for(i = start; i <= end; i++){arr[i] = arrMer[i];} }//归并函数 void mergeSort(int *arr, int start, int end){int mid;if(start < end){mid = (start + end) / 2;mergeSort(arr, start, mid);mergeSort(arr, mid + 1, end);merge(arr, start, mid, end);} }void swap(x, y){int t;t = arrRst[x];arrRst[x] = arrRst[y];arrRst[y] = t; }void shiftDown(int x){int t, flag = 0;while(x * 2 <= n && flag == 0){//左孩子 if(arrRst[x * 2] > arrRst[x]){t = x * 2;}else{t = x;}//右孩子if(x * 2 + 1 <= n){if(arrRst[x * 2 + 1] > arrRst[t]){t = x * 2 + 1;}} if(t == x){flag = 1;}else{swap(x, t);x = t;}} }void creatHeap(){int i;for(i = n / 2; i >= 1; i--){shiftDown(i);} }int main(){int arr[10000], arr2[10000];int N, sum = 0;int i, j;//输入 scanf("%d", &N);for(i = 1; i <= N; i++){scanf("%d", &arr[i]);}for(i = 1; i <= N; i++){scanf("%d", &arr2[i]);}//排序mergeSort(arr, 1, N);mergeSort(arr2, 1, N); for(i = 1; i <= N; i++){for(j = 1; j <= N / i; j++){//要插入的数据 if(n <= N - 1){ //只存放前N个 arrRst[++ n] = arr[i] + arr2[j];if(n == N){creatHeap();//对前N个建立最大堆 } }else{if(arr[i] + arr2[j] < arrRst[1]){arrRst[1] = arr[i] + arr2[j];shiftDown(1);}} }}for(i = 1; i <= n; i ++){sum += arrRst[i];}printf("%d", sum);return 0; }
博客名称:王乐平博客
博客地址:http://blog.lepingde.com
CSDN博客地址:http://blog.csdn.net/lecepin