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

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

相关推荐
北上ing19 分钟前
算法练习:19.JZ29 顺时针打印矩阵
算法·leetcode·矩阵
无声旅者1 小时前
深度解析 IDEA 集成 Continue 插件:提升开发效率的全流程指南
java·ide·ai·intellij-idea·ai编程·continue·openapi
.格子衫.2 小时前
真题卷001——算法备赛
算法
XiaoyaoCarter2 小时前
每日一道leetcode
c++·算法·leetcode·职场和发展·二分查找·深度优先·前缀树
Ryan-Joee2 小时前
Spring Boot三层架构设计模式
java·spring boot
Hygge-star2 小时前
【数据结构】二分查找5.12
java·数据结构·程序人生·算法·学习方法
dkmilk2 小时前
Tomcat发布websocket
java·websocket·tomcat
工一木子3 小时前
【Java项目脚手架系列】第七篇:Spring Boot + Redis项目脚手架
java·spring boot·redis
哞哞不熬夜3 小时前
JavaEE--初识网络
java·网络·java-ee
等等5433 小时前
Java EE初阶——wait 和 notify
java·开发语言