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.小结

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

相关推荐
dj2442945707几秒前
JAVA中的Lamda表达式
java·开发语言
工业3D_大熊14 分钟前
3D可视化引擎HOOPS Luminate场景图详解:形状的创建、销毁与管理
java·c++·3d·docker·c#·制造·数据可视化
szc176718 分钟前
docker 相关命令
java·docker·jenkins
程序媛-徐师姐27 分钟前
Java 基于SpringBoot+vue框架的老年医疗保健网站
java·vue.js·spring boot·老年医疗保健·老年 医疗保健
yngsqq28 分钟前
c#使用高版本8.0步骤
java·前端·c#
尘浮生38 分钟前
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
小白不太白95042 分钟前
设计模式之 模板方法模式
java·设计模式·模板方法模式
Tech Synapse44 分钟前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
xoxo-Rachel1 小时前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql