Java算法OJ(8)随机选择算法


目录

1.前言

2.正文

[1. 入口方法:findKthLargest](#1. 入口方法:findKthLargest)

[2. 核心方法:randomizedSelect](#2. 核心方法:randomizedSelect)

[3. 划分方法:partition](#3. 划分方法:partition)

[4. 工具方法:swap](#4. 工具方法:swap)

3.小结


1.前言

哈喽大家好吖,上次我们学了随机快速排序,并大致了解了其通过随机来使时间复杂度收敛到更小的程度,今天学习的随机选择算法是随机快速排序的延伸,话不多说让我们开始吧。

2.正文

先附上测试链接:

215. 数组中的第K个最大元素 - 力扣(LeetCode)https://leetcode.cn/problems/kth-largest-element-in-an-array/description/题干再粘一下:

首先我们需要先明确一个问题,找一个无序数组中的第K个最大的元素,和在这个无序数组找第n-K个最小的元素,这俩个问题等价,这样我们就实现了问题的转化。

另外还需要注意一个问题,题目提醒找的不是第K个不同的元素,意味着会有重复,就可能会出现同一个数组第x个和第x+y个最大的元素是同一个数,清楚好这一点后,我们按照上一讲随机快速排序的思想对其实现,排序完后直接返回ans即可。下面每一段代码详细讲解:

1. 入口方法:findKthLargest

java 复制代码
public static int findKthLargest(int[] nums, int k) {
    return randomizedSelect(nums, nums.length - k);
}
  • 功能 :找到数组中第 k 大的元素。
  • 转换
    • k 大相当于按升序排序后第 nums.length - k 小的元素。
    • 比如数组 [3, 2, 1, 5, 6, 4],第 2 大元素是 5,按升序排序后是第 4 小。

2. 核心方法:randomizedSelect

java 复制代码
public static int randomizedSelect(int[] arr, int i) {
    int ans = 0;
    for (int l = 0, r = arr.length - 1; l <= r;) {
        partition(arr, l, r, arr[l + (int) (Math.random() * (r - l + 1))]);
        if (i < first) {
            r = first - 1;
        } else if (i > last) {
            l = last + 1;
        } else {
            ans = arr[i];
            break;
        }
    }
    return ans;
}
  • 输入
    • arr: 待查找的数组。
    • i: 目标索引,即排序后第 i 小的元素。
  • 过程
    1. 定义搜索范围 lr,初始为整个数组。
    2. [l, r] 范围内随机选择一个基准元素(arr[l + (int) (Math.random() * (r - l + 1))])。
    3. 调用 partition 方法按照基准元素划分数组。
    4. 判断目标索引 i 所在的部分:
      • 如果 i < first,说明目标在左部分,缩小范围到 [l, first - 1]
      • 如果 i > last,说明目标在右部分,缩小范围到 [last + 1, r]
      • 否则,i 在基准元素的范围 [first, last] 内,直接返回结果。
  • 退出条件
    • 找到目标索引 i 的元素,赋值给 ans,并跳出循环。

3. 划分方法:partition

java 复制代码
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--);
        }
    }
}
  • 输入
    • arr: 当前数组。
    • l, r: 当前区间范围。
    • x: 基准值。
  • 功能 :实现荷兰国旗划分,将数组分为三部分:
    • 小于 x 的元素放左侧。
    • 等于 x 的元素居中。
    • 大于 x 的元素放右侧。
  • 过程
    1. 初始化两个指针 firstlast,分别表示小于 x 部分的右边界和大于 x 部分的左边界。
    2. 遍历数组:
      • 如果 arr[i] == x,说明是中间部分,跳过。
      • 如果 arr[i] < x,交换到左侧,并更新 firsti
      • 如果 arr[i] > x,交换到右侧,并更新 last
  • 输出
    • 通过修改全局变量 firstlast,标记中间部分的范围。

4. 工具方法:swap

java 复制代码
public static void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
  • 功能:交换数组中两个索引的元素。

最后汇总一下代码并梳理一下大体思路:

java 复制代码
public static int findKthLargest(int[] nums, int k) {
		return randomizedSelect(nums, nums.length - k);
	}

	public static int randomizedSelect(int[] arr, int i) {
		int ans = 0;
		for (int l = 0, r = arr.length - 1; l <= r;) {
			partition(arr, l, r, arr[l + (int) (Math.random() * (r - l + 1))]);
			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;
	}
  • 调用 findKthLargest,将问题转化为查找第 nums.length - k 小的元素。
  • randomizedSelect 递归地在数组中划分并缩小范围:
    • 随机选择一个基准元素,划分数组。
    • 根据目标索引所在的部分,只递归需要的部分。
  • 使用 partition 划分数组。
    • 通过荷兰国旗问题,将数组划分为三部分。
  • 返回结果

提交以下没有问题。

3.小结

今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,你的支持就是对我最大的鼓励,加油!

相关推荐
Erik_LinX5 分钟前
算法日记25:01背包(DFS->记忆化搜索->倒叙DP->顺序DP->空间优化)
算法·深度优先
Alidme13 分钟前
cs106x-lecture14(Autumn 2017)-SPL实现
c++·学习·算法·codestepbystep·cs106x
小王努力学编程13 分钟前
【算法与数据结构】单调队列
数据结构·c++·学习·算法·leetcode
最遥远的瞬间15 分钟前
15-贪心算法
算法·贪心算法
非 白22 分钟前
【Java】单例模式
java·笔记·单例模式
IDRSolutions_CN36 分钟前
如何在 PDF 文件中嵌入自定义数据
java·经验分享·pdf·软件工程·团队开发
_风中无我。42 分钟前
Spring的过滤器获取请求体中JSON参数,同时解决Controller获取不到请求体参数的问题。
java·spring·json
bing_1581 小时前
Spring Boot 中为什么 需要限流、降级和熔断?
java
万兴丶1 小时前
Unity 适用于单机游戏的红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含源码)
数据结构·unity·设计模式·c#
ccm031 小时前
高效开发助手:深入了解Hutool工具库
java·g工具库