class 024 随机选择算法

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解了, 所以就将整个过程写下来了。

这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐.
https://space.bilibili.com/8888480?spm_id_from=333.999.0.0

在这里插入图片描述

1. 题目的解释

举一个例子:有一个数组:arr[100], 所以找这个 arr数组 中第 1 大的数字(或者是第 1 小的数字), 不就是找 arr数组 中第 100 小的数字(第 100 大的) 吗?

时间复杂度的要求是:O(n), 所以首先排除了先排序后找的方法, 因为无论怎么进行排序 (只要是基于比较的排序), 时间复杂度最少最少也是:O(n * log(n)).

2. 逻辑实现

借用随机快速排序进行实现的.

假设, 此时我要找一个第 52大 的数字, 假设随机出来的数字是:34, 一个数组: arr[100], 然后进行随机快速排序的过程, arr[0.........99], 假设 34 这个数字在这个数组中有很多个, 此时有三种情况:

  1. 排序好的位置是:49 ~ 66, 所以第 52 大的数字肯定是 34.
  2. 排好序的位置是:25 ~ 32, 所以第 52 大的数字肯定在 32位置 右边. 此时在这个位置上继续选择一个数字, 然后继续重复随机过程.
  3. 排好序的位置是:77 ~ 89, 所以第 52 大的数字肯定在 77位置 左边. 此时在这个位置上继续选择一个数字, 然后继续重复随机过程.

这个过程将随机快速排序的划分过程高度利用了. 而且这个过程只看随机选择的数字有没有命中对应的位置. 若是没有命中, 那我经过随机快速的划分过程已经实现了划分, 所以只用走一侧就行了(左边右边选一个).

3. 时间复杂度分析

3 .1 最差情况

还是原来的数组 arr[100], 若是我想要的是 0 位置的数字, 但是恰好随机选择的数字是:99 位置的数字, 那么我要从 0 ~ 98 范围上进行选择, 然后我又运气不好, 选择了 98 位置的数字, 那么我要从 0 ~ 98 范围上进行选择, 以此类推, 直到最后我才选择到 0 位置的数字,

所以最差情况的时间复杂度是:O(n^2).

3.2 最好情况

最好情况就是我想要选择的数字是在中间位置的数字,

  1. 可能一开始经过随机选择过程我直接选择到了中间位置的数字, 直接返回,
  2. 要是没有返回, 继续在左右两侧选择一侧随机选择一个数字, 若是命中就返回, 没有命中继续选择一侧随机选择数字.
  3. 假设上述行为每一次都实现到

所以最开始过程是 N, 第二次过程是 N/2, 第三次过程是:N/4, 所以这个过程加起来 N + N/2 + N/4 + ..., 这个过程是一个等比数列, 最后肯定是收敛于 O(n).

3.3 总结

所以分析时间复杂度的方式还是根据期望进行求解, 最后求解出来的时间复杂度是:O(n). 只需要记住就行了, 不用去证明.

4. 代码实例

代码实例和逻辑实现的情况是一样的, 自己看肯定能看懂, 也没有什么需要注意的部分, 最后直接看就行了. 该写的注释也都写了.

// 随机选择算法,时间复杂度O(n)  
public static int findKthLargest(int[] nums, int k) {  
    return randomizedSelect(nums, nums.length - k);  
}  
  
// 如果arr排序的话,在i位置的数字是什么  
public static int randomizedSelect(int[] arr, int i) {  
    int ans = 0;  
    for (int l = 0, r = arr.length - 1; l <= r;) {  
       // 随机这一下,常数时间比较大  
       // 但只有这一下随机,才能在概率上把时间复杂度收敛到O(n)  
       partition(arr, l, r, arr[l + (int) (Math.random() * (r - l + 1))]);  
       // 因为左右两侧只需要走一侧  
       // 所以不需要临时变量记录全局的first、last  
       // 直接用即可  
       if (i < first) {  
          r = first - 1;  
       } else if (i > last) {  
          l = last + 1;  
       } else {  
          ans = arr[i];  
          break;  
       }  
    }  
    return ans;  
}  
  
// 荷兰国旗问题  
public static int first, last;  
  
public static void partition(int[] arr, int l, int r, int x) {  
    first = l;  
    last = r;  
    int i = l;  
    while (i <= last) {  
       if (arr[i] == x) {  
          i++;  
       } else if (arr[i] < x) {  
          swap(arr, first++, i++);  
       } else {  
          swap(arr, i, last--);  
       }  
    }  
}  
  
public static void swap(int[] arr, int i, int j) {  
    int tmp = arr[i];  
    arr[i] = arr[j];  
    arr[j] = tmp;  
}
在C#中,带权重的随机选择通常涉及到概率分布和随机数生成。一个常见的方法是使用`System.Collections.Generic.Dictionary<TKey, TValue>`或者`Dictionary<T, double>`来存储元素及其对应的权重,然后利用这些权重来计算每个元素被选中的概率。这里有一个简单的概念实现: ```csharp using System; using System.Collections.Generic; using System.Linq; class WeightedRandomSelection { private readonly Dictionary<int, double> weights; public WeightedRandomSelection(Dictionary<int, double> weights) { this.weights = weights; // 确保所有权重的和为1(或接近于1),以便进行正确的概率计算 double totalWeight = weights.Values.Sum(); if (totalWeight != 1) { weights = weights.ToDictionary(kvp => kvp.Key, kvp => kvp.Value / totalWeight); } } public int Choose() { double randomValue = Random.NextDouble(); // 0到1之间的随机浮点数 double cumulativeProbability = 0; foreach (var (key, weight) in weights) { cumulativeProbability += weight; if (randomValue <= cumulativeProbability) { return key; } } throw new InvalidOperationException("无法生成随机选择,可能是因为权重总和不为1"); } } public class Program { static void Main(string[] args) { Dictionary<int, double> selections = new Dictionary<int, double> { {1, 0.3}, {2, 0.4}, {3, 0.3} }; WeightedRandomSelection random = new WeightedRandomSelection(selections); int chosenNumber = random.Choose(); Console.WriteLine($"随机选择了数字: {chosenNumber}"); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值