leedcode-小知识

前缀和

  • 问题描述:给定一个整数数组(含有负数)和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数
  • preSum[nums.length + 1]:记录 [0…i-1] 的和(必须记录 0)
  • nums[i…j] = preSum[j+1] - preSum[i]
  • 优化:借助哈希表,在记录前缀和的同时记录该前缀和出现的次数
  • leedcode-525

随机数

  • 问题描述:给定一个未知长度的链表,设计一个算法,只能遍历一次,随机地返回链表中的 一个 / k 个节点
  1. 单个节点(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;
 }
  1. 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
  • 求余运算规则:
    在这里插入图片描述
  1. 循环求余
    在这里插入图片描述

long rem = 1;
int a = n / 3 - 1;
int p = 1000000007;
for(; a > 0; a--){
	rem = (rem * 3) % p;
}

  1. 快速幂求余

在这里插入图片描述

// 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()));
     }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值