leetcode刷题 633~
题目633
平方数之和
给定一个非负整数 c
,你要判断是否存在两个整数 a
和 b
,使得 a2 + b2 = c
。
思路实现
class Solution { public boolean judgeSquareSum(int c) { int left = 0; int right = (int) Math.floor(Math.sqrt(c)); while(left <= right){ int sum = left*left + right*right; if(sum > c){ right -= 1; }else if(sum < c){ left += 1; }else{ return true; } } return false; } }
题目636
函数的独占时间
给出一个非抢占单线程CPU的 n 个函数运行日志,找到函数的独占时间。
每个函数都有一个唯一的 Id,从 0 到 n-1,函数可能会递归调用或者被其他函数调用。
日志是具有以下格式的字符串:function_id:start_or_end:timestamp。例如:"0:start:0" 表示函数 0 从 0 时刻开始运行。"0:end:0" 表示函数 0 在 0 时刻结束。
函数的独占时间定义是在该方法中花费的时间,调用其他函数花费的时间不算该函数的独占时间。你需要根据函数的 Id 有序地返回每个函数的独占时间。
示例 1:
输入:
n = 2
logs =
["0:start:0",
"1:start:2",
"1:end:5",
"0:end:6"]
输出:[3, 4]
说明:
函数 0 在时刻 0 开始,在执行了 2个时间单位结束于时刻 1。
现在函数 0 调用函数 1,函数 1 在时刻 2 开始,执行 4 个时间单位后结束于时刻 5。
函数 0 再次在时刻 6 开始执行,并在时刻 6 结束运行,从而执行了 1 个时间单位。
所以函数 0 总共的执行了 2 +1 =3 个时间单位,函数 1 总共执行了 4 个时间单位。
思路实现
class Solution { public int[] exclusiveTime(int n, List<String> logs) { int[] res = new int[n]; Stack < Integer > stack = new Stack < > (); String[] temp = logs.get(0).split(":"); int lastTime = Integer.parseInt(temp[2]), index = 1; stack.push(Integer.parseInt(temp[0])); while(index < logs.size()){ temp = logs.get(index).split(":"); if(temp[1].equals("start")){ if (!stack.isEmpty()) res[stack.peek()] += Integer.parseInt(temp[2]) - lastTime; lastTime = Integer.parseInt(temp[2]); stack.push(Integer.parseInt(temp[0])); }else{ res[stack.pop()] += Integer.parseInt(temp[2]) - lastTime +1; lastTime = Integer.parseInt(temp[2]) + 1; } index ++; } return res; } }
题目637
二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
思路实现
class Solution { public List<Double> averageOfLevels(TreeNode root) { Queue<TreeNode> curLevel = new LinkedList<>(); curLevel.offer(root); List<Double> res = new ArrayList<>(); while(!curLevel.isEmpty()){ double sum = 0; int len = curLevel.size(); for (int index =0; index < len; index++){ TreeNode node = curLevel.poll(); sum += node.val; if(node.left != null) curLevel.offer(node.left); if(node.right != null) curLevel.offer(node.right); } res.add(sum/len); } return res; } }
题目638
大礼包
在LeetCode商店中, 有许多在售的物品。
然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。
现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费。
每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。
任意大礼包可无限次购买。
示例 1:
输入: [2,5], [[3,0,5],[1,2,10]], [3,2]
输出: 14
解释:
有A和B两种物品,价格分别为¥2和¥5。
大礼包1,你可以以¥5的价格购买3A和0B。
大礼包2, 你可以以¥10的价格购买1A和2B。
你需要购买3个A和2个B, 所以你付了¥10购买了1A和2B(大礼包2),以及¥4购买2A。
思路
深度优先遍历
实现
class Solution { private int res; public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) { for (int i = 0; i < needs.size(); i++) { res += price.get(i) * needs.get(i); } shopping(price, special, needs, 0, res); return res; } private void shopping(List<Integer> price, List<List<Integer>> special, List<Integer> needs , int start, int money){ if (start >= special.size()) { return; } List<Integer> cur = special.get(start); List<Integer> newNeeds = new ArrayList<>(); boolean can = true; int tmp = 0; for(int i=0; i < cur.size()-1; i++){ int use = cur.get(i); if(needs.get(i) < use){ can = false; break; } newNeeds.add(needs.get(i) - use); tmp += price.get(i) * use; } if(can){ int newMoney = money - tmp + cur.get(cur.size() - 1); res = Math.min(res, newMoney); shopping(price, special, newNeeds, start, newMoney); } shopping(price, special, needs, start+1, money); } }
题目640
求解方程
求解一个给定的方程,将x以字符串"x=#value"的形式返回。该方程仅包含'+',' - '操作,变量 x 和其对应系数。
如果方程没有解,请返回“No solution”。
如果方程有无限解,则返回“Infinite solutions”。
如果方程中只有一个解,要保证返回值 x 是一个整数。
思路
1.将等式以"="为界限分为左右两边
2.计算两边的x系数和常数
3.若系数与常数都相等,则无穷解
若系数相等,常数不等,无解
其他则是整数解
实现
class Solution { public String solveEquation(String equation) { String[] eq = equation.split("="); int[] left = solveSide(eq[0]); int[] right = solveSide(eq[1]); String res; if(left[0]==right[0] && left[1]==right[1]) res = "Infinite solutions"; else if(left[0]==right[0]) res = "No solution"; else{ int l = -(right[1]- left[1])/(right[0]-left[0]); res = "x=" + l; } return res; } private int[] solveSide(String side){ Stack <Character> stack = new Stack <> (); int[] res = new int[]{0,0}; if(side.charAt(0) != '+' && side.charAt(0) != '-'){ stack.push('+'); } for (int i = 0; i < side.length(); i++) { char ch = side.charAt(i); stack.push(ch); } while(!stack.isEmpty()){ char cur = stack.pop(); if(cur == 'x'){ char tmp = stack.pop(); int num = 1; String op = ""; while(!stack.isEmpty() && tmp != '+' && tmp != '-'){ op = tmp + op; tmp = stack.pop(); } if(op.length() != 0) num = Integer.parseInt(op); if(tmp == '+') res[0] += num; else res[0] -= num; }else{ char tmp = cur; int num = 1; String op = ""; while(!stack.isEmpty() && tmp != '+' && tmp != '-'){ op = tmp + op; tmp = stack.pop(); } num = Integer.parseInt(op); if(tmp =='+') res[1] += num; else res[1] -= num; } } return res; } }
题目641
设计循环双端队列
设计实现双端队列。
你的实现需要支持以下操作:
MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。
思路实现
class MyCircularDeque { private LinkedList<Integer> queue; private int maxLen; private int curLen; /** Initialize your data structure here. Set the size of the deque to be k. */ public MyCircularDeque(int k) { this.queue = new LinkedList<Integer>(); this.maxLen = k; this.curLen = 0; } /** Adds an item at the front of Deque. Return true if the operation is successful. */ public boolean insertFront(int value) { if(isFull()) return false; this.queue.offerFirst(value); this.curLen += 1; return true; } /** Adds an item at the rear of Deque. Return true if the operation is successful. */ public boolean insertLast(int value) { if(isFull()) return false; this.queue.offer(value); this.curLen += 1; return true; } /** Deletes an item from the front of Deque. Return true if the operation is successful. */ public boolean deleteFront() { if(isEmpty()) return false; this.queue.removeFirst(); this.curLen -= 1; return true; } /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ public boolean deleteLast() { if(isEmpty()) return false; this.queue.removeLast(); this.curLen -= 1; return true; } /** Get the front item from the deque. */ public int getFront() { if(isEmpty()) return -1; return this.queue.get(0); } /** Get the last item from the deque. */ public int getRear() { if(isEmpty()) return -1; return this.queue.get(curLen-1); } /** Checks whether the circular deque is empty or not. */ public boolean isEmpty() { if(curLen == 0) return true; return false; } /** Checks whether the circular deque is full or not. */ public boolean isFull() { if(curLen == maxLen) return true; return false; } }
题目643
子数组的最大平均数I
思路
滑动窗口
实现
class Solution { public double findMaxAverage(int[] nums, int k) { int sum = 0; for(int i=0; i <k; i++){ sum += nums[i]; } double res = sum; for(int i=k; i< nums.length; i++){ sum += nums[i] - nums[i-k]; res = Math.max(sum,res); } return (res/k); } }
题目645
错误的集合
集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组 nums 代表了集合 S 发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
思路实现
class Solution { public int[] findErrorNums(int[] nums) { int[] res = new int[2]; for(int i=0; i < nums.length; i++){ int temp = Math.abs(nums[i]) -1; if(nums[temp] < 0) res[0] = temp + 1; else nums[temp] = -nums[temp]; } for(int i=0;i < nums.length; i++){ if(nums[i]>0){ res[1] = i+1; break; } } return res; } }
题目646
最长对数链
给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。
现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。
给定一个数对集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
思路
动态规划
在一个长度为 k
,以 pairs[i]
结尾的数对链中,如果 pairs[i][1] < pairs[j][0]
,则将该数对加入链中,数对链长度变为 k+1
。
实现
class Solution { public int findLongestChain(int[][] pairs) { Arrays.sort(pairs, (pair1, pair2) -> (pair1[0] - pair2[0])); int n = pairs.length; int[] dp = new int[n]; int res = 0; Arrays.fill(dp, 1); for(int i=1; i <n; i++){ for(int j=0; j <i; j++){ if(pairs[i][0] > pairs[j][1]) dp[i] = Math.max(dp[i], dp[j]+1); } res = Math.max(dp[i], res); } return res; } }
题目647
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
思路
动态规划,dp[i][j] 表示s[i:j]是否为回文字符串
实现
class Solution { public int countSubstrings(String s) { int res = 0; int n = s.length(); boolean dp[] = new boolean[n]; for(int j=0 ; j < n; j++){ for(int i=0; i <= j; i++){ if(i==j){ dp[i] = true; res += 1; }else if(j-i == 1 && s.charAt(i) == s.charAt(j)){ dp[i] = true; res += 1; }else if(j-i>1 && s.charAt(i) == s.charAt(j) && dp[i+1]){ dp[i] = true; res += 1; }else{ dp[i] = false; } } } return res; } }
题目648
单词替换
在英语中,我们有一个叫做 词根(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。
现在,给定一个由许多词根组成的词典和一个句子。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
你需要输出替换之后的句子。
思路
字典树:对所有词根生成一颗字典树,所有单词在在字典树中寻找最短词根。
实现
class Solution { class TrieNode{ TrieNode[] child;//记录孩子节点 boolean is_end;//记录当前节点是不是一个单词的结束字母 public TrieNode(){// child = new TrieNode[26];//子节点数组长度26,0:‘a’,1:‘b’..... is_end = false; } } class Trie { TrieNode root;//记录前缀树的根 public Trie() { root = new TrieNode(); } private void insert(String word) { TrieNode ptr = root; for(int i = 0;i < word.length();i++){ char c = word.charAt(i);//对于每个单词 if(ptr.child[c - 'a'] == null){//如果c - 'a'为空,说明还没有存入 ptr.child[c - 'a'] = new TrieNode();//存入节点 } ptr = ptr.child[c - 'a'];//指向当前节点 } ptr.is_end = true; } private int search(String word){ TrieNode ptr = root;//从根出发 for(int i = 0;i < word.length();i++){ char c = word.charAt(i);//对于每个字母 if(ptr.child[c - 'a'] == null){ break; } ptr = ptr.child[c - 'a']; if(ptr.is_end) return i; } return -1; } } public String replaceWords(List<String> dictionary, String sentence) { String[] sen = sentence.split(" "); Trie root = new Trie(); for(String dic: dictionary){ root.insert(dic); } for(int i =0; i< sen.length; i++){ String word = sen[i]; int index = root.search(word); if(index >= 0){ sen[i] = word.substring(0,index+1); } } StringBuffer res = new StringBuffer(); for(int i = 0; i < sen.length; i++){ res. append(sen[i]); if(i != sen.length-1) res. append(" "); } return res.toString(); } }
题目649
Dota2参议院
Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)
Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:
禁止一名参议员的权利:
参议员可以让另一位参议员在这一轮和随后的几轮中丧失所有的权利。
宣布胜利:
如果参议员发现有权利投票的参议员都是同一个阵营的,他可以宣布胜利并决定在游戏中的有关变化。
给定一个字符串代表每个参议员的阵营。字母 “R” 和 “D” 分别代表了 Radiant(天辉)和 Dire(夜魇)。然后,如果有 n 个参议员,给定字符串的大小将是 n。
以轮为基础的过程从给定顺序的第一个参议员开始到最后一个参议员结束。这一过程将持续到投票结束。所有失去权利的参议员将在过程中被跳过。
假设每一位参议员都足够聪明,会为自己的政党做出最好的策略,你需要预测哪一方最终会宣布胜利并在 Dota2 游戏中决定改变。输出应该是 Radiant 或 Dire。
思路
贪心算法:
每个R阵营的参议员禁止下一个离他最近的D阵营的参议员,反之亦然。
实现
class Solution { public String predictPartyVictory(String senate) { Queue<Integer> radiant = new LinkedList<>(); Queue<Integer> dire = new LinkedList<>(); char c; int n = senate.length(); for (int i = 0; i < n; i++) { c = senate.charAt(i); if (c == 'R') { radiant.offer(i); } else { dire.offer(i); } } while(!radiant.isEmpty() && !dire.isEmpty()){ int r = radiant.poll(); int d = dire.poll(); if(r < d){ radiant.offer(r + n); }else{ dire.offer(d + n); } } return radiant.isEmpty() ? "Dire" : "Radiant"; } }