2021-6-7笔记02
Posted 轻舟一曲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-6-7笔记02相关的知识,希望对你有一定的参考价值。
笔记02
_21_和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:
1 <= target <= 10^5
package LeetCode.笔记02;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class _21_和为s的连续正数序列 {
/*
借鉴和为s的两个数的方法:采用首尾双指针来求解,有序列至少有两个数,所以big最大为(1+target)/2
* */
public int[][] findContinuousSequence(int target) {
if (target<3) return null;
List<int[]> lists= new ArrayList<>();
int small=1,big=2;
int mid=(1+target)/2;
while (small<big&&small<mid){
//1~small-1 small~big
// int sum=big*(big+1)/2-(small-1)*small/2;
int sum=((small+big)*(big-small+1))>>1;//提交超时的原因是这里,两个乘法比一个乘法耗时间
if (sum>target) small++;
else if (sum<target) big++;//都是++
else {
int[] res=new int[big-small+1];
//int start=small; 少一个加法的时间
for (int i=small;i<=big;i++) res[i-small]=i;
lists.add(res);
small++;//继续寻找下一组
}
}
return lists.toArray(new int[lists.size()][]);
}
@Test
public void test(){
int[][] res= findContinuousSequence(108160);
for (int[] re : res) {
for (int i : re) {
System.out.print(i+" ");
}
System.out.println();
}
}
}
_22_翻转句子中单词的顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
示例 1:
输入: “the sky is blue”
输出: “blue is sky the”
示例 2:
输入: " hello world! "
输出: “world! hello”
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
说明:
无空格字符构成一个单词。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
package LeetCode.笔记02;
import java.util.Arrays;
public class _22_翻转句子中单词的顺序 {
/*
官方解法:先旋转句子,再旋转单词
左旋字符串:先旋转单词,再旋转句子,都是读句子的顺序罢了
Java解法:直接用不加锁的stringbuild从后往前添加单词,左旋就是从前往后添加单词
* */
//从后往前,双指针定位,直接扫描一次
public String reverseWords01(String s) {
s=s.trim();//删除首尾空格
int i=s.length()-1;
int j=i;
StringBuffer res = new StringBuffer();
while (i>=0){
while (i>=0&&s.charAt(i)!=' ') i--;//首个空格
//[,)
res.append(s.substring(i + 1, j + 1)).append(" ");//添加单词
while (i>=0&&s.charAt(i)==' ') i--;//若前面还有空格,跳过单词间空格
j=i;
}
return res.toString().trim();//去掉首尾空格
}
}
_23_左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = “abcdefg”, k = 2
输出: “cdefgab”
示例 2:
输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”
限制:
1 <= k < s.length <= 10000
package LeetCode.笔记02;
public class _23_左旋转字符串 {
public String reverseLeftWords(String s, int n) {
if (s==null) return null;
if (s.length()<n) return null;
StringBuilder res = new StringBuilder();
s=s.trim();
//n=2 i=1 [0,2) [2,)
res.append(s.substring(n,s.length())).append(s.substring(0,n));
return res.toString().trim();
}
}
_24_滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
package LeetCode.笔记02;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Arrays;
public class _24_滑动窗口的最大值 {
/*
O(N)算法:单调队列 两端开口队列队列
首部保留最大值,队列保存的下标,当首部元素下标与当前元素下标相差k即移出
* */
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums==null) return null;
int len=nums.length;
if (len<k) return null;
if (len==0&&k==0) return new int[]{};//特殊
if (k==1) return nums;
ArrayDeque<Integer> deque = new ArrayDeque<>();
int[] res=new int[len-k+1];
deque.addFirst(0);
int i=1;
//先处理第一个窗口
while (i<k){
//和队首元素比较,看能不能直接替代
if (nums[i]>=nums[deque.getFirst()]){
//说明之前的都不可能是最大值,,应该清空,因为有他们的窗口必有它
deque.clear();
deque.addFirst(i++);
}
//和队尾元素比较
else {
//说明前面这个队尾不可能是最大值,应该去除
while (nums[i]>=nums[deque.getLast()]) deque.removeLast();
deque.addLast(i++);
}
}
int j=0;
res[j]=nums[deque.getFirst()];
j++;
//窗口移动i++ (不是队列移动)
while (i<len){
//入队前处理
//1.判断队首元素还该不该存在
if (i-deque.getFirst()>=k) deque.removeFirst();
//2.
if (nums[i]>=nums[deque.getFirst()]){//当k=1时会导致空
deque.clear();
deque.addFirst(i++);
}
else {
while (nums[i]>=nums[deque.getLast()]) deque.removeLast();
deque.addLast(i++);
}
res[j]=nums[deque.getFirst()];
j++;
}
return res;
}
@Test
public void test(){
int[] nums={1,-1};
int[] ints = maxSlidingWindow(nums, 1);
System.out.println(Arrays.toString(ints));
}
}
_25_队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
[“MaxQueue”,“pop_front”,“max_value”]
[[],[],[]]
输出: [null,-1,-1]
限制:
1 <= push_back,pop_front,max_value的总操作数 <= 10000
1 <= value <= 10^5
package LeetCode.笔记02;
import java.util.ArrayDeque;
public class _25_队列的最大值 {
//队列本身就有先进先出的特性,这点是不能改变的,所以需要借助上题滑动窗口增加一个队列保存最大值
public ArrayDeque<Integer> deque=null;
public ArrayDeque<Integer> dequeMax=null;
public _25_队列的最大值() {
deque= new ArrayDeque<>();
dequeMax= new ArrayDeque<>();
}
public int max_value() {
if (deque.size()==0) return -1;
return dequeMax.getFirst();
}
//滑动窗口,单调队列思想
public void push_back(int value) {
deque.addLast(value);
if (dequeMax.isEmpty()) dequeMax.addFirst(value);
else {
if (value>=dequeMax.getFirst()){
dequeMax.clear();
dequeMax.addFirst(value);
}
else {
while (value>=dequeMax.getLast()) dequeMax.removeLast();
dequeMax.addLast(value);
}
}
}
public int pop_front() {
if (deque.isEmpty()) return -1;
int value = deque.removeFirst();
if (value==dequeMax.getFirst()) dequeMax.removeFirst();
return value;
}
}
_26_n个骰子的点数概率
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
示例 2:
输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
限制:
1 <= n <= 11
package LeetCode.笔记02;
import org.junit.Test;
import java.util.Arrays;
public class _26_n个骰子的点数概率 {
//可以将6提取出来,这样当厂商改变点数的时候可以拓展,但是面试写代码的时候为了简洁易懂就直接写6了
/*
基于循环实现:添加一个筛子,则点数和n是上一个次点数和n-1,n-2,n-3,n-4,n-5,n-6的和
所以需要用两个数组循环实现
* */
public double[] dicesProbability(int n){
if (n<1) return null;
int[][] point=new int[2][6*n+1];
int flag=0;
//扔第一个筛子初始化数组
for (int i=1;i<=6;i++) point[flag][i]=1;
//循环
for (int k=2;k<=n;k++){
flag=1-flag;
//全部归零
Arrays.fill(point[flag],0);
for (int i=k;i<=k*6;i++){
for (int p=1;p<=6;p++){
if (i-p>=(k-1)) point[flag][i]+=point[1-flag][i-p];
}
}
}
double[] res = new double[6 * n - n + 1];
double all=Math.pow(6,n);
for (int i = 0; i < 6 * n - n + 1; i++) {
res[i]=(double) point[flag][i+n]/all;
}
return res;
}
@Test
public void test(){
double[] doubles = dicesProbability(3);
System.out.println(Arrays.toString(doubles));
}
//点数和:n~6n 用一个6n-n+1的数组存
public double[] dicesProbability02(int n) {
point=new int[6*n-n+1];
N=n;
int[] sum=new int[1];
pointSum(1);
double[] res = new double[6 * n - n + 1];
double all=Math.pow(6,n);
for (int i = 0; i < 6 * n - n + 1; i++) {
res[i]=(double) point[i]/all;
}
return res;
}
public int N=0;
public int Sum=0;//设为全局变量,提交通过,设为参数超时
public int[] point=null;
public void pointSum(int n){//1-n的骰子的点数和
if (n==N+1){
point[Sum-N]++;
return;
}
for (int i=1;i<=62021-6-7剑指笔记03