前缀和
- 问题描述:给定一个整数数组(含有负数)和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数
- preSum[nums.length + 1]:记录 [0…i-1] 的和(必须记录 0)
- nums[i…j] = preSum[j+1] - preSum[i]
- 优化:借助哈希表,在记录前缀和的同时记录该前缀和出现的次数
- leedcode-525
随机数
- 问题描述:给定一个未知长度的链表,设计一个算法,只能遍历一次,随机地返回链表中的 一个 / k 个节点
- 单个节点(leedcode-382)
- 结论:当遇到第 i 个元素时,选择该元素的概率为 1 / i ,1 - 1/i 的概率保持原有的选择
- 证明:假设共有 n 个元素,我们要的随机性无非就是每个元素被选择的概率是 1 / n,对于第 i 个元素,被选择的概率:
public int getRandom(ListNode head) {
Random rand = new Random();
ListNode p = head;
int i = 0;
int ans = 0;
while(p != null) {
// [0, i): 1 / i
if(rand.nextInt(++i) == 0) {
ans = p.val;
}
p = p.next;
}
return ans;
}
- k 个节点
- 结论:当遇到第 i 个元素时,选择该元素的概率为 k / i,1 - k/i 的概率保持原有的选择
- 证明:虽然每个更新选择的概率增大了 k 倍,但选到具体第 i 个元素的概率还要乘 1/k
public int[] getKRandom(ListNode head, int k) {
Random rand = new Random();
ListNode p = head;
int[] ans = new int[k];
int i = 0;
// 前 k 个元素先默认选上,后面更新
for(; i < k && p != null; i++) {
ans[i] = p.val;
p = p.next;
}
// i = k
while(p != null) {
// 同时更新 ans[j] 的内容
int j = rand.nextInt(++i);
if(j < k) {
ans[j] = p.val;
}
p = p.next;
}
return ans;
}
大数求余法(剑指-14)
- 在仅使用 int32 类型存储的前提下,正确计算x^a % p
- 求余运算规则:
- 循环求余
long rem = 1;
int a = n / 3 - 1;
int p = 1000000007;
for(; a > 0; a--){
rem = (rem * 3) % p;
}
- 快速幂求余
// input : n
long x = 3, rem = 1;
int a = n / 3 - 1;
int b = n % 3;
int p = 1000000007;
for(; a > 0; a /= 2){
if(a % 2 == 1){
rem = (rem * x) % p;
}
x = (x * x) % p;
}
O(1)增删(leedcode-380)
- O(1)时间复杂度增加,删除
- 借助数组和哈希表,
– 插入:在数组最后插入(O(1))
– 删除:通过哈希表定位数组中要删除元素的位置,将数组中最后一个元素放置在被删除元素位置上,并删除最后一个元素
class RandomizedSet {
public LinkedList<Integer> list;
public HashMap<Integer, Integer> map;
Random rand = new Random();
/** Initialize your data structure here. */
public RandomizedSet() {
list = new LinkedList<>();
map = new HashMap<Integer, Integer>();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(map.containsKey(val)){
return false;
}
list.addLast(val);
map.put(val, list.size() - 1);
return true;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(!map.containsKey(val)){
return false;
}
int index = map.get(val);
// 修改最后一个元素的下标, 并在map中删除所要删除的元素
map.put(list.getLast(), index);
map.remove(val);
// 将最后一个元素放在要被删除的位置
list.set(index, list.getLast());
// list删除最后一个
list.removeLast();
return true;
}
/** Get a random element from the set. */
public int getRandom() {
int rand = (int)(Math.random() * list.size());
return list.get(rand);
// return list.get(rand.nextInt(list.size()));
}
}