关于k小的实现及优化(c)
2016-03-14 20:20
274 查看
在分治中有一个经典案例是求中项及第k小的元素。先不管算法书里面是怎么解决这个问题的,如果让我们自己来想,这个问题应该怎么解决?惯性思维肯定是先排序然后求出第k小的值。
排序算法我们首推肯定是快速排序,用快速排序来求第k小的值的算法如下(c):
更进一步,我们发现其实如果单纯的求k小并不需要把整个数组都排好序。当一趟划分完之后,如果i小于k,那么k小在后半部分,前半部分就不需要递归排序了;同理,如果i大于k,那么k小在前半部分,后半部分就不需要递归排序了。
修改后的算法如下:
很明显,第二种写法比第一种简单了许多。上述两种方法的时间复杂度均为 nlogn。接下来看看算法书上的方法,与上述的思想类似,也是从递归调用的途中舍弃掉一部分无关的值,不过比上述方法要更加复杂一些:
A中总共q个数,分成q/5组,每组5个元素。每组分别取中项,组成一个大小为q/5的数组M,求M的中项mm(若M中元素数大于6则一直递归,否则通过快排求出mm)。将A分i成三份A1,A2,A3。分三种情况讨论:若k小在A1,将A2,A3舍弃,A=A1;若k小在A2,则直接输出k=mm;若k小在A3,将A1,A2舍弃,A=A3。继续递归,一直到求出k小为止。
这种算法的时间复杂度可以达到 n。
排序算法我们首推肯定是快速排序,用快速排序来求第k小的值的算法如下(c):
#include "stdafx.h" void quickSort(int a[],int s,int k){ int i,j,tmp; if(s<k){ i = s;j=k,tmp = a[s]; while(i<j){ while(i<j&&tmp<=a[j]) j--; a[i] = a[j]; while(i<j&&a[i]<=tmp) i++; a[j] = a[i]; } a[i] = tmp; quickSort(a,s,i-1); quickSort(a,i+1,k); } } int _tmain(int argc, _TCHAR* argv[]) { int a[] = {8,7,6,5,4,3,2,1},k; quickSort(a,0,7); for(int i = 0;i<8;i++) printf("%d",a[i]); printf("\n"); scanf("%d",&k); printf("k小:%d\n",a[k-1]); system("pause"); return 0; }
更进一步,我们发现其实如果单纯的求k小并不需要把整个数组都排好序。当一趟划分完之后,如果i小于k,那么k小在后半部分,前半部分就不需要递归排序了;同理,如果i大于k,那么k小在前半部分,后半部分就不需要递归排序了。
修改后的算法如下:
#include "stdafx.h" void quickSort(int a[],int s,int k,int x){ int i,j,tmp; if(s<k){ i = s;j=k,tmp = a[s]; while(i<j){ while(i<j&&tmp<=a[j]) j--; a[i] = a[j]; while(i<j&&a[i]<=tmp) i++; a[j] = a[i]; } a[i] = tmp; if(i>x-1){ quickSort(a,s,i-1,x); }else if(i<x-1){ quickSort(a,i+1,k,x); } } } int _tmain(int argc, _TCHAR* argv[]) { int a[] = {8,7,6,5,4,3,2,1},k; scanf("%d",&k); quickSort(a,0,7,k); printf("k小:%d\n",a[k-1]); system("pause"); return 0; }
很明显,第二种写法比第一种简单了许多。上述两种方法的时间复杂度均为 nlogn。接下来看看算法书上的方法,与上述的思想类似,也是从递归调用的途中舍弃掉一部分无关的值,不过比上述方法要更加复杂一些:
void quickSort2(int a[],int s,int k){ int i,j,tmp; if(s<k){ i = s;j=k,tmp = a[s]; while(i<j){ while(i<j&&tmp<=a[j]) j--; a[i] = a[j]; while(i<j&&a[i]<=tmp) i++; a[j] = a[i]; } a[i] = tmp; quickSort2(a,s,i-1); quickSort2(a,i+1,k); } } //寻找第k小的元素,但会破坏原数组的顺序 int select(int * A, int low, int high, int k) { int result = 0; int p = high-low+1; if (p < 6/*44*/) { quickSort2(A, low, high); return A[k-1]; } int q = p / 5; int * M = new int [q]; for (int i = 0; i < q; i++) { quickSort2(A, i*5, i*5+4); M[i] = A[i*5+2]; } int mm = select(M, 0, q-1, int(ceil(q/2.0))); int * A1 = new int [p]; int * A2 = new int [p]; int * A3 = new int [p]; int count1 = 0, count2 = 0, count3 = 0; for (int i = low; i <= high; i++) { if (A[i] < mm) { A1[count1++] = A[i]; } else if (A[i] == mm) { A2[count2++] = A[i]; } else { A3[count3++] = A[i]; } } if (count1 >= k) { result = select(A1, 0, count1-1, k); } else if (count1+count2 >= k) { result = mm; } else if (count1+count2 < k) { result = select(A3, 0, count3-1, k-count1-count2); } return result; }
A中总共q个数,分成q/5组,每组5个元素。每组分别取中项,组成一个大小为q/5的数组M,求M的中项mm(若M中元素数大于6则一直递归,否则通过快排求出mm)。将A分i成三份A1,A2,A3。分三种情况讨论:若k小在A1,将A2,A3舍弃,A=A1;若k小在A2,则直接输出k=mm;若k小在A3,将A1,A2舍弃,A=A3。继续递归,一直到求出k小为止。
这种算法的时间复杂度可以达到 n。
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- Lua和C语言的交互详解
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数
- 深入C++中API的问题详解
- 基于C语言string函数的详解
- C语言中fchdir()函数和rewinddir()函数的使用详解
- C语言内存对齐实例详解
- C语言编程中统计输入的行数以及单词个数的方法
- C语言自动生成enum值和名字映射代码
- 使用C语言判断英文字符大小写的方法
- c语言实现的带通配符匹配算法
- C语言实现顺序表基本操作汇总
- C语言中计算正弦的相关函数总结
- 使用C语言详解霍夫曼树数据结构
- 探讨C语言的那些小秘密之断言
- C语言实现BMP转换JPG的方法
- 深入探讨C语言中局部变量与全局变量在内存中的存放位置
- C语言查找数组里数字重复次数的方法