if(cnt1 <= 0){
if(cnt2 < 0) return false;
else cnt2–;
}else cnt1–;
}else if(s[i] ==‘*’){
cnt2++;
}
}
printf(“%d\n”,cnt1);
printf(“%d\n”,cnt2);
if(cnt1 == 0 || cnt2 >= cnt1) return true;
return false;
}
};
使用双栈,栈内保存下标
其实第一次遍历和上面模拟思路是一样的,区别点在于
第二次遍历的时候,将*视为右括号,每个左括号必须在右括号之前
class Solution {
public:
bool checkValidString(string s) {
stack s1,s2;
for(int i = 0;s[i];i++){
if(s[i] == ‘(’) s1.push(i);
else if(s[i] == ‘)’){
if(!s1.empty()) s1.pop();
else {
if(!s2.empty()) s2.pop();
else return false;
}
}else s2.push(i);
}
while(!s1.empty() && !s2.empty()){
int idx1 = s1.top(),idx2 = s2.top();
s1.pop(),s2.pop();
if(idx1 > idx2) return false;
}
if(s1.empty()) return true;
return false;
}
};
贪心
维护最大值和最小值
-
当遇到左括号,最大值和最小值都加一
-
遇到右括号,最小值和最小值都减一
-
遇到星号,最大值加一,最小值减一
维护时应该
-
保证最小值非负
-
判断最大值是否为负,如果为负,则返回
-
最后判断最小值是否为0,为0则表示左右括号能匹配成功
class Solution {
public:
bool checkValidString(string s) {
int minn = 0,maxn = 0;
for(int i = 0;s[i];i++){
if(s[i] == ‘(’) minn++,maxn++;
else if(s[i] == ‘)’){
maxn–;
minn = max(0,minn-1);
if(maxn < 0) return false;
}else{
maxn++;
minn = max(0,minn-1);
}
}
return minn == 0;
}
};
447. 回旋镖的数量
-
距离使用平方,避免开方
-
使用哈希表查找
-
从m 个元素中选出 2 个不同元素的排列数
-
class Solution {
public:
int numberOfBoomerangs(vector<vector>& points) {
if(points.size() <= 2) return 0;
int ans = 0;
for(int i = 0;i < points.size();i++){
unordered_map<int,int> m;
int x1 = points[i][0],y1 = points[i][1];
for(int j = 0;j < points.size();j++){
int x2 = points[j][0],y2 = points[j][1];
if(i == j) continue;
int dis = (x2-x1)(x2-x1) + (y2-y1)(y2-y1);
m[dis]++;
}
for(auto dis:m) ans += (dis.second-1) * dis.second;
}
return ans;
}
};
524. 通过删除字母匹配到字典里最长单词
-
先对字典排序,排序后字典序是由大到小
-
双指针,判断字典里面的字符串的指针能否走完,能走完就是s字符串的子串
-
如果长度长那就替换,因为越到后面字典序越小
class Solution {
public:
string findLongestWord(string s, vector& dictionary) {
string ans = “”;
sort(dictionary.rbegin(),dictionary.rend());
for(int i = 0;i < dictionary.size();i++){
string sub = dictionary[i];
int k = 0;
for(int j = 0;j < s.size();j++){
while(k < sub.size() && sub[k] == s[j]) k++,j++;
}
if(k == sub.size() && sub.size() >= ans.size()) ans = sub;
}
return ans;
}
};
162. 寻找峰值
遍历,处理好边界即可
法一:遍历
class Solution {
public:
int findPeakElement(vector& nums) {
if(nums.size() == 1) return 0;
for(int i = 0;i < nums.size();i++){
if(i == 0) {
if(nums[0] > nums[1]) return 0;
}
else if(i == nums.size()-1) {
if(nums[nums.size()-1] > nums[nums.size()-2]) return nums.size()-1;
}
else {
if(nums[i] > nums[i-1] && nums[i] > nums[i+1]) return i;
}
}
return -1;
}
};
法二:二分
class Solution {
public:
int findPeakElement(vector& nums) {
int l = 0,r = nums.size()-1;
while(l < r){
int mid = (l+r) >> 1;
if(nums[mid] > nums[mid+1]) r = mid;
else l = mid + 1;
}
return l;
}
};
思考一下:
-
因为num[i] != num[i+1],并且num[-1] = num[N] = 负无穷
-
这就代表着 只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值
-
根据上述结论,我们就可以使用二分查找找到峰值
-
查找时,左指针 l,右指针 r,以其保持左右顺序为循环条件
-
根据左右指针计算中间位置 m,并比较 m 与 m+1 的值,如果 m 较大,则左侧存在峰值,r = m,如果 m + 1 较大,则右侧存在峰值,l = m + 1
-
Base on : https://leetcode-cn.com/problems/find-peak-element/solution/hua-jie-suan-fa-162-xun-zhao-feng-zhi-by-guanpengc/ 作者:guanpengchn
36. 有效的数独
class Solution {
public:
bool isValidSudoku(vector<vector>& board) {
// 遍历每一行每一列
for(int i = 0;i < 9;i++){
int x[9],y[9];
for(int i = 0;i < 9;i++) x[i] = 0,y[i] = 0;
for(int j = 0;j < 9;j++){
int idxx = board[i][j] - ‘0’,idxy = board[j][i] - ‘0’;
if(idxx >=0 && idxx <= 9){
if(!x[idxx-1]) x[idxx-1] = 1;
else return false;
}
if(idxy >= 0 && idxy <= 9){
if(!y[idxy-1]) y[idxy-1] = 1;
else return false;
}
}
}
// 遍历九个方格
int count = 9;
int countx=-3,county=0;
while(count–){
countx+=3;
if(countx == 9) countx = 0,county+=3;
int st[9];
for(int i = 0;i < 9;i++) st[i] = 0;
for(int i = 0+county;i < 3+county;i++){
// 输出三行
for(int j = 0+countx;j < 3+countx;j++){
int idx = (int)(board[i][j] - ‘0’);
if(idx >= 1 && idx <= 9) {
if(!st[idx-1]) st[idx-1] = 1;
else return false;
}
}
}
}
return true;
}
};
650. 只有两个键的键盘
class Solution {
// 判断是否为指数
public boolean isPrime(int x){
if(x <= 2) return false;
for(int i = 2;i <= x/i;i++){
if(x%i==0) return false;
}
return true;
}
public int minSteps(int n) {
// dp[i],i这个数,最小的操作数
int[] dp = new int[n+1];
for(int i = 0;i <=n;i++){
// 质数
if(isPrime(i)) dp[i] = i;
// 偶数
else if(i%2 == 0) dp[i] = dp[i/2] + 2;
// 奇数
else {
for(int j = i-1;j > 0;j–){
if(i % j == 0){
dp[i] = dp[j] + i/j;
break;
}
}
}
}
return dp[n];
}
}
我觉得这个思路其实就相当DP了
300. 最长递增子序列
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length,maxL = 0;
int[] dp = new int[n];
for(int i = 0;i < n;i++){
dp[i] = 1;
for(int j = 0;j < i;j++){
if(nums[i] > nums[j]){
if(dp[j] + 1 > dp[i]) dp[i] = dp[j]+1;
}
}
if(dp[i] > maxL) maxL = dp[i];
}
return maxL;
}
}
DP分析:
-
dp数组以及下表含义:
-
以nums[i]结尾的元素的最长递增子序列的长度
-
dp的边界考虑
-
每次遍历之前dp[i]初始化为1,当作一个数字作为上升子序列
-
dp的状态转移方程
-
dp[i] = max(dp[j]) + 1
-
因为dp[j]表示第i个元素之前的最长递增子序列的长度,那么直接将num[i]插入最后面还能构成递增子序列的话就能求出dp[i]
-
遍历顺序
-
j在i之前,dp[i]需要dp[j],从左往右
-
打印dp表
673. 最长递增子序列的个数
class Solution {
public int findNumberOfLIS(int[] nums) {
int n = nums.length,maxL = 0,ans = 0;
// 以nums[i]结尾的最长上升子序列的长度
int[] dp = new int[n];
// 以nums[i]结尾的最长上升子序列的个数
int[] cnt = new int[n];
// 遍历
for(int i = 0;i < n;i++){
dp[i] = 1;
cnt[i] = 1;
for(int j = 0;j < i;j++){
// 可以直接加在dp[j]之后
if(nums[i] > nums[j]){
if(dp[j] + 1 > dp[i]){
// 能构成一个比之前长的递增子序列
dp[i] = dp[j]+1;
cnt[i] = cnt[j];
// 构成的上升子序列和之前的上升子序列一样长
}else if(dp[j] + 1 == dp[i]) cnt[i] += cnt[j];
}
}
// 刷新最大长度
if(dp[i] > maxL){
maxL = dp[i];
ans = cnt[i];
}else if(dp[i] == maxL) ans += cnt[i];
}
return ans;
}
}
DP分析的方法和上面一题一样,这里多了个cnt[]数组
cnt[i]表示以nums[i]结尾的最长上升子序列的个数
58. 最后一个单词的长度
class Solution {
public int lengthOfLastWord(String s) {
int ans = 0;
boolean flag = true;
for(int i = s.length()-1;i >=0;i–){
if(s.charAt(i) == ’ ’ && flag) continue;
else {
flag = false;
if(s.charAt(i) == ’ ') break;
ans++;
}
}
return ans;
}
}
从后面往前面遍历,从第一个不是空格的字母到第一个空格
326. 3的幂
class Solution {
public boolean isPowerOfThree(int n) {
long x = 1;
while(x <= n){
if(x == n) return true;
x *= 3;
}
return false;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPNXY67D-1634459055741)(https://i.loli.net/2021/09/23/hFNZm4jPOUqs9MT.png)]
371. 两整数之和
class Solution {
public int getSum(int a, int b) {
int count = 1;
while(b != 0){
// a & b 可以得出所有需要进位的位
// << 1 将进位的位置移到需要进位的地方
int carry = (a&b) << 1;
// ^ 异或运算,无进位加法
a = a ^ b;
b = carry;
}
return a;
}
}
https://leetcode-cn.com/problems/sum-of-two-integers/solution/liang-zheng-shu-zhi-he-by-leetcode-solut-c1s3/
1436. 旅行终点站
双重遍历:如果cityB没有在cityA出现过
class Solution {
public String destCity(List<List> paths) {
int n = paths.size();
for(int i = 0;i < n;i ++ ){
List list1 = paths.get(i);
String second = list1.get(1);
boolean flag = true;
for(int j = 0;j < n;j++){
List list2 = paths.get(j);
String first = list2.get(0);
if(first.equals(second)){
flag = false;
break;
}
}
if(flag) return second;
}
return null;
}
}
哈希表:
class Solution {
public String destCity(List<List> paths) {
Set hashSet = new HashSet<>();
// 遍历cityA
for(List list : paths){
String cityA = list.get(0);
hashSet.add(cityA);
}
// 遍历cityB,如果cityB没有在cityA出现过,那就是终点
for(List list : paths){
String cityB = list.get(1);
if(!hashSet.contains(cityB)) return cityB;
}
return null;
}
}
405. 数字转换为十六进制数
进制转换-> 辗转相除法
class Solution {
public String toHex(int _num) {
if(_num == 0) return “0”;
Stack stack = new Stack<>();
String str = “0123456789abcdef”;
long num = _num;
if(num < 0) num = (long)(Math.pow(2, 32) + num);
int size = 0;
while(num > 0){
long k = num % 16;
stack.push(str.charAt((int)k));
num /= 16;
size++;
}
char[] ans = new char[size];
int idx = 0;
for(int i = 0;i < size;i++) ans[idx++] = stack.pop();
return new String(ans);
}
}
调用函数:
class Solution {
public String toHex(int num) {
return Integer.toHexString(num);
}
}
166. 分数到小数
class Solution {
public String fractionToDecimal(int numerator, int denominator) {
// 转成long,防止溢出
long a = numerator,b = denominator;
// 如果自身能被整除,直接返回
if(a % b == 0) return String.valueOf(a / b);
StringBuilder sb = new StringBuilder();
// 如果有一个是负数,先追加负号
if(a * b < 0) sb.append(‘-’);
// 都先变为正数
a = Math.abs(a);b = Math.abs(b);
// 计算小数点前的数,并将余数赋值给a
sb.append(String.valueOf(a/b) + “.”);
a %= b;
Map<Long,Integer> map = new HashMap<>();
while(a != 0){
// 记录当前的余数所在位置,并且继续模拟除法
map.put(a,sb.length());
// 后面加0
a*=10;
sb.append(a/b);
// 余数
a %= b;
// 如果当前余数之前出现过,则将 [出现位置 到 当前位置] 的部分抠出来(循环小数部分)
if(map.containsKey(a)){
int u = map.get(a);
return String.format(“%s(%s)”,sb.substring(0,u),sb.substring(u));
}
}
// 有限小数
return sb.toString();
}
}
思路来源:https://leetcode-cn.com/problems/fraction-to-recurring-decimal/solution/gong-shui-san-xie-mo-ni-shu-shi-ji-suan-kq8c4/
482. 密钥格式化
两个char数组
class Solution {
public String licenseKeyFormatting(String s, int k) {
int n = s.length();
char[] ch = new char[n];
int len = 0;
for(int i = 0;i < n;i++){
if(s.charAt(i) != ‘-’) ch[len++] = Character.toUpperCase(s.charAt(i));
}
int cout = 0;
if(len%k == 0)cout = len/k-1;
else cout = len/k;
if(len + cout == -1) cout = 0;
char[] ans = new char[len+cout];
int idx = len+cout-1,k2 = k;
for(int i = len-1;i >= 0;i–){
if(idx >= 0)ans[idx–] = ch[i];
k2–;
if(k2 == 0 && idx >= 0) {
ans[idx–] = ‘-’;
k2 = k;
}
}
return new String(ans);
}
}
StringBuilder
class Solution {
public String licenseKeyFormatting(String s, int k) {
StringBuilder sb = new StringBuilder();
// 从后往前
for(int i = s.length()-1,cnt = 0;i >=0 ;i–){
if(s.charAt(i) == ‘-’) continue;
// 计数器
if(cnt == k){
sb.append(‘-’);
cnt = 0;
}
sb.append(s.charAt(i));
cnt++;
}
return sb.reverse().toString().toUpperCase();
}
}
284. 顶端迭代器
// Java Iterator interface reference:
// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html
class PeekingIterator implements Iterator {
// 迭代器
private Iterator iterator;
private Integer nextElement;
public PeekingIterator(Iterator iterator) {
// initialize any member here.
this.iterator = iterator;
nextElement = iterator.next();
}
// Returns the next element in the iteration without advancing the iterator.
public Integer peek() {
return nextElement;
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
@Override
public Integer next() {
Integer ret = nextElement;
nextElement = iterator.hasNext() ? iterator.next() : null;
return ret;
}
@Override
public boolean hasNext() {
return nextElement != null;
}
}
414. 第三大的数
有序序列:
-
TreeSet里面就放最大的三个元素
-
每次都把当前元素加进来,有重复的TreeSet自动去重
-
当大于三个的时候,把最小的,就是第一个清除即可
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
tor) {
// initialize any member here.
this.iterator = iterator;
nextElement = iterator.next();
}
// Returns the next element in the iteration without advancing the iterator.
public Integer peek() {
return nextElement;
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
@Override
public Integer next() {
Integer ret = nextElement;
nextElement = iterator.hasNext() ? iterator.next() : null;
return ret;
}
@Override
public boolean hasNext() {
return nextElement != null;
}
}
414. 第三大的数
有序序列:
-
TreeSet里面就放最大的三个元素
-
每次都把当前元素加进来,有重复的TreeSet自动去重
-
当大于三个的时候,把最小的,就是第一个清除即可
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-qXljVqOr-1715796894971)]
[外链图片转存中…(img-2OZCCm78-1715796894971)]
[外链图片转存中…(img-wxsMXLdZ-1715796894971)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!