算法思想:找出第i个最小元素

找出第i个最小元素

  • 方法一:
    堆排序:时间复杂度O(nlog(i))
    思路:维护一个元素个数为i的最大堆,每比较一个元素,当该元素大于等于堆顶,则不操作,移到下一位;若该元素小于堆顶,移除堆顶元素,将该元素放入该堆中,维护该堆,保持为最大堆,需要时间复杂度log(i),继续比较下一个元素。
    总共有n个元素,所以需要nlog(i)的时间复杂度。

  • 方法二:
    分治算法:
    (1)若随机选取枢纽,线性期望时间O(N);
    (2)若选取数组的“中位数的中位数”作为枢纽,最坏情况下的时间复杂度O(N)。
    思想:随机Divide-and-Conquer,首先,类似于快速排序,随机选择一个元素,经过一次排序后,左边的元素全部小于该元素,右边的元素全部大于该元素。若第一次选择的元素位置正好在第i位,则找到该元素,查找结束;若该元素的位置大于i,则第i小的位置在该元素左端,重复执行第一步操作;若该元素的位置小于i,则第i小的位置在该元素右端,重复执行第一步操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 线性时间选择算法的基本思想是分治法,通过每次选取一个 pivot 元素将集合划分为左右两部分,然后根据 pivot 元素所在位置与 k 的大小关系来决定继续在哪一部分进行查,直到到第 k 个最小元素为止。而为了保证算法的时间复杂度为线性,需要对 pivot 元素的选择进行优化,这里采用基于"五元中值组取中值分割法"( median - of - median - of - five partitioning )。 具体实现过程如下: 1. 将集合 S 按照每 5 个元素一组划分,将每组内部进行排序并选中位数,再对这些中位数进行排序并选其中位数作为 pivot 元素。 2. 根据 pivot 元素将集合 S 划分为两部分,左边部分的元素均小于 pivot 元素,右边部分的元素均大于 pivot 元素。 3. 判断 pivot 元素在集合 S 中的位置 i 与 k 的大小关系,若 i = k,则到第 k 个最小元素;若 i < k,则在右边部分继续查第 k-i 个最小元素;若 i > k,则在左边部分继续查第 k 个最小元素。 4. 重复步骤 1-3 直到到第 k 个最小元素。 下面是用 C 语言实现的代码: ```c #include <stdio.h> #include <stdlib.h> #define N 10 int median_of_five(int a[], int i, int j, int k, int l, int m) { int b[5] = {a[i], a[j], a[k], a[l], a[m]}; for (int x = 0; x < 5; x++) for (int y = x + 1; y < 5; y++) if (b[x] > b[y]) { int temp = b[x]; b[x] = b[y]; b[y] = temp; } return b[2]; } int partition(int a[], int left, int right, int pivot) { while (left <= right) { while (a[left] < pivot) left++; while (a[right] > pivot) right--; if (left <= right) { int temp = a[left]; a[left] = a[right]; a[right] = temp; left++; right--; } } return left; } int linear_select(int a[], int left, int right, int k) { if (left == right) return a[left]; int n = right - left + 1; int num_groups = n / 5; if (n % 5 != 0) num_groups++; int medians[num_groups]; int group_left = left; for (int i = 0; i < num_groups; i++) { int group_right = group_left + 4; if (group_right > right) group_right = right; medians[i] = median_of_five(a, group_left, group_left + 1, group_left + 2, group_left + 3, group_right); group_left += 5; } int pivot = linear_select(medians, 0, num_groups - 1, num_groups / 2); int partition_index = partition(a, left, right, pivot); int pivot_index = partition_index - left + 1; if (k == pivot_index) return pivot; else if (k < pivot_index) return linear_select(a, left, partition_index - 1, k); else return linear_select(a, partition_index, right, k - pivot_index); } int main() { int a[N] = {10, 5, 2, 7, 4, 9, 8, 1, 3, 6}; int k = 5; int kth_smallest = linear_select(a, 0, N - 1, k); printf("The %dth smallest element is %d.\n", k, kth_smallest); return 0; } ``` 该代码的输结果为: ``` The 5th smallest element is 5. ``` ### 回答2: 线性时间选择算法是一种能够在线性时间内无序数组中第k个最小元素算法。这里选择使用基于"五元中值组取中值分割法"的线性时间选择算法来实现。 首先,我们将N个元素分成n/5组,每组5个元素,如果有剩余则将剩余的元素视为一组。接下来,对每个组进行排序,然后每个组的中位数。 然后,我们将这些中位数作为一个新的数组,并递归地调用线性时间选择算法,即新数组中的第newN/10个元素,newN为新数组的长度。 接下来,我们需要到这个中位数的位置。我们可以使用parition函数来实现。将新数组的数值分成两部分,一部分是小于等于中位数的数,另一部分是大于中位数的数。如果中位数的位置是k,那么我们就到了第k个最小元素算法结束。 若中位数的位置大于k,那么第k个最小元素应该在小于中位数的数中,我们可以递归地调用算法第k个最小元素。 若中位数的位置小于k,则第k个最小元素应该在大于中位数的数中,我们可以递归地调用算法第k中位数在新数组中的位置,即 k - m,这里m为中位数的位置。 通过这种分割的方式,可以将问题的规模缩小到原来的1/10,从而实现了线性时间的算法。该算法的时间复杂度为O(n)。 ### 回答3: 线性时间选择算法是一种能够在线性时间内到一个无序集合中第 k 个最小元素算法。而基于"五元中值组取中值分割法"的线性时间选择算法可以更有效地处理大规模数据集。 算法的基本思想是将待选择的集合 S 划分为若干个子集合,每个子集合中包含 5 个元素。然后对每个子集合进行排序,每个子集合的中值。再将这些中值组成一个新的集合 M,对 M 再次执行线性时间选择算法到其中的中值 M_median。 通过递归调用线性时间选择算法,可以将问题规模不断缩小,直到问题规模小于等于 5。 当问题规模小于等于 5 时,可以使用快速排序等排序算法到第 k 个元素。 在实现算法时,可以先预处理 S,将 S 划分为若干个子集合,并到每个子集合的中值。然后递归调用线性时间选择算法,将中值集合 M 作为输入,直到问题规模小于等于 5。 在函数的代码实现中,可以使用快速选择算法或者其他类似的算法,根据问题规模的大小选择最合适的排序算法。 总的来说,基于"五元中值组取中值分割法"的线性时间选择算法能够有效地在线性时间内到一个无序集合中第 k 个最小元素。利用预处理和递归调用,可将问题规模不断缩小,提高算法的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值