笔记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<=6;i++){
Sum+=i;
pointSum(n+1);
//回溯
Sum-=i;
}
}
}
_27_扑克牌中的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5]
输出: True
示例 2:
输入: [0,0,1,2,5]
输出: True
限制:
数组长度为 5
数组的数取值为 [0, 13] .
package LeetCode.笔记02;
import java.util.Arrays;
public class _27_扑克牌中的顺子 {
/*
这几道题考查的都是对实际问题的数学建模能力:
0 2 4 5 6
1.排序
2.统计0的个数
3.统计空缺的个数(相邻两个数,如果相等的则直接false) 4-2-1=1
4.0的个数大于等于空缺的个数
* */
public boolean isStraight(int[] nums) {
if (nums==null) return false;
if (nums.length<5) return false;
Arrays.sort(nums);
int count0=0;
int i=0;
while (i<5){
if (nums[i]==0){
count0++;
i++;
}
else break;
}
int vacant=0;
while (i<4){
if (nums[i]==nums[i+1]) return false;
vacant+=nums[i+1]-nums[i]-1;
i++;
}
return count0>=vacant;
}
}
_28_圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出: 3
示例 2:
输入: n = 10, m = 17
输出: 2
限制:
1 <= n <= 10^5
1 <= m <= 10^6
package LeetCode.笔记02;
import java.util.ArrayList;
public class _28_圆圈中最后剩下的数字 {
/*
约瑟夫问题:两种解决方案
1.环形链表 O(n)
2.找规律 找映射
* */
public int lastRemaining02(int n, int m) {
if (n==0) return -1;
//LinkedList<Integer> list = new LinkedList<>();
ArrayList<Integer> list = new ArrayList<>();
for (int i=0;i<n;i++) list.add(i);
int p=0;
while (n>1){
p=(p+m-1)%n;//循环的正确表示方式
//查找时间用数组会更好
list.remove(p);
n--;
}
return list.get(0);
}
public int lastRemaining(int n, int m){
int ans=0;
//最后一轮只剩下2个人,所以从2开始反推
for (int i=2;i<=n;i++) ans=(ans+m)%i;
return ans;
}
}
_29_股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
package LeetCode.笔记02;
import org.junit.Test;
public class _29_股票的最大利润 {
/*
O(N):一次扫描,用d(i)保存第i个数字卖出,需要一个临时变量记录前面的最小值,还有一个最大值
* */
public int maxProfit(int[] prices) {
if (prices==null) return -1;
if (prices.length==0) return 0;
int min=prices[0];
int max=Integer.MIN_VALUE;
for (int i=1;i<prices.length;i++){
if (prices[i]-min>max) max=prices[i]-min;
if (prices[i]<min) min=prices[i];
}
return Math.max(max, 0);//利润有可能为负
}
@Test
public void test(){
int[] prices={7,6,4,3,1};
int i = maxProfit(prices);
System.out.println(i);
}
}
_30_不用乘除循环求1到n的和
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3
输出: 6
示例 2:
输入: n = 9
输出: 45
限制:
1 <= n <= 10000
通过次数113,271提交次
package LeetCode.笔记02;
import org.junit.Test;
public class _30_不用乘除循环求1到n的和 {
/*
return (int) (Math.pow(n,2)+n)>>1;
要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)
1.利用构造函数(C++)
2.递归 O(N)
3.利用二进制位运算:俄罗斯农民乘法 O(logN)
* */
public int sumNums01(int n) {
//return n==0?0:n+sumNums(n-1);
//可用&&和||来替换 A&&B A为false B不执行, A||B A为true B不执行
boolean flag=n>0&&(n+=sumNums01(n-1))>0;
return n;
}
/*
(n+1)n>>1 考虑A*B使用位运算
如果B的二进制表示下第i位为 1,那么这一位对最后结果的贡献就是 A*(1<<i)A∗(1<<i) ,即 A<<i,最后累加即可
又因为不能用循环,但是n<10,000 二进制表示 14位绰绰有余
*/
public int sumNums(int n) {
int ans=0,A=n,B=n+1;
boolean flag;
// 有贡献吗?
int i=0;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
flag = ((B & 1) > 0) && (ans += (A<<i)) > 0;
B>>=1;
i++;
return ans >> 1;
}
@Test
public void test(){
// System.out.println(sumNums01(4));
int i = sumNums(4);
System.out.println(i);
}
}