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

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

相关推荐
源代码•宸1 分钟前
Leetcode—85. 最大矩形【困难】
经验分享·算法·leetcode·职场和发展·golang·单调栈
爬山算法2 分钟前
Hibernate(38)如何在Hibernate中配置乐观锁?
android·java·hibernate
江上月5136 分钟前
JMeter中级指南:从数据提取到断言校验全流程掌握
java·前端·数据库
晨旭缘7 分钟前
零基础后端入门:JDK21 + PostgreSQL+Java项目
java·数据库·postgresql
BullSmall8 分钟前
SpringBoot 项目日志规范(企业级标准 + 最佳实践)
java·spring boot·spring
better_liang8 分钟前
每日Java面试场景题知识点之-线程池
java·线程池·并发编程·juc·企业级开发
一直都在5729 分钟前
SpringBoot:自动配置原理
java·spring boot·spring
郝学胜-神的一滴9 分钟前
《机器学习》经典教材全景解读:周志华教授匠心之作的技术深探
数据结构·人工智能·python·程序人生·机器学习·sklearn
ss27310 分钟前
ruoyi 新增每页分页条数
java·数据库·mybatis
平哥努力学习ing13 分钟前
《数据结构》-第八章 排序
数据结构·算法·排序算法