需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构。
注意: 允许出现重复元素。
insert(val):向集合中插入元素 val。
remove(val):当 val 存在时,从集合中移除一个 val。
getRandom:从现有集合中随机获取一个元素。每个元素被返回的概率应该与其在集合中的数量呈线性相关。
解答
class RandomizedCollection {
HashMap<Integer, HashSet<Integer>> map;
ArrayList<Integer> list;
/** Initialize your data structure here. */
public RandomizedCollection() {
map = new HashMap<>();
list = new ArrayList<>();
}
/** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
public boolean insert(int val) {
if(!map.containsKey(val)){
list.add(val);
HashSet<Integer> set = new HashSet<>();
set.add(list.size()-1);
map.put(val,set);
return true;
}else{
HashSet<Integer> set = map.get(val);
list.add(val);
set.add(list.size()-1);
map.put(val,set);
return false;
}
}
/** Removes a value from the collection. Returns true if the collection contained the specified element. */
public boolean remove(int val) {
if(map.containsKey(val)){
HashSet<Integer> set = map.get(val);
Integer index = set.iterator().next();//删掉这个索引位置的val
set.remove(index);//删除这个索引
if(set.size() == 0) map.remove(val);
if(index != list.size()-1) {
int lastNumber = list.get(list.size()-1);
HashSet<Integer> set1 = map.get(lastNumber);
set1.remove(list.size() - 1);//最后一个数字的索引删除
list.set(index, lastNumber);//最后一个数字替换要删除的位置
set1.add(index);//新的索引加入。
}
list.remove(list.size() - 1);
return true;
}else return false;
}
/** Get a random element from the collection. */
public int getRandom() {
Random random = new Random();
int index = random.nextInt(list.size());
return list.get(index);
}
}
分析
和380题目很像,只是这道题目可以重复,所以hashMap 记录的value不能是一个索引值
而应该是一个索引集合,为了查找的速度快 所以 索引集合使用HashSet
增加和删除的过程和上一题类似
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
解答
ArrayList<Integer> list = new ArrayList<>();
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
ListNode p = head;
while(p!=null){
list.add(p.val);
p = p.next;
}
}
/** Returns a random node's value. */
public int getRandom() {
Random random = new Random();
int index = random.nextInt(list.size());
return list.get(index);
}
//方法二
private ListNode head;
public Solution(ListNode head) {
this.head = head;
}
public int getRandom() {
int res = head.val;
ListNode no = head.next;
int i = 2;
Random random = new Random();
while(no!=null){
if(random.nextInt(i) == 0){
res = no.val;
}
i++;
no = no.next;
}
return res;
}
分析
将链表里的数字存在数组中,然后根据随机数生成一个索引,返回索引对应的值。
方法二是使用常数级空间复杂度 蓄水池出样算法
random.nextInt(i)==0
这行代码决定了最后输出的res,即所有满足该条件且i最大的no.val
会被输出。那么我们不妨以i的值降序来看这里的while循环:
i==n
时执行res=no.val
的概率为1/n,即最终选择第n的点的概率是1/n;i==n-1
时执行了res=no.val
,即(1-1/n)*(1/(n-1))=1/n…以此类推,所有n个点被选的概率都是1/n。
提交结果
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
解答
//方法一
public boolean canConstruct(String ransomNote, String magazine) {
char[] chars = ransomNote.toCharArray();
char[] chars2 = magazine.toCharArray();
Arrays.sort(chars);
Arrays.sort(chars2);
int index = 0;
int index2 = 0;
while(index < chars.length && index2 < chars2.length){
if(chars[index] == chars2[index2]){
index++;
index2++;
}else if(chars[index] > chars2[index2]){
index2++;
}else {
return false;
}
}
return index == chars.length;
}
//方法二
public boolean canConstruct(String ransomNote, String magazine) {
HashMap<Character,Integer> map2 = new HashMap<>();
char[] chars = magazine.toCharArray();
for(char c : chars){
int sum = map2.getOrDefault(c,0);
sum++;
map2.put(c,sum);
}
char[] chars2 = ransomNote.toCharArray();
for(char c : chars2){
if(!map2.containsKey(c)){
return false;
}else{
int sum = map2.get(c);
sum--;
if(sum == 0)map2.remove(c);
else map2.put(c,sum);
}
}
return true;
}
// 方法三
public boolean canConstruct(String ransomNote, String magazine) {
char[] chars = new char[26];
for(int i = 0;i < magazine.length();i++){
chars[magazine.charAt(i) - 'a']++;
}
for(int i = 0;i < ransomNote.length();i++){
if(chars[ransomNote.charAt(i)-'a'] == 0)return false;
else chars[ransomNote.charAt(i)-'a']--;
}
return true;
}
分析
提交结果
解答
//方法一
private int[] array;
private int[] original;
private Random rand = new Random();
private List<Integer> getArrayCopy() {
List<Integer> asList = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
asList.add(array[i]);
}
return asList;
}
public Solution(int[] nums) {
array = nums;
original = nums.clone();
}
public int[] reset() {
array = original;
original = original.clone();
return array;
}
public int[] shuffle() {
List<Integer> aux = getArrayCopy();
for (int i = 0; i < array.length; i++) {
int removeIdx = rand.nextInt(aux.size());
array[i] = aux.get(removeIdx);
aux.remove(removeIdx);
}
return array;
}
//方法二
private int[] array;
private int[] original;
Random rand = new Random();
private int randRange(int min, int max) {
return rand.nextInt(max - min) + min;
}
private void swapAt(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public Solution(int[] nums) {
array = nums;
original = nums.clone();
}
public int[] reset() {
array = original;
original = original.clone();
return original;
}
public int[] shuffle() {
for (int i = 0; i < array.length; i++) {
swapAt(i, randRange(i, array.length));
}
return array;
}
分析
提交结果
给定一个用字符串表示的整数的嵌套列表,实现一个解析它的语法分析器。
列表中的每个元素只可能是整数或整数嵌套列表
提示:你可以假定这些字符串都是格式良好的:
解答
//递归函数通过字符数组和cur下标确定要处理的位置
char[] chars;
int cur = 0;
public NestedInteger deserialize(String s) {
chars = s.toCharArray();
//本身不是一个集合而是一个整数的情况
if(chars[0]!='[') return new NestedInteger(Integer.valueOf(s));
//调用递归函数返回根集合
return getNest();
}
public NestedInteger getNest(){
NestedInteger nest = new NestedInteger();
int num = 0;//num用于缓存用逗号分割的整数类型的值
int sign = 1;//当前记录的整数的符号,1代表正数,-1代表负数
while(cur!=chars.length-1){
cur ++;
if(chars[cur]==',') continue;
if(chars[cur]=='[') nest.add(getNest());//遇到[递归获取子集合
else if(chars[cur]==']') return nest;
else if(chars[cur]=='-') sign = -1;
else{//是数字的情况
num = 10*num + sign * (chars[cur]-'0');
//如果下一个字符是,或者]说明当前数字已经记录完了,需要加入集合中
if(chars[cur+1]==','||chars[cur+1]==']'){
nest.add(new NestedInteger(num));
num = 0;
sign = 1;
}
}
}
return null;
}
分析
给定一个整数 n, 返回从 1 到 n 的字典顺序。
例如,
给定 n =1 3,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] 。
请尽可能的优化算法的时间复杂度和空间复杂度。 输入的数据 n 小于等于 5,000,000。
解答
// 方法一
public List<Integer> lexicalOrder(int n) {
PriorityQueue<String> queue = new PriorityQueue<>(new Comparator<String>(){
public int compare(String o1,String o2){
return o1.compareTo(o2);
}
});
for(int i = 1;i<=n;i++){
queue.add(String.valueOf(i));
}
ArrayList<Integer> list = new ArrayList<>();
while(!queue.isEmpty()){
list.add(Integer.valueOf(queue.poll()));
}
return list;
}
// 方法二
List<Integer> list = new ArrayList<>();
public List<Integer> lexicalOrder(int n) {
dfs(0,0,n);
return list;
}
public void dfs(int start,int num,int n){
if(num > n) return;
if(num > 0)list.add(num);
for(int i = start > 0 ? 0 : 1;i <= 9;i++){
dfs(start + 1,10 * num + i,n);
}
}
分析
提交结果
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
解答
//方法一
public int firstUniqChar(String s) {
HashMap<Character, Integer> count = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
count.put(c, count.getOrDefault(c, 0) + 1);
}
for (int i = 0; i < s.length(); i++) {
if (count.get(s.charAt(i)) == 1)
return i;
}
return -1;
}
//方法二
public int firstUniqChar(String s) {
int[] count = new int[26];
int n = s.length();
for (int i = 0; i < n; i++) {
count[s.charAt(i) - 'a']++;
}
for (int i = 0; i < n; i++) {
if (count[s.charAt(i) - 'a'] == 1)
return i;
}
return -1;
}
分析
提交结果
假设我们以下述方式将我们的文件系统抽象成一个字符串:
字符串 “dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext” 表示:
目录 dir 包含一个空的子目录 subdir1 和一个包含一个文件 file.ext 的子目录 subdir2 。
字符串 “dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext” 表示:
目录 dir 包含两个子目录 subdir1 和 subdir2。 subdir1 包含一个文件 file1.ext 和一个空的二级子目录 subsubdir1。subdir2 包含一个二级子目录 subsubdir2 ,其中包含一个文件 file2.ext。
我们致力于寻找我们文件系统中文件的最长 (按字符的数量统计) 绝对路径。例如,在上述的第二个例子中,最长路径为 “dir/subdir2/subsubdir2/file2.ext”,其长度为 32 (不包含双引号)。
给定一个以上述格式表示文件系统的字符串,返回文件系统中文件的最长绝对路径的长度。 如果系统中没有文件,返回 0。
说明:
文件名至少存在一个 . 和一个扩展名。
目录或者子目录的名字不能包含 .。
要求时间复杂度为 O(n) ,其中 n 是输入字符串的大小。
请注意,如果存在路径 aaaaaaaaaaaaaaaaaaaaa/sth.png 的话,那么 a/aa/aaa/file1.txt 就不是一个最长的路径。
解答
public int lengthLongestPath(String input) {
if (input.length() == 0) {
return 0;
}
int res = 0;
int[] sum = new int[input.length() + 1];
for (String s : input.split("\n")) {
int level = s.lastIndexOf('\t') + 2;
int len = s.length() - (level - 1);
if (s.contains(".")) {
res = Math.max(res, sum[level - 1] + len);
} else {
sum[level] = sum[level - 1] + len + 1;
}
}
return res;
}
分析
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
解答
//方法一
public char findTheDifference(String s, String t) {
int[] counts = new int[26];
char[] schar = s.toCharArray();
char[] tchar = t.toCharArray();
for(int i = 0;i<schar.length;i++){
counts[schar[i]-'a']++;
}
for(int i = 0;i<tchar.length;i++){
counts[tchar[i] - 'a']--;
if(counts[tchar[i] - 'a'] < 0)return tchar[i];
}
return 'a';
}
//方法二
public char findTheDifference(String s, String t) {
char res = t.charAt(t.length()-1);
char[] schar = s.toCharArray();
char[] tchar = t.toCharArray();
for(int i = 0;i<schar.length;i++){
res ^= schar[i];
res ^= tchar[i];
}
return res;
}
分析
方法一,计数
方法二,异或
两个相同的字符异或为0
一个字符异或0等于本身。
所以异或可以找出唯一一个字符个数是奇数的字符。
提交结果
给定一个从1 到 n 排序的整数列表。
首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。
第二步,在剩下的数字中,从右到左,从倒数第一个数字开始,每隔一个数字进行删除,直到列表开头。
我们不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
返回长度为 n 的列表中,最后剩下的数字。
解答
public int lastRemaining(int n) {
return n == 1 ? 1 : 2 * (n/2 + 1 - lastRemaining(n/2));
}
分析
官方题解
假设n = 2k 第一次消除之后,剩下绿色的部分。
第二次是从后面往前消除,不妨从后往前编号 1 - k
第一次可以表示为f(2n) 表示2n个数中最后剩下的数字
第二次可以表示为f(n) 表示n个数中最后剩下的数字。
因为第二次的结果是蓝色的部分的数字,那么如何将第二次的结果映射回绿色的部分呢?
可以发现绿色和蓝色之间是有关系的。
f(2k) = 2(k + 1 - f(k))
上面分析的是偶数的情况。如果当n为奇数呢?
可以发现 在第一次删除的时候,就把最后一个奇数给删掉了。
所以f(2k + 1) = 2(k + 1 - f(k)) 式子和偶数的时候一样。
所以可以统一写为
f(n) = 2(n/2 +1 - f(n/2)) 这里除法向下取整
我们有 N 个与坐标轴对齐的矩形, 其中 N > 0, 判断它们是否能精确地覆盖一个矩形区域。
每个矩形用左下角的点和右上角的点的坐标来表示。例如, 一个单位正方形可以表示为 [1,1,2,2]。 ( 左下角的点的坐标为 (1, 1) 以及右上角的点的坐标为 (2, 2) )。
解答
public boolean isRectangleCover(int[][] rectangles) {
int left = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
int top = Integer.MIN_VALUE;
int bottom = Integer.MAX_VALUE;
int n = rectangles.length;
Set<String> set = new HashSet<>();
int sumArea = 0;
for (int i = 0; i < n; i++) {
//获取四个点的坐标
left = Math.min(left, rectangles[i][0]);
bottom = Math.min(bottom, rectangles[i][1]);
right = Math.max(right, rectangles[i][2]);
top = Math.max(top, rectangles[i][3]);
//计算总小矩形的面积
sumArea += (rectangles[i][3] - rectangles[i][1]) * (rectangles[i][2] - rectangles[i][0]);
//分别记录小矩形的坐标
String lt = rectangles[i][0] + " " + rectangles[i][3];
String lb = rectangles[i][0] + " " + rectangles[i][1];
String rt = rectangles[i][2] + " " + rectangles[i][3];
String rb = rectangles[i][2] + " " + rectangles[i][1];
//如果有就移除 没有就加入
if (!set.contains(lt)) set.add(lt);
else set.remove(lt);
if (!set.contains(lb)) set.add(lb);
else set.remove(lb);
if (!set.contains(rt)) set.add(rt);
else set.remove(rt);
if (!set.contains(rb)) set.add(rb);
else set.remove(rb);
}
//最后只剩4个大矩形坐标且面积相等即为完美矩形
if (set.size() == 4 && set.contains(left + " " + top) && set.contains(left + " " + bottom) && set.contains(right + " " + bottom) && set.contains(right + " " + top)) {
return sumArea == (right - left) * (top - bottom);
}
return false;
}
分析
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
解答
public boolean isSubsequence(String s, String t) {
int s_index = 0;
int t_index = 0;
while(s_index < s.length() && t_index < t.length()){
if(s.charAt(s_index) == t.charAt(t_index)){
s_index++;
t_index++;
}else t_index++;
}
return s_index == s.length();
}
分析
UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:
对于 1 字节的字符,字节的第一位设为0,后面7位为这个符号的unicode码。
对于 n 字节的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
这是 UTF-8 编码的工作方式:
给定一个表示数据的整数数组,返回它是否为有效的 utf-8 编码。
解答
public boolean validUtf8(int[] data) {
int numberOfBytesToProcess = 0;
int mask1 = 1 << 7;//用于检查最高位是否是1
int mask2 = 1 << 6;//用于检查第二高位是否是0
for(int i = 0; i < data.length; i++) {
if (numberOfBytesToProcess == 0) {
int mask = 1 << 7;
while ((mask & data[i]) != 0) {
numberOfBytesToProcess += 1;//记录高位开始有多少个1
mask = mask >> 1;
}
if (numberOfBytesToProcess == 0) {//如果高位为0 跳过
continue;
}
if (numberOfBytesToProcess > 4 || numberOfBytesToProcess == 1) {//1的个数大于1 或者等于1 不符合字符要求,返沪false
return false;
}
} else {
//判断后续的有多少个10 开头的二进制,若不是10开头 则返回false
if (!((data[i] & mask1) != 0 && (mask2 & data[i]) == 0)) {
return false;
}
}
numberOfBytesToProcess -= 1;//计数-1
}
return numberOfBytesToProcess == 0;
}
分析
提交结果
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
解答
public String decodeString(String s){
StringBuilder builder = new StringBuilder();//记录答案
StringBuilder temp = new StringBuilder();//记录递归解析的字符串
int depth = 0;//括号的深度
int num = 0;//数字
for(int i = 0;i < s.length();i++){
char ch = s.charAt(i);
if(ch >= '0' && ch <= '9'){
//如果depth为0.并且当前是数字,说明这个数字是用来计算这一层解析到的字符串 要在答案中出现的次数,计算这个num
if(depth == 0)
num = num * 10 + ch-'0';
//否则直接加入到准备递归解析的字符串序列中
else temp.append(ch);
}
else if(ch == '['){//遇到'[' 若depth > 0 说明当前的字符属于递归解析的字符串,则加入到temp中
if(depth > 0)
temp.append("[");
depth++; // depth++ 递归深度
}
else if(ch == ']'){//遇到']' 若dept > 1 说明当前的字符属于递归解析的字符串,则加入到temp中
if(depth > 1)
temp.append("]");
else if(depth == 1){//若等于 1 表示要递归的部分字符串已经找到。递归的解析temp
String str = decodeString(temp.toString());
if(num == 0)
num = 1;
for(int j = 0;j<num;j++){//根据数字。将解析后的结果重复的添加到答案中
builder.append(str);
}
num = 0;
temp = new StringBuilder();
}
depth--;
}
else if(depth > 0){//若大于0。加入到递归解析字符串中
temp.append(ch);
}
else if(depth == 0){//若等于0 加入到答案集合中。
builder.append(ch);
}
}
return builder.toString();
}
分析
找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。
解答
public int longestSubstring(String s, int k) {
int len = s.length();
if (len == 0 || k > len) return 0;
if (k < 2) return len;
return count(s.toCharArray(), k, 0, len - 1);
}
private static int count(char[] chars, int k, int p1, int p2) {
if (p2 - p1 + 1 < k) return 0;
int[] times = new int[26]; // 26个字母
// 统计出现频次
for (int i = p1; i <= p2; ++i) {
++times[chars[i] - 'a'];
}
// 去掉头部不符合的字符
while (p2 - p1 + 1 >= k && times[chars[p1] - 'a'] < k) {
++p1;
}
// 去掉尾部不符合的字符
while (p2 - p1 + 1 >= k && times[chars[p2] - 'a'] < k) {
--p2;
}
if (p2 - p1 + 1 < k) return 0;
for (int i = p1; i <= p2; ++i) {
// 如果第i个不符合要求,从i处分为前半部分和后半部分。
if (times[chars[i] - 'a'] < k) {
// 对于两部分去递归的寻找答案。返回两个结果的最大值
return Math.max(count(chars, k, p1, i - 1), count(chars, k, i + 1, p2));
}
}
return p2 - p1 + 1;
}
分析
给定一个长度为 n 的整数数组 A 。
假设 Bk 是数组 A 顺时针旋转 k 个位置后的数组,我们定义 A 的“旋转函数” F 为:
F(k) = 0 * Bk[0] + 1 * Bk[1] + … + (n-1) * Bk[n-1]。
计算F(0), F(1), …, F(n-1)中的最大值。
//方法一
public int maxRotateFunction(int[] A) {
int len = A.length;
if(len == 0)return 0;
int res = Integer.MIN_VALUE;
int num = 0;
for(int i = 0;i<len;i++){
int temp = 0;
for(int j = 0;j<len;j++){
temp += ((num + j) % len) * A[j];
}
num++;
res = Math.max(res,temp);
}
return res;
}
//方法二
public int maxRotateFunction(int[] A) {
int sum = 0;
int n = A.length;
int dp1 = 0, dp2 = 0;
for (int i = 0; i < n; i++) {
sum += A[i];
dp1 += i * A[i];
}
int ans = dp1;
for (int i = 1; i < A.length; i++) {
dp2 = dp1 + sum - n * A[n - i];
ans = Math.max(dp2, ans);
dp1 = dp2;
}
return ans;
}
分析
提交结果
给定一个正整数 n,你可以做如下操作:
解答
public int integerReplacement(int n) {
if (n == Integer.MAX_VALUE)
return 32;
if (n <= 3)
return n - 1;
if (n % 2 == 0)
return integerReplacement(n / 2) + 1;
else
return (n & 2) == 0 ? integerReplacement(n - 1) + 1 : integerReplacement(n + 1) + 1;
}
分析
把题目中的数字转换成2进制来看
例如 8 的二进制为 1000 , 15的二进制为 1111
当是偶数的时候,二进制直接右移动一位,记为1次。
当是偶数的时候,可以选择n - 1 或者n + 1. 记为1次。
对于15 而言 可以选择成为 10000 或者 1110
重点在于如何选择奇数变成偶数的策略
其实就是要使得这一次的转换,二进制当中1 出现的个数变少。
例如15 如果转换成10000 之后只需要一直除2 就好了。若转换成了1110 除2之后 得到111 还需要再进行奇数到偶数的转换。
所以当末尾两位是11的时候,就选择n+1的策略 若当末尾两位是01的时候,就选择n-1的策略。
也就是当n是奇数的时候 判断 n & 2 是否等于 0
3 是一种特殊情况。3 的二进制为 11 若根据上述的规则 应该采取n+1的策略,3->4->2>1 但是 3->2>1明显次数更少。所以另外的判断。
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
解答
//方法1
class Solution {
HashMap<Integer,ArrayList<Integer>> map = new HashMap<>();
public Solution(int[] nums) {
for(int i = 0;i < nums.length;i++){
ArrayList<Integer> list = map.getOrDefault(nums[i],new ArrayList<>());
list.add(i);
map.put(nums[i],list);
}
}
public int pick(int target) {
ArrayList<Integer> list = map.get(target);
Random random = new Random();
int index = random.nextInt(list.size());
return list.get(index);
}
}
//方法2
class Solution {
private int[] nums;
public Solution(int[] nums) {
this.nums = nums;
}
public int pick(int target) {
Random r = new Random();
int n = 0;
int index = 0;
for(int i = 0;i < nums.length;i++)
if(nums[i] == target){
//我们的目标对象中选取。
n++;
//我们以1/n的概率留下该数据
if(r.nextInt() % n == 0) index = i;
}
return index;
}
}
分析
提交结果
给出方程式 A / B = k, 其中 A 和 B 均为用字符串表示的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。
输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
解答
HashMap<String, List<Edge>> map = new HashMap<>();
double[] res;
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
init(equations,values);//初始化图
res = new double[queries.size()];
for(int i = 0;i < queries.size();i++){
List<String> points = queries.get(i);
String point1 = points.get(0);
String point2 = points.get(1);
ArrayList<String> visited = new ArrayList<>();
visited.add(point1);
res[i] = dfs(point1,point2,visited);
}
return res;
}
public void init(List<List<String>> equations, double[] values){
for(int i = 0;i < equations.size();i++){
List<String> points = equations.get(i);
double result = values[i];
String point1 = points.get(0);
String point2 = points.get(1);
List<Edge> edges = map.get(point1);
if(edges == null){
edges = new ArrayList<>();
map.put(point1,edges);
}
edges.add(new Edge(point1,point2,result));
edges = map.get(point2);
if(edges == null){
edges = new ArrayList<>();
map.put(point2,edges);
}
edges.add(new Edge(point2,point1,1/result));
}
}
private double dfs(String start, String dest, List<String> visited) {
// 验证是否存顶点
if (!map.containsKey(start) || !map.containsKey(dest)) {
return -1.0;
}
visited.add(start);
if (start.equals(dest)) {
return 1.0;
}
// 获取 start 顶点的边
List<Edge> startEdges = map.get(start);
if (startEdges == null || startEdges.isEmpty()) {
return -1.0;
}
// 深度优先遍历集合
for (Edge edge : startEdges) {
if (visited.contains(edge.v2)) {
continue;
}
double res = dfs(edge.v2, dest, visited);
if (res != -1.0) {
return res * edge.val;
}
}
return -1.0;
}
class Edge{
private String v1;
private String v2;
private double val;
public Edge(String v1,String v2,double val){
this.v1 = v1;
this.v2 = v2;
this.val = val;
}
}
分析
提交结果
在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …中找到第 n 个数字。
解答
public int findNthDigit(int n) {
long bit = 1, p = 9;
while(n - bit * p > 0){
n -= p * bit;
bit++;
p *= 10;
}
int num = (int)(Math.pow(10, bit - 1) + (n-1) / bit);//第n个数字 所在的数
int pos = (int)(n % bit);//第n个数所在的位置
pos = pos == 0 ? (int)bit : pos;//当 pos等于0的时候 则pos应该是最后一位
int t = (int) Math.pow(10, bit - pos);//需要去掉末尾多少位的数字
return num / t % 10;//得到该位数字
}
分析