目录
前言
本栏目主要是针对leetcode的每日一题进行按时按量刷
本博文的笔记主要是总结自已的逻辑思路
也慢慢积累算法,感兴趣的也可关注该栏目
大部分思路都是在代码中(含注释)
796. 旋转字符串(简单)(22.4.7)
该题目链接:
796. 旋转字符串
给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。
s 的 旋转操作 就是将 s 最左边的字符移动到最右边。
例如, 若 s = ‘abcde’,在旋转一次之后结果就是’bcdea’ 。
示例 1:
输入: s = “abcde”, goal = “cdeab”
输出: true
示例 2:
输入: s = “abcde”, goal = “abced”
输出: false
提示:
1 <= s.length, goal.length <= 100
s 和 goal 由小写英文字母组成
思路:
通过字符串的拼接对比,使用substring,以及equals比较
class Solution {
public boolean rotateString(String s, String goal) {
//先判断两者长度是否相等,如果不等,直接返回false
if(s.length()!=goal.length())return false;
//在相应遍历s的字符串长度,通过substring拼接相应的字符串,如果两者相等直接返回true
for(int i=1;i<s.length();i++){
String str=s.substring(i,goal.length())+s.substring(0,i);
if(str.equals(goal))return true;
}
return false;
}
}
本身该题为简单题
官方可能就想教会我们使用某种api
如下:
class Solution {
public boolean rotateString(String s, String goal) {
return s.length() == goal.length() && (s + s).contains(goal);
}
}
字符串 s +s 包含了所有 s 可以通过旋转操作得到的字符串,只需要检查子字符串
429. N 叉树的层序遍历(中等)(22.4.8)
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
示例 2:
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]
提示:
树的高度不会超过 1000
树的节点总数在 [0, 10^4] 之间
思路:
很明显的思路就是广度优先遍历
通过层次遍历进行输出即可
区分不同的二叉树结构(层次输出其子节点,也就是遍历子节点,一个个放入队列中即可)
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> list=new ArrayList<List<Integer>>();
if(root==null)return list;
LinkedList<Node> que=new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
List<Integer> sonlist=new ArrayList<>();
int n=que.size();
for(int i=0;i<n;i++){
Node node=que.poll();
sonlist.add(node.val);
//和其他二叉树的区别是,n叉树是孩子,直接遍历n叉树的children节点
//主要是这个的区别
for(Node child:node.children){
que.offer(child);
}
}
list.add(sonlist);
}
return list;
}
}
780. 到达终点(困难)(22.4.9)
该题目链接:
780. 到达终点
给定四个整数 sx , sy ,tx 和 ty,如果通过一系列的转换可以从起点 (sx, sy) 到达终点 (tx, ty),则返回 true,否则返回 false。
从点 (x, y) 可以转换到 (x, x+y) 或者 (x+y, y)。
示例 1:
输入: sx = 1, sy = 1, tx = 3, ty = 5
输出: true
解释:
可以通过以下一系列转换从起点转换到终点:
(1, 1) -> (1, 2)
(1, 2) -> (3, 2)
(3, 2) -> (3, 5)
示例 2:
输入: sx = 1, sy = 1, tx = 2, ty = 2
输出: false
示例 3:
输入: sx = 1, sy = 1, tx = 1, ty = 1
输出: true
提示:
1 <= sx, sy, tx, ty <= 109
思路:
这道题跟某一个公司的笔试题有些相像,都是求能否加到某个数
如果正向肯定方向很多,反向的话通过求余去直接一步到位比较
具体代码思路如下:
class Solution {
public boolean reachingPoints(int sx, int sy, int tx, int ty) {
//通过反向的迭代,tx不可等于ty,否则跳出while条件
while(tx>sx && ty>sy && tx!=ty){
//本身通过求余,可以一步到位,而不是一步一步减
if(tx>ty){
tx=tx%ty;
}else if(tx<ty){
ty=ty%tx;
}
}
//终止条件 两个相等,或者某个不相等
if(tx==sx && ty==sy)return true;
else if(tx==sx && ty>sy) return (ty-sy)%tx==0; //本身最后一步,直接用减法,之后求余其对应值即可
else if(ty==sy && tx>sx) return (tx-sx)%ty==0;
//以上所有情况都不满足则返回false
return false;
}
}
357. 统计各位数字都不同的数字个数(中等)(22.4.11)
该题目链接:
357. 统计各位数字都不同的数字个数
给你一个整数 n ,统计并返回各位数字都不同的数字 x 的个数,其中 0 <= x < 10n 。
示例 1:
输入:n = 2
输出:91
解释:答案应为除去 11、22、33、44、55、66、77、88、99 外,在 0 ≤ x < 100 范围内的所有数字。
示例 2:
输入:n = 0
输出:1
提示:
0 <= n <= 8
思路:
class Solution {
public int countNumbersWithUniqueDigits(int n) {
//默认初始值 给予0的时候返回1
if(n==0)return 1;
//第一位的时候返回 10
if(n==1)return 10;
int ans=9,cur=10;
//本身是数学组合,超过两位数的时候。第一位为1-9选择(共有9个数字),第二位有0-9(排除第一位,也有9个数字),第三位以此递减
for(int i=0;i<n-1;i++){
//在这里开始实现组合的思想
ans=ans*(9-i);
//将第一位的特殊情况有10个数字加起来(小于第n的数字累加起来)
cur=cur+ans;
}
return cur;
}
}
806. 写字符串需要的行数(简单)(22.4.12)
我们要把给定的字符串 S 从左到右写到每一行上,每一行的最大宽度为100个单位,如果我们在写某个字母的时候会使这行超过了100 个单位,那么我们应该把这个字母写到下一行。我们给定了一个数组 widths ,这个数组 widths[0] 代表 ‘a’ 需要的单位, widths[1] 代表 ‘b’ 需要的单位,…, widths[25] 代表 ‘z’ 需要的单位。
现在回答两个问题:至少多少行能放下S,以及最后一行使用的宽度是多少个单位?将你的答案作为长度为2的整数列表返回。
示例 1:
输入:
widths = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = “abcdefghijklmnopqrstuvwxyz”
输出: [3, 60]
解释:
所有的字符拥有相同的占用单位10。所以书写所有的26个字母,
我们需要2个整行和占用60个单位的一行。
示例 2:
输入:
widths = [4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = “bbbcccdddaaa”
输出: [2, 4]
解释:
除去字母’a’所有的字符都是相同的单位10,并且字符串 “bbbcccdddaa” 将会覆盖 9 * 10 + 2 * 4 = 98 个单位.
最后一个字母 ‘a’ 将会被写到第二行,因为第一行只剩下2个单位了。
所以,这个答案是2行,第二行有4个单位宽度。
注:
字符串 S 的长度在 [1, 1000] 的范围。
S 只包含小写字母。
widths 是长度为 26的数组。
widths[i] 值的范围在 [2, 10]。
思路:
题目老长老长了,光是看题目就看了大半天,但是还是很好理解很好做的
具体思路如下:
左到右遍历字符串 s 中的每个字母
注意边界条件以及临界值的赋值即可
class Solution {
public int[] numberOfLines(int[] widths, String s) {
int n=s.length();
int sum=0;
//定义层面上的高度,一开始第一层就是1
int height=1;
for(int i=0;i<n;i++){
//将其字符-‘a’ 去配对哪个数组,然后将其遍历加起来即可
sum+=widths[ s.charAt(i)-'a'];
//如果超过100,则高度加1,而且把原先的值赋值给sum即可,让sum重新带着这个值遍历(不用给sum清空,在加上去,状态变量赋值即可)
if(sum>100){
height++;
sum=widths[ s.charAt(i)-'a'];
}
}
//返回的其数组变量即可
return new int[]{height,sum};
}
}
380. O(1) 时间插入、删除和获取随机元素(中等)(22.4.13)
实现RandomizedSet 类:
RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。
示例:
输入
[“RandomizedSet”, “insert”, “remove”, “insert”, “getRandom”, “remove”, “insert”, “getRandom”]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
提示:
-231 <= val <= 231 - 1
最多调用 insert、remove 和 getRandom 函数 2 * 105 次
在调用 getRandom 方法时,数据结构中 至少存在一个 元素。
思路:
想到了用哈希去计数每一个数字,但是返回随机元素的时候可能不好返回,因为要遍历哈希结构
后面看了叶总以及官方的大致意思
结合多了一种数组或者列表的这个结构
大致代码如下:
因为本身题目要求 每个函数的 平均 时间复杂度为 O(1) 。
class RandomizedSet {
//创建一个数组。也可以通过使用列表来代替数组
static int [] nums = new int [200000];
//数组下标,从-1开始遍历
int idx=-1;
//random主要是为了随机元素,map引入减少它的时间复杂度,用数组和哈希的结合
Random random ;
Map <Integer,Integer> map ;
public RandomizedSet() {
map = new HashMap <> ();
random = new Random();
}
public boolean insert(int val) {
if(map.containsKey(val))return false;
//放到数组中
nums[++idx] = val;
//数组的元素对应元素的下标值
map.put(val,idx);
return true;
}
public boolean remove(int val) {
if(!map.containsKey(val)) return false;
//取出值,然后将该值在数组删除,删除的用法是直接用尾数组覆盖其值即可
int temp = map.remove(val);
//判断删除的位置是最后一个还是中间的元素,如果不是最后一个位置
//则将其最后一个位置map集合放到其被删除的元素位置那里
if(temp!=idx) map.put(nums[idx],temp);
//将其数组的最后一个元素赋值覆盖原先删除的元素,然后idx下标减1
nums[temp] = nums[idx--];
return true;
}
public int getRandom() {
//调用random的随机nextInt函数
return nums[random.nextInt(idx + 1)];
}
}
链表加哈希计数具体如下:
class RandomizedSet {
List<Integer> nums;
Map<Integer, Integer> indices;
Random random;
public RandomizedSet() {
nums = new ArrayList<Integer>();
indices = new HashMap<Integer, Integer>();
random = new Random();
}
public boolean insert(int val) {
if (indices.containsKey(val)) {
return false;
}
int index = nums.size();
nums.add(val);
indices.put(val, index);
return true;
}
public boolean remove(int val) {
if (!indices.containsKey(val)) {
return false;
}
int index = indices.get(val);
int last = nums.get(nums.size() - 1);
nums.set(index, last);
indices.put(last, index);
nums.remove(nums.size() - 1);
indices.remove(val);
return true;
}
public int getRandom() {
int randomIndex = random.nextInt(nums.size());
return nums.get(randomIndex);
}
}
1672. 最富有客户的资产总量(简单)(22.4.14)
给你一个 m x n 的整数网格 accounts ,其中 accounts[i][j] 是第 i 位客户在第 j 家银行托管的资产数量。返回最富有客户所拥有的 资产总量 。
客户的 资产总量 就是他们在各家银行托管的资产数量之和。最富有客户就是 资产总量 最大的客户。
示例 1:
输入:accounts = [[1,2,3],[3,2,1]]
输出:6
解释:
第 1 位客户的资产总量 = 1 + 2 + 3 = 6
第 2 位客户的资产总量 = 3 + 2 + 1 = 6
两位客户都是最富有的,资产总量都是 6 ,所以返回 6 。
示例 2:
输入:accounts = [[1,5],[7,3],[3,5]]
输出:10
解释:
第 1 位客户的资产总量 = 6
第 2 位客户的资产总量 = 10
第 3 位客户的资产总量 = 8
第 2 位客户是最富有的,资产总量是 10
示例 3:
输入:accounts = [[2,8,7],[7,1,3],[1,9,5]]
输出:17
提示:
m == accounts.length
n == accounts[i].length
1 <= m, n <= 50
1 <= accounts[i][j] <= 100
思路:
class Solution {
public int maximumWealth(int[][] accounts) {
//这道题还是比较简单,求二维数组中每一维的最大值,输出最大的一维数组之和即可
int m=accounts.length;
int n=accounts[0].length;
int max=Integer.MIN_VALUE;
for(int i=0;i<m;i++){
int sum=0;
for(int j=0;j<n;j++){
sum+=accounts[i][j];
}
max=Math.max(sum,max);
}
return max;
}
}
这么简单的每日一题可能是早睡的一天
但官方可能想教会我们某个api的调用
那就是stream类
Arrays.stream(account)返回的是Optional。目的是尽可能避免 NullPointerException
然后调用该函数的sum值进行统计输出即可
关于这个类的其他解释可看这篇文章的讲解
Arrays.stream()
class Solution {
public int maximumWealth(int[][] accounts) {
int maxWealth = Integer.MIN_VALUE;
for (int[] account : accounts) {
maxWealth = Math.max(maxWealth, Arrays.stream(account).sum());
}
return maxWealth;
}
}
905. 按奇偶排序数组(简单)(22.4.28)
class Solution {
public int[] sortArrayByParity(int[] nums) {
int i=0,j=nums.length-1;
while(i<j){
//在while条件中不要忘记i小于j这个大条件判断
while(i<j && nums[i]%2==0)i++;
while(i<j && nums[j]%2==1)j--;
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
//原地置换之后 要记得i++以及j--
i++;
j--;
}
return nums;
}
}
908. 最小差值 I(简单)(22.4.30)
class Solution {
public int smallestRangeI(int[] nums, int k) {
//用第一个值保存最大和最小的值
int max=nums[0];
int min=nums[0];
//通过遍历数组找到最大值和最小值
for(int i=0;i<nums.length;i++){
min=Math.min(nums[i],min);
max=Math.max(nums[i],max);
}
//返回的值主要通过最大值减去最小值再减去2*k
//如果上面这个值小于0,说明可以变为一样的值,也就是最小值为0
//如果不小于0,说明不能变为一样的值,即(min+k,max-k)往两边缩,范围最小,总值为max-min-2*k
return Math.max(0,max-min-2*k);
}
}