今天在一个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
优快云博客地址:http://blog.youkuaiyun.com/lecepin