洛谷P1923 求第K小的数 研讨关于输入输出效率的问题(scanf and cin ,printf and cout)
最简单的思想就是将这n个数从小到大排序,然后直接输出下标为K的数,不用想肯定会超时,三个测试点过了,另外两个超时。
那么我想的就是,既然全排序会超时,有没有什么方法可以不用全排序也可以拿出第K小的数呢,我想到了堆这种数据结构,我们不用使用堆排序,而是将数组建堆,不断从堆中拿出当时最小的数,当取出第K个数时,那就是我们想要的第K小的数
代码如下(因为还不会STL,于是就手写建堆以及出堆)
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; void swap(int* a, int* b) { int c = *a; *a = *b; *b = c; } void ShiftDown(int* Num, int Now, int Last)//一次向下调整 { int Max = Now; if (Now * 2 <= Last) { Max = Now * 2; if (Now * 2 + 1 <= Last && Num[Now * 2 + 1] < Num[Max]) Max = Now * 2 + 1; if (Num[Max] < Num[Now]) { swap(Num + Now, Num + Max); ShiftDown(Num, Max, Last); } } } int Pop(int* Num, int* Last)//出堆 { int Temp = Num[1]; int Now = Num[*Last]; Num[1] = Now; ShiftDown(Num, 1, *Last - 1); (*Last)--; return Temp; } int main() { int n, k; cin >> n >> k; int L = n; int* Num = new int[n + 1]; for (int i = 1; i <= n; i++) { cin >> Num[i]; } for (int i = n / 2; i >= 1; i--) ShiftDown(Num, i, n);//调整建堆 int Temp; for (int i = 1; i <= k + 1; i++) Temp = Pop(Num, &n); cout << Temp; return 0; }
但是不负众望,他还是TLE了,草泥马
然后,想起来输入输出也会影响,我就把C++的输入输出流cin和cout改为了C的标准输入输出scanf以及printf,然后就发生了神奇的现象,就改了输入输出,但是原先的代码AC了!!!呜呼呜呼
所以,在一些数据输入输出量很多的题里,如果TLE了可以尝试将cin和cout改为scanf以及printf!!!!而且听说getchar会更快
最后来说一下这道题的另一种做法,或者说题干推荐的做法?,分治或者说二分,利用快排的思想去找而不是进行整体的快速排序
#include<iostream> #include<cstdio> using namespace std; void Swap(int* a, int* b) { int c = *a; *a = *b; *b = c; } int FindK(int* Num, int Begin, int Last, int k) { int Temp = Num[Begin]; int L = Begin; int R = Last; while (L < R) { for (; Num[R] >= Temp && L < R; R--); for (; Num[L] <= Temp && L < R; L++); Swap(Num + R, Num + L); } Swap(Num + L, Num + Begin);//一次快排,所有比Temp小的都在Temp的左边,比Temp大的都在Temp右边 //而L(R)正好是比Temp小的元素个数,若K>L,则从L右边部分继续去找第K小,K<L,从右边找,K==L,说明找到了,输出 //所以只需要比较元素个数与K的大小就可以确定在那部分去寻找第K小的元素 if (k > L) return FindK(Num, L + 1, Last, k);//去右边查找 else if (k < L) return FindK(Num, Begin, L - 1, k);//去左边查找 else if (k == L) return Num[k];//若正好,直接输出 } int main() { int n, k; scanf("%d %d", &n, &k); int* Num = new int[n]; for (int i = 0; i < n; i++) scanf("%d", Num + i); printf("%d", FindK(Num, 0, n - 1, k)); return 0; }
这样的话不需要去快排整个序列而是通过不断的二分查找去找部分的快排后的数据,来找第K小的数,比直接sort快多了,时间复杂度O(n),而快排时间复杂度O(nlogn)