定义
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 可以看到,它是一个静态本地方法,由虚拟机实现,效率自然比用java一个个复制高。
方法含义
从源数组src取元素,范围为下标srcPos到srcPos+length-1,取出共length个元素,存放到目标数组中,存放位置为下标destPos到destPos+length-1。
152. 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
我们可以根据正负性进行分类讨论。
考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个 f min (i),它表示以第 ii 个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:
它代表第 ii个元素结尾的乘积最大子数组的乘积 fmax (i),可以考虑把 ai加入第 i - 1个元素结尾的乘积最大或最小的子数组的乘积中,二者加上 ai,三者取大,就是第 i 个元素结尾的乘积最大子数组的乘积。第 i个元素结尾的乘积最小子数组的乘积f min
(i) 同理。
不难给出这样的实现:
class Solution {
public int maxProduct(int[] nums) {
int length = nums.length;
int[] maxF = new int[length];
int[] minF = new int[length];
System.arraycopy(nums, 0, maxF, 0, length);
System.arraycopy(nums, 0, minF, 0, length);
for (int i = 1; i < length; ++i) {
maxF[i] = Math.max(maxF[i - 1] * nums[i], Math.max(nums[i], minF[i - 1] * nums[i]));
minF[i] = Math.min(minF[i - 1] * nums[i], Math.min(nums[i], maxF[i - 1] * nums[i]));
}
int ans = maxF[0];
for (int i = 1; i < length; ++i) {
ans = Math.max(ans, maxF[i]);
}
return ans;
}
}
给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。
一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。
请你返回乘积为正数的最长子数组长度。
记录以i结尾的最大正负最大长度,然后需要考虑i-1是否为0
的特殊情况
class Solution {
public int getMaxLen(int[] nums) {
int length = nums.length;
int[] positive = new int[length];
int[] negative = new int[length];
if (nums[0] > 0) {
positive[0] = 1;
} else if (nums[0] < 0) {
negative[0] = 1;
}
int maxLength = positive[0];
for (int i = 1; i < length; i++) {
if (nums[i] > 0) {
positive[i] = positive[i - 1] + 1;
negative[i] = negative[i - 1] > 0 ? negative[i - 1] + 1 : 0;
} else if (nums[i] < 0) {
positive[i] = negative[i - 1] > 0 ? negative[i - 1] + 1 : 0;
negative[i] = positive[i - 1] + 1;
} else {
positive[i] = 0;
negative[i] = 0;
}
maxLength = Math.max(maxLength, positive[i]);
}
return maxLength;
}
}
从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉搜索树,输出所有可能生成此树的数组。
//用了回溯思想
示例:
给定如下二叉树
2
/ \
1 3
返回:
[
[2,1,3],
[2,3,1]
]
public class Solution {
private List<List<Integer>> res;
public List<List<Integer>> BSTSequences(TreeNode root) {
res = new ArrayList<>();
Deque<TreeNode> path = new LinkedList<>();
if (root != null) {
path.add(root);
}
dfs(path, new ArrayList<>());
return res;
}
private void dfs(Deque<TreeNode> path, List<Integer> ans) {
if (path.isEmpty()) {
res.add(new ArrayList<>(ans));
return;
}
int size = path.size();
for (int i = 0; i < size; i++) {
TreeNode node = path.removeFirst();
if (node.left != null) {
path.addLast(node.left);
}
if (node.right != null) {
path.addLast(node.right);
}
ans.add(node.val);
dfs(path, ans);
if (node.left != null) {
path.removeLast();
}
if (node.right != null) {
path.removeLast();
}
path.addLast(node);
ans.remove(ans.size() - 1);
}
}
}
设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
public class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode newNode = new DLinkedNode(key, value);
// 添加进哈希表
cache.put(key, newNode);
// 添加至双向链表的头部
addToHead(newNode);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode tail = removeTail();
// 删除哈希表中对应的项
cache.remove(tail.key);
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}