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

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

相关推荐
记得开心一点嘛3 分钟前
Nginx与Tomcat之间的关系
java·nginx·tomcat
界面开发小八哥16 分钟前
「Java EE开发指南」如何用MyEclipse构建一个Web项目?(一)
java·前端·ide·java-ee·myeclipse
王伯爵18 分钟前
<packaging>jar</packaging>和<packaging>pom</packaging>的区别
java·pycharm·jar
Kenneth風车24 分钟前
【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)11
算法·机器学习·分类
最后一个bug28 分钟前
rt-linux中使用mlockall与free的差异
linux·c语言·arm开发·单片机·嵌入式硬件·算法
Q_192849990641 分钟前
基于Spring Boot的个人健康管理系统
java·spring boot·后端
蹉跎x1 小时前
力扣1358. 包含所有三种字符的子字符串数目
数据结构·算法·leetcode·职场和发展
m0_748245172 小时前
Web第一次作业
java
小码的头发丝、2 小时前
Java进阶学习笔记|面向对象
java·笔记·学习
m0_548514772 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript