Q17.11 单词距离
有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离(相隔单词数)。如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗?
示例:
输入:words = ["I","am","a","student","from","a","university","in","a","city"], word1 = "a", word2 = "student"
输出:1
提示:words.length <= 100000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-closest-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
import java.lang.*;
class Solution {
public int findClosest(String[] words, String word1, String word2) {
int start = -10000;
int end = 10000;
int min = 10000;
for (int i = 0; i < words.length; i++) {
if (words[i].equals(word1)) {
start = i;
}
if (words[i].equals(word2)) {
end = i;
}
min = Math.min(min, Math.abs(end-start));
}
return min;
}
}
Q17.12 BiNode
二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。
注意:本题相对原题稍作改动
示例:
输入: [4,2,5,1,3,null,6,0]
输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]
提示:节点数量不会超过 100000。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binode-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
简单的递归,借用一个先驱节点当做链表一样
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode f;
public TreeNode convertBiNode(TreeNode root) {
f = new TreeNode(Integer.MIN_VALUE);
TreeNode pre = f;
dfs(root);
return pre.right;
}
public void dfs(TreeNode root) {
if (root == null) return;
dfs(root.left);
f.right = root;
root.left = null;
f = f.right;
dfs(root.right);
}
}
Q17.13 恢复空格
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
注意:本题相对原题稍作改动,只需返回未识别的字符数
示例:
输入:
dictionary = ["looked","just","like","her","brother"]
sentence = "jesslookedjustliketimherbrother"
输出: 7
解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。
提示:0 <= len(sentence) <= 1000
dictionary中总字符数不超过 150000。
你可以认为dictionary和sentence中只包含小写字母。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/re-space-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:直接求最少匹配的字符数比较复杂,所以反过来我们求能匹配的最大字符数。这道题相当于一个 完全0-1背包问题,背包容量为字符串长度,物品(字典中的单词)数量没有限制。我们要做的就是尽可能的填满这个背包。
1.定义状态: dp[i][j] 表示字符串(0~i),字典(0~i)的状态下,匹配的最大字符数。
2.状态转移:从背包末尾开始考虑:
当单词能塞进背包末尾的时候,即 sentence.substring(i-len,i) 与 当前单词相等,len 为单词长度。此时单词有两种状态,塞进背包,和不塞进背包,记住单词可以重复取用,取这两种状态的最大值:dp[i][j] = max(dp[i-len][j]+len,dp[i][j-1])。
当单词不能塞进背包末尾的时候,取字符串(0~i-1)与当前单词的组合dp[i-1][j],和当前字符串与前一个单词组合dp[i][j-1]这两种组合的最大值。即:dp[i][j] = max(dp[i-1][j],dp[i][j-1]).
3.返回值:sentence.length() - dp[m][n];
4.状态压缩:二维dp数组可以压缩为一维数组。
5.如果有帮到您,请不吝啬点赞~
class Solution {
public int respace(String[] dictionary, String sentence) {
int m = sentence.length();
int[] dp = new int[m+1];
for(int i=1;i<=m;i++){ //外层循环字符串
for(String word:dictionary){ //内层循环字典
int len = word.length();
if(i >= len && word.equals(sentence.substring(i-len,i))){
dp[i] = Math.max(dp[i],dp[i-len]+len); //状态转移
}else{
dp[i] = Math.max(dp[i],dp[i-1]);
}
}
}
return m-dp[m];
}
}
Q17.14 最小K个数
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
提示:0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-k-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
快速排序递归版
class Solution {
public int[] smallestK(int[] arr, int k) {
select(arr, 0, arr.length - 1, k);
int[] ans = new int[k];
System.arraycopy(arr, 0, ans, 0, k);
return ans;
}
public void select(int[] arr, int p, int q, int k) {
if (p >= q) return;
int l = p, r = q;
int m = arr[p];
while (l < r) {
while (l < r && arr[r] >= m) {
r--;
}
arr[l] = arr[r];
while (l < r && arr[l] <= m) {
l++;
}
arr[r] = arr[l];
}
arr[l] = m;
int t = l - p + 1;
if (t == k) return;
if (t < k) {
select(arr, l + 1, q, k - t);
} else {
select(arr, p, l - 1, k);
}
}
}
Q17.15 最长单词
给定一组单词words,编写一个程序,找出其中的最长单词,且该单词由这组单词中的其他单词组合而成。若有多个长度相同的结果,返回其中字典序最小的一项,若没有符合要求的单词则返回空字符串。
示例:
输入: ["cat","banana","dog","nana","walk","walker","dogwalker"]
输出: "dogwalker"
解释: "dogwalker"可由"dog"和"walker"组成。
提示:0 <= len(words) <= 200
1 <= len(words[i]) <= 100来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-word-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路简单:
按照长度降序排序,如果长度相等就按照字典序排序。这样的话如果每个单词都是组合单词,那么最前面的单词就是题目要求的返回单词。
遍历排序后的单词数组,如果是复合单词就返回
复合单词检测思路: 目标单词的前i个字母是单词列表里面的单词 && 剩下的字母是组合单词
class Solution {
// 检测word是否是由set里面的单词组成
private boolean isComposeWord(Set<String> set, String word) {
if (word.length() == 0) {
return true;
}
for (int i = 0; i < word.length(); i++) {
if (set.contains(word.substring(0, i + 1)) && isComposeWord(set, word.substring(i + 1))) {
return true;
}
}
return false;
}
public String longestWord(String[] words) {
// 将单词列表按照长度降序,长度相同按照字典序
Arrays.sort(words, (o1, o2) -> {
if (o1.length() == o2.length()) {
return o1.compareTo(o2);
} else {
return Integer.compare(o2.length(), o1.length());
}
});
// 把数组做成set,提高检测的效率
Set<String> set = new HashSet<>(Arrays.asList(words));
// 遍历检测当前单词是不是组合单词,是的话直接返回
for (String word :words ) {
set.remove(word);
if (isComposeWord(set, word)) {
return word;
}
set.add(word);
}
return "";
}
}
Q17.16 按摩师
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
注意:本题相对原题稍作改动
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:输入: [2,7,9,3,1]
输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-masseuse-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
递推方程:dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
class Solution {
public int massage(int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
if (n == 1) {
return nums[0];
}
int[] dp = new int[n];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[n - 1];
}
}
Q17.17 多次搜索
给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]为smalls[i]出现的所有位置。
示例:
输入:
big = "mississippi"
smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]
提示:0 <= len(big) <= 1000
0 <= len(smalls[i]) <= 1000
smalls的总字符数不会超过 100000。
你可以认为smalls中没有重复字符串。
所有出现的字符均为英文小写字母。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/multi-search-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public int[][] multiSearch(String big, String[] smalls) {
if (smalls == null || big == null || smalls.length == 0) return new int[0][];
List<List<Integer>> tempRes = new LinkedList<>();
for (String small : smalls) {
List<Integer> tmp = new LinkedList<>();
if (small.length() == 0) {
tempRes.add(tmp);
continue;
}
int index = 0;
while (index < big.length() && big.indexOf(small, index) != -1) {
int pos = big.indexOf(small, index);
tmp.add(pos);
index = pos + 1;
}
tempRes.add(tmp);
}
int[][] res = new int[tempRes.size()][];
for (int i = 0; i < res.length; i++) {
int[] ints = new int[tempRes.get(i).size()];
for (int i1 = 0; i1 < ints.length; i1++) {
ints[i1] = tempRes.get(i).get(i1);
}
res[i] = ints;
}
return res;
}
Q17.18 最短超串
假设你有两个数组,一个长一个短,短的元素均不相同。找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。
返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。
示例 1:
输入:
big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7]
small = [1,5,9]
输出: [7,10]
示例 2:输入:
big = [1,2,3]
small = [4]
输出: []
提示:big.length <= 100000
1 <= small.length <= 100000来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-supersequence-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
滑动窗口
class Solution {
public int[] shortestSeq(int[] big, int[] small) {
Map<Integer,Integer> window = new HashMap<>(); //window记录窗口内元素的情况
Set<Integer> set = new HashSet<>(); //set作为查找表
for(int e : small)
set.add(e);
int[] res = {big.length,big.length};
int minLen = big.length + 1;
for(int l=0,r=0;r<big.length;r++) {
if(set.contains(big[r])) //考虑big[r]要不要进窗口【在set中的值才允许进入】
window.put(big[r],window.getOrDefault(big[r],0)+1);
while(window.size()==set.size()) { //当窗口符合要求,左边界收缩,注意要不要更新window
if(set.contains(big[l])) { //此时,需要更新window
int n = window.get(big[l]);
if(n==1) {
window.remove(big[l]); //。。。
if(r-l+1<minLen) { //看要不要更新答案
res[0] = l;
res[1] = r;
minLen = r-l+1;
}
}else {
window.put(big[l],n-1);
}
}
l++;
}
}
return res[0]==big.length?new int[0]:res;
}
}
Q17.19 消失的两个数字
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入: [1]
输出: [2,3]
示例 2:输入: [2,3]
输出: [1,4]
提示:nums.length <= 30000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/missing-two-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
boolean length1 = false;
public int[] missingTwo(int[] nums) {
int[] result = new int[2];
for(int i = 0 ; i < nums.length ; i++){
if(nums[i] != i + 1){
if(nums[i] < nums.length + 1){
int temp = nums[i];
nums[i] = nums[temp - 1];
nums[temp - 1] = temp;
i--;
}else{
if(nums[i] == nums.length + 1){
length1 = true;
}
}
}
}
for(int i = 0 ; i < nums.length ; i++){
if(nums[i] != i + 1){
if(result[0] == 0){
result[0] = i + 1;
}else{
result[1] = i + 1;
return result;
}
}
}
if(result[0] == 0){
result[0] = nums.length + 1;
result[1] = nums.length + 2;
}else{
if(length1){
result[1] = nums.length + 2;
}else{
result[1] = nums.length + 1;
}
}
return result;
}
}
Q17.20 消失的两个数字
随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/continuous-median-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
利用一个大顶堆一个小顶堆,中位数可以看作是隔开两个数组的分位线,这样左端用大顶堆,右端用小顶堆,最后两个堆(奇数个是一个)的peek求平均即可。
class MedianFinder {
/** initialize your data structure here. */
PriorityQueue<Integer> left, right;
boolean isLeft;
public MedianFinder() {
left = new PriorityQueue<>((x, y) -> y - x);
right = new PriorityQueue<>();
}
public void addNum(int num) {
left.offer(num);
right.offer(left.poll());
if (left.size() < right.size())
left.offer(right.poll());
}
public double findMedian() {
if (left.size() > right.size())
return left.peek();
return (left.peek() + right.peek()) / 2.0;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/