版本说明
当前版本号[20231113]。
版本 | 修改说明 |
---|---|
20231113 | 初版 |
目录
文章目录
- 版本说明
- 目录
- 数据流的中位数
- 题目
- 解题思路
- 代码思路
- 参考代码
- 乘积最大子数组
- 题目
- 解题思路
- 代码思路
- 参考代码
- 旋转链表
- 题目
- 解题思路
- 代码思路
- 参考代码
数据流的中位数
题目
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
解题思路
- 使用两个优先队列(最小堆和最大堆)来存储数据。
- 初始化时,将最大堆作为较小的一半元素,最小堆作为较大的一半元素。
- 添加一个数字时,将其添加到最大堆中,然后将最大堆的顶部元素移动到最小堆中。这样可以保证最小堆的大小始终大于或等于最大堆的大小。
- 如果最小堆的大小大于最大堆的大小,将最小堆的顶部元素移动回最大堆中。这样可以保证两个堆的大小之差不超过1。
- 查找中位数时,如果两个堆的大小相等,则中位数为两个堆顶元素的平均值;否则,中位数为最大堆的顶部元素。
代码思路
-
在初始化时,创建了两个优先队列:
min
和max
。min
是一个最小堆,用于存储较小的一半元素;max
是一个最大堆,用于存储较大的一半元素。min = new PriorityQueue<>(); // 较小的一半元素队列 max = new PriorityQueue<>((a, b) -> { // 较大的一半元素队列,按照降序排列
-
addNum(int num)
方法用于向数据结构中添加一个新的数字。首先,将数字添加到max
堆中。然后,从max
堆中移除顶部的元素,并将其添加到min
堆中。这样可以确保min
堆的大小始终大于或等于max
堆的大小。如果min
堆的大小比max
堆的大小大,则将min
堆中的顶部元素移除并添加到max
堆中,以保持平衡。/** * 向数据结构中添加一个数字 * @param num 要添加的数字 */ public void addNum(int num) { max.add(num); // 将数字添加到较大的一半元素队列中 min.add(max.remove()); // 从较大的一半元素队列中取出最大值,并添加到较小的一半元素队列中 // 如果较小的一半元素队列的大小大于较大的一半元素队列的大小,则将较小的一半元素队列中的最小值移动到较大的一半元素队列中 if (min.size() > max.size()) { max.add(min.remove()); } }
-
findMedian()
方法用于查找当前数据结构的中位数。如果max
堆和min
堆的大小相等,则中位数是两个堆顶元素的平均值;否则,中位数是max
堆的顶部元素。
/**
* 查找当前数据结构的中位数
* @return 中位数
*/
public double findMedian() {
// 如果两个队列的大小相等,则中位数为两个队列顶部元素的平均值
if (max.size() == min.size()) {
return (max.peek() + min.peek()) / 2.0;
} else {
// 否则,中位数为较大的一半元素队列的顶部元素
return max.peek();
}
}
参考代码
这段代码是用于实现一个数据结构来查找中位数。该类使用两个优先队列**(最小堆和最大堆)**来实现这个功能。
class MedianFinder {
PriorityQueue<Integer> min;
PriorityQueue<Integer> max;
/** initialize your data structure here. */
public MedianFinder() {
min = new PriorityQueue<>();
max = new PriorityQueue<>((a, b) -> {
return b - a;
});
}
public void addNum(int num) {
max.add(num);
min.add(max.remove());
if (min.size() > max.size())
max.add(min.remove());
}
public double findMedian() {
if (max.size() == min.size())
return (max.peek() + min.peek()) / 2.0;
else
return max.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
乘积最大子数组
题目
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出:
6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
解题思路
解题思路如下:
- 初始化三个变量,max表示当前最大乘积,imax表示以当前元素结尾的最大乘积,imin表示以当前元素结尾的最小乘积。初始值都设为1,因为数组中至少有一个数字。
- 遍历数组中的每个元素,对于每个元素nums[i]:
- 如果nums[i]小于0,说明负数乘以最大值会变成最小值,负数乘以最小值会变成最大值。所以交换imax和imin的值。
- 更新imax为当前元素与imax的乘积和当前元素中的较大值。这样可以保证imax始终是当前子数组的最大乘积。
- 更新imin为当前元素与imin的乘积和当前元素中的较小值。这样可以保证imin始终是当前子数组的最小乘积。
- 更新max为当前最大乘积和imax中的较大值。这样可以保证max始终是整个数组的最大乘积。
- 遍历结束后,返回max作为结果。
代码思路
-
定义公共方法maxProduct,该方法接收一个整数数组nums作为参数,返回一个整数值。
-
在maxProduct方法中,初始化三个变量max、imax和imin,分别表示最大值、当前最大值和当前最小值。其中,max的初始值为Integer.MIN_VALUE,imax和imin的初始值为1。
public int maxProduct(int[] nums) { int max = Integer.MIN_VALUE, imax = 1, imin = 1; // 初始化最大值为最小整数,imax和imin为1
-
使用for循环遍历整数数组nums,对于每个元素nums[i],执行以下操作:
for (int i = 0; i < nums.length; i++)
-
如果nums[i]小于0,说明当前元素为负数,需要交换imax和imin的值。
if (nums[i] < 0) { // 如果当前元素为负数 int tmp = imax; // 交换imax和imin的值 imax = imin; imin = tmp; }
-
更新imax为当前元素与imax的乘积和当前元素中的较大值。
imax = Math.max(imax * nums[i], nums[i]); // 更新imax为当前元素与imax的乘积和当前元素中的较大值
-
更新imin为当前元素与imin的乘积和当前元素中的较小值。
imin = Math.min(imin * nums[i], nums[i]); // 更新imin为当前元素与imin的乘积和当前元素中的较小值
-
更新max为当前最大值和imax中的较大值。
max = Math.max(max, imax); // 更新最大值为当前最大值和imax中的较大值
-
-
循环结束后,返回最大值max。
return max; // 返回最大值
参考代码
这段代码是用于求解一个整数数组中连续子数组的最大乘积。
class Solution {
public int maxProduct(int[] nums) {
int max = Integer.MIN_VALUE, imax = 1, imin = 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] < 0) {
int tmp = imax;
imax = imin;
imin = tmp;
}
imax = Math.max(imax * nums[i], nums[i]);
imin = Math.min(imin * nums[i], nums[i]);
max = Math.max(max, imax);
}
return max;
}
}
旋转链表
题目
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
提示:
链表中节点的数目在范围 [0, 500] 内
-100 <= Node.val <= 100
0 <= k <= 2 * 109
解题思路
- 首先判断链表是否为空或者k是否为0,如果满足任一条件,则直接返回原链表。
- 初始化两个指针cursor和tail,分别指向链表的头节点和尾节点。同时记录链表的长度length。
- 遍历链表,找到尾节点tail,并让cursor指向头节点。
- 计算需要旋转的次数loop,即length减去k对length取模的结果。
- 将尾节点tail的next指针指向头节点head,完成链表的连接。
- 再次遍历链表,让cursor和tail分别向后移动loop次,直到cursor到达新的头节点。
- 将尾节点tail的next指针设置为null,断开循环连接。
- 返回新的头节点cursor。
代码思路
-
定义链表节点类ListNode,包含两个成员变量:val表示节点值,next表示指向下一个节点的指针。同时提供一个构造函数,用于初始化节点值。
// 定义链表节点类 public class ListNode { int val; // 节点值 ListNode next; // 指向下一个节点的指针 ListNode(int x) { // 构造函数,初始化节点值 val = x; } }
-
定义解决方案类Solution,包含一个方法rotateRight,用于实现链表的右旋转操作。该方法接收两个参数:head表示链表的头节点,k表示需要旋转的次数。
public ListNode rotateRight(ListNode head, int k)
-
在rotateRight方法中,首先判断链表是否为空或k是否为0,如果满足任一条件,则直接返回原链表。
// 如果链表为空或k为0,直接返回原链表 if (head == null || k == 0) { return head; }
-
定义游标cursor和尾节点tail,并初始化为null。
// 定义游标和尾节点 ListNode cursor = head; ListNode tail = null;
-
使用while循环遍历链表,计算链表的长度length。
while (cursor.next != null) { cursor = cursor.next; length++; }
-
计算需要旋转的次数loop,即length减去k对length取模的结果。
// 计算需要旋转的次数 int loop = length - (k % length);
-
找到尾节点tail,并将其next指针指向头节点head,完成链表的连接。
// 找到尾节点并连接头节点 tail = cursor; cursor.next = head;
-
移动游标cursor到旋转位置,即头节点head。
// 移动游标到旋转位置 cursor = head;
-
使用for循环移动游标cursor和尾节点tail,直到游标cursor到达旋转位置。
for (int i = 0; i < loop; i++) { cursor = cursor.next; tail = tail.next; }
-
断开循环连接,即将尾节点tail的next指针设置为null。
// 断开循环连接,返回新的头节点 tail.next = null;
-
返回新的头节点cursor。
return cursor;
参考代码
这段代码是用于实现链表的右旋转操作。
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
ListNode cursor = head;
ListNode tail = null;
int length = 1;
while (cursor.next != null) {
cursor = cursor.next;
length++;
}
int loop = length - (k % length);
tail = cursor;
cursor.next = head;
cursor = head;
for (int i = 0; i < loop; i++) {
cursor = cursor.next;
tail = tail.next;
}
tail.next = null;
return cursor;
}
}