实践报告
1、实践题目名称:
找第k小的数
2、问题描述:
在n(1<=n<=1000)个无序的整数中找出第k小的数,要求平均时间为O(N)
3、算法描述:
思路:本题要求使用O(n)的时间,所以不能直接采用排序然后输出的方法来解题。因此采用分治方法,先任意找数组中的一个元素a(代码中的a为数组的第一个元素,亦可才用随机数选取数组中的任一个元素),采用快速排序将数组进行一次划分,即将小于a的元素放在其左侧,大于a的元素放在其右侧。然后判断元素a是否满足题目为第k小的数,满足则直接输出,否则判断下一次在哪一区间进行划分。
①int find(int a[], int left, int right, int k)的功能:
int pos:定位当前位置,比较与k是否相等
这个函数调用partition函数来得到以某个元素为基准划分好的此时这个元素的下标,因为我们要求找到第k小的数,由于下标从0开始,因此满足m+1==k,即可找到第k小个数
int find(int a[], int left, int right, int k) {//在数组a的第left到right中寻找第k小的数 int pos = partition(a, left, right); if (k - 1 == pos) cout << a[k - 1]; else if (k - 1 < pos)//判断下一次划分在哪一区间进行 find(a, left, pos - 1, k); else find(a, pos + 1, right, k); return 0; }
②int partition(int a[], int left, int right)的功能:
用函数partition来根据给定下标为p的元素大小对a[p]到a[r]进行划分,从a[p]往右找起直到找到一个数组元素比a[p]大,然后从a[r]往左找起直到找到一个数组元素比a[p]小,然后判断这两个元素的下标i和j,i是否小于j,若是,则将这两个元素交换,并重复以上的步骤继续找,若不是,则已经将整个数组划分完成,最后将a[p](基数)和此时的a[j]交换,跳出循环划分完成。并返回此时的j下标。
int partition(int a[], int left, int right) { int x = a[left]; while(left < right) {//采用快排策略 while (left < right && a[right] >= x){ right--;
} a[left] = a[right];
while (left < right && a[left] <= x){ left++;
} a[right] = a[left]; } a[left] = x; return left; }
4、算法时间及空间复杂度分析
①时间复杂度:每次将待排序数组分为两个部分,在理想状况下,每一次都将待排序数组划分成等长两个部分,则需要logn次划分。而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分只能减少一个元素,将退化为冒泡排序,所以快速排序时间复杂度下界为O(nlogn),最坏情况为O(n^2)。平均时间复杂度为O(nlogn)。
②空间复杂度:使用原本的数组进行排序,不许要额外的开销,空间复杂度为O(n)
5、心得体会:
老师在讲解快速排序的过程中,我很快就理解了算法的意思,但是遇到了此算法基础上更难一点的题,一上手操作就遇到了困难:第k个位置到底怎么确定,find函数怎么和partition函数联系在一起,该返回的是哪一个值。好在仔细思考后,这些问题都一一解决。在此次编程过程中我深刻的理解到,要提高自己的思维能力,学会把算法和实际问题结合在一起,注意语句之间的逻辑和分治法的正确使用。
浙公网安备 33010602011771号