8月刷题笔记

刷题笔记—8月

LCP40.心算挑战(贪心、排序)

class Solution {
public:int maxmiumScore(vector<int>& cards, int cnt) {//24.8.1ranges::sort(cards, greater()); //从大到小排序int s = reduce(cards.begin(), cards.begin()+cnt, 0);if(s%2 == 0) return s;auto replace_sum = [&](int x) -> int {for(int i = cnt; i < cards.size(); i++) {if(cards[i]%2 != x%2) {return s-x+cards[i];}}return 0;};int x = cards[cnt-1];int ans = replace_sum(x);cout << ans;for(int i = cnt-2; i >= 0; i--) {if(cards[i]%2 != x%2) {ans = max(ans, replace_sum(cards[i]));break;}}return ans;}
};

我感觉这压根不是简单题啊!想过很多办法:双指针、前缀和、dp,dp会超时。结果是个贪心题,话说贪心思路的确可以想到,但是要实现出来好像真的很麻烦!

946.验证栈序列(栈、模拟)

class Solution {
public:bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {stack<int> st;int j = 0;for(int pu : pushed) {st.push(pu);while(!st.empty() && st.top() == popped[j]) {st.pop();j++;}}return st.empty();}
};
func validateStackSequences(pushed []int, popped []int) bool {st := []int{}j := 0for _, val := range pushed {st = append(st, val)for len(st) > 0 && st[len(st)-1] == popped[j] {st = st[:len(st)-1]j++}}if len(st) > 0 {return false} else {return true}
}

简单的栈的使用,主要是模拟题目的过程。

3128.直角三角形(数学)

class Solution {
public:long long numberOfRightTriangles(vector<vector<int>>& grid) {int n = grid[0].size();vector<int> col_sum(n, -1); //提前减一for(auto row : grid) {for(int i = 0; i < n; i++) {col_sum[i] += row[i];}}long long ans = 0;for(auto row : grid) {int rowsum = reduce(row.begin(), row.end(), 0)-1;   //提前减一for(int j = 0; j < n; j++) {if(row[j] == 1) ans += rowsum*col_sum[j];}}return ans;}
};
func numberOfRightTriangles(grid [][]int) int64 {n := len(grid[0])colSum := make([]int, n)for _, row := range grid {for j, x := range row {colSum[j] += x}}ans := 0for _, row := range grid {rowSum := -1   //提前减一for _, x := range row {rowSum += x}for j, x := range row {if x == 1 {ans += rowSum*(colSum[j]-1)}}}return int64(ans)
}

1021.删除最外层的括号(栈、计数法)

class Solution {
public:string removeOuterParentheses(string s) {int level = 0;string ans;for(auto c : s) {if(c == ')') level--;if(level) ans.push_back(c);if(c == '(') level++;}return ans;}
};
func removeOuterParentheses(s string) string {level := 0ans := []byte{}for _, c := range s {if c == ')' {level--}if level > 0 {ans = append(ans, byte(c))}if c == '(' {level++}}return string(ans)
}

涨见识的一道题,关键是要想到这个方法,已经是知道要用栈来写这一题,但是还是没办法解决,这种感觉太难受了,就是无论怎么硬想都想不出来。。。这里的level是用来计数的,第一次和为0就代表第一个原语,还有这里的顺序很重要!感觉就把这个当作模板来记忆吧!

1190.反转每对括号间的子串(栈)

class Solution {
public:string reverseParentheses(string s) {stack<string> st;string str = "";for(char c : s) {if(c == '(') {st.push(str);str = "";} else if(c == ')') {reverse(str.begin(), str.end());str = st.top()+str;st.pop();} else {str.push_back(c);}}return str;}
};
func reverseParentheses(s string) string {st := [][]byte{}str := []byte{}for _, c := range s {if c == '(' {st = append(st, str)str = []byte{}} else if c == ')' {for j, n := 0, len(str); j < n-1-j; j++ {str[j], str[n-1-j] = str[n-1-j], str[j]}str = append(st[len(st)-1], str...)st = st[:len(st)-1]} else {str = append(str, byte(c))}}return string(str)
}

看来还是大意了,以为栈的题目都是很简单的,现在来看好像其实并不如此,还是有很多题型和方法是需要总结的。对于这一题,和之前都有所不同,这里是将栈定义成了stack<string>类型保存的每一层的str,当遇到’(‘就将str入栈并str更新为空,当遇到’)'就将这一层的str反转并返回给上一层,这里也是复习到了reverse(nums.begin(), nums.end())反转函数,然后就是对于go,是没有反转函数的,这样也复习到了对对称字符串的操作了,最后特别说明一行str = append(st[len(st)-1], str...),由于str是一个字符串,所以append中的str要写成str…

1003.检查替换后的词是否有效(栈)

自己写的版本

class Solution {
public:bool isValid(string s) {stack<char> st;for(char ch : s) {if(!st.empty() && ch == 'c') {char t = st.top();st.pop();if(!st.empty() && t == 'b' && st.top() == 'a') st.pop();else st.push(t), st.push(ch);} else {st.push(ch);}}return st.empty();}
};

思路就是将abc看成整体,所以只需要遇到c的时候再出栈就行,然后就是栈操作的注意事项,这一题wa了两次,主要就是错在了两次的栈判空操作上,由于需要取出c的前两位,所以就需要有两次的st判空操作,这样就能ac这一题了

func isValid(s string) bool {st := []byte{}for _, ch := range s {if len(st) > 0 && ch == 'c' {t := st[len(st)-1]st = st[:len(st)-1]if len(st) > 0 && t == 'b' && st[len(st)-1] == 'a' {st = st[:len(st)-1]} else {st = append(st, byte(ch))}} else {st = append(st, byte(ch))}}if len(st) > 0 {return false} else {return true}
}

灵神版本

class Solution {
public:bool isValid(string s) { // s 同时作为栈int i = 0; // i-1 表示栈顶下标,i=0 表示栈为空for (char c: s) {if (c > 'a' && (i == 0 || c - s[--i] != 1))return false;if (c < 'c')s[i++] = c; // 入栈}return i == 0;}
};

真的太过于简洁了,很多东西都合并在一起了,不看题解文章真的有点难理解。。。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2216.美化数组的最少删除数

class Solution {
public:int minDeletion(vector<int>& nums) {int cnt = 0, n = nums.size();for(int i = 0; i < n; i++) {if((i-cnt)%2 == 0 && i+1 < n && nums[i] == nums[i+1]) cnt++;}return (n-cnt)%2 == 0 ? cnt : cnt+1;}
};
func minDeletion(nums []int) int {cnt, n := 0, len(nums)for i := 0; i < n; i++ {if (i-cnt)%2 == 0 && i+1 < n && nums[i] == nums[i+1] {cnt++}}if (n-cnt)%2 == 0 {return cnt} else {return cnt+1}
}

这是栈的题单中的一道题,但是我看不懂栈的写法,从评论区看到一个很牛逼的写法,关键就是找到这个下标i-cnt,这一点我感觉很妙,也非常切合题目意思,删除一个元素之后其它元素向前移动,这个就是充分利用了下标的和变量之间的关系

1006.笨阶乘(栈)

class Solution {
public:int clumsy(int n) {//24.8.3stack<int> st;st.push(n);n--;int index = 0;while(n > 0) {cout << n;if(index%4 == 0) st.top() *= n;else if(index%4 == 1) st.top() /= n;else if(index%4 == 2) st.push(n);else if(index%4 == 3) st.push(-n);index++;n--;}int sum = 0;while(!st.empty()) {sum += st.top();st.pop();}return sum;}
};
func clumsy(n int) int {st := []int{}st = append(st, n)n--index := 0for n > 0 {if index%4 == 0 {st[len(st)-1] *= n} else if index%4 == 1 {st[len(st)-1] /= n} else if index%4 == 2 {st = append(st, n)} else if index%4 == 3 {st = append(st, -n)}n--index++}sum := 0for len(st) > 0 {sum += st[len(st)-1]st = st[:len(st)-1]}return sum
}

栈的灵活运用,思考的时候想到了利用取余的方式判断乘除加减,就是对栈操作上还是有很多漏洞,这一题的思路就是遇到乘除就将栈顶元素进行运算,遇到加减就将n或-n入栈就行,唉,主要是没想到这一些栈操作。

224.基本计算器(栈)

class Solution {
public:int calculate(string s) {//24.8.3stack<int> st;  //保存每一层的符号位int sign = 1;st.push(sign);int n = s.size(), i = 0, ans = 0;while(i < n) {if(s[i] == ' ') i++;else if(s[i] == '+') sign = st.top(), i++;else if(s[i] == '-') sign = -st.top(), i++;else if(s[i] == '(') st.push(sign), i++;else if(s[i] == ')') st.pop(), i++;else {long long num = 0;while(i < n && s[i] >= '0' && s[i] <= '9') {num = num*10+s[i]-'0';i++;}ans += sign*num;}}return ans;}
};
func calculate(s string) int {st := []int{}st = append(st, 1)i, n, ans, sign := 0, len(s), 0, 1for i < n {switch s[i] {case ' ':i++case '+':sign = st[len(st)-1]i++case '-':sign = -st[len(st)-1]i++case '(':st = append(st, sign)i++case ')':st = st[:len(st)-1]i++default:num := 0for i < n && s[i] >= '0' && s[i] <= '9' {num = num*10+int(s[i]-'0')  //一定要有int()i++}ans += sign*num}}return ans
}

和上一题相似,都属于表达式解析类型的题目,当然这里仅限的符号加减和括号,还没有涉及到乘除运算。两题解法其实很相似,栈中都是保存的每一层的数据,这里保存的是当前层的符号位,上一题保存的是值,感觉对栈的应用层次还是挺低的,就是明确这一题是用栈来实现,但是就是写不出

1475.商品折扣后的最终价格(单调栈)

class Solution {
public:vector<int> finalPrices(vector<int>& prices) {int n = prices.size();stack<int> st;vector<int> ans(n);for(int i = n-1; i >= 0; i--) {while(!st.empty() && st.top() > prices[i]) st.pop();ans[i] = st.empty() ? prices[i] : prices[i]-st.top();st.push(prices[i]);}return ans;}
};
func finalPrices(prices []int) []int {st := []int{}n := len(prices)ans := make([]int, n)for i := n-1; i >= 0; i-- {for len(st) > 0 && st[len(st)-1] > prices[i] {st = st[:len(st)-1]}if len(st) == 0 {ans[i] = prices[i]} else {ans[i] = prices[i]-st[len(st)-1]}st = append(st, prices[i])}return ans
}

栈中维护的是右侧第一个小于prices[i]的值,自底向上是递增的一个栈,当栈空时表明当前prices[i]右侧没有小于它的值,每次循环结束后需要将当前prices[i]入栈

496.下一个更大元素I(单调栈、哈希表)

从右往左

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {int n1 = nums1.size(), n2 = nums2.size();vector<int> ans(n1, -1);    //找不到直接初始化为-1unordered_map<int, int> hash;    //nums1[i] -- ifor(int i = 0; i < n1; i++) {hash[nums1[i]] = i;}stack<int> st;for(int i = nums2.size()-1; i >= 0; i--) {int x = nums2[i];while(!st.empty() && x >= st.top()) st.pop();if(!st.empty() && hash.contains(x)) ans[hash[x]] = st.top();st.push(x);}return ans;}
};
func nextGreaterElement(nums1 []int, nums2 []int) []int {n1, n2 := len(nums1), len(nums2)hash := make(map[int]int)for i, x := range nums1 {hash[x] = i}st := []int{}ans := make([]int, n1)//得把ans数组初始化为-1 否则要在if len(st) > 0处加个else判断// for i := range ans {//     ans[i] = -1// }for i := n2-1; i >= 0; i-- {x := nums2[i]for len(st) > 0 && x >= st[len(st)-1] {st = st[:len(st)-1]}if len(st) > 0 {if j, ok := hash[x]; ok {ans[j] = st[len(st)-1]}} else {ans[hash[x]] = -1}st = append(st, x)}return ans
}

过了一天后感觉对单调栈有了新认识,之前做过单调栈的题,但是昨天做的时候忘掉的差不多了,怎么都想不清楚这个单调栈维护的过程。

从左往右

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {int n1 = nums1.size(), n2 = nums2.size();vector<int> ans(n1, -1);    //找不到直接初始化为-1unordered_map<int, int> hash;    //nums1[i] -- ifor(int i = 0; i < n1; i++) {hash[nums1[i]] = i;}stack<int> st;  //记录还没有找到下一个更大元素的栈for(int x : nums2) {while(!st.empty() && x > st.top()) {ans[hash[st.top()]] = x;st.pop();}if(hash.contains(x)) {st.push(x);}}return ans;}
};

572.另一棵树的子树(二叉树)—100

class Solution {
public:bool isSameTree(TreeNode* p, TreeNode* q) {if(p == nullptr || q == nullptr) return p == q;return p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);}bool isSubtree(TreeNode* root, TreeNode* subRoot) {if(root == nullptr) return false;return isSameTree(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);}
};
func isSameTree(p, q *TreeNode) bool {if p == nil || q == nil {return p == q}return p.Val == q.Val && isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)
}func isSubtree(root *TreeNode, subRoot *TreeNode) bool {if root == nil {return false}return isSameTree(root, subRoot) || isSubtree(root.Left, subRoot) || isSubtree(root.Right, subRoot)
}

这一题和100关联了起来,调用了100的函数isSameTree用来判断p,q是否为相同的树,关键就是最后return的或逻辑,我感觉不是很好理解,我的理解是可能只要是root的一个子树满足于subtree相同就行了,所以是或的逻辑

1019.链表中的下一个更大节点

从右往左遍历

class Solution {
public:vector<int> nextLargerNodes(ListNode* head) {vector<int> t;ListNode* p = head;while(p) {t.push_back(p->val);p = p->next;}int n = t.size();stack<int> st;vector<int> ans(n);for(int i = 0; i < n; i++) {int x = t[i];while(!st.empty() && x > t[st.top()]) {ans[st.top()] = x;st.pop();}//if(st.empty()) ans[i] = 0;st.push(i);}return ans;}
};

从左往右遍历

class Solution {
public:vector<int> nextLargerNodes(ListNode* head) {vector<int> t;ListNode* p = head;while(p) {t.push_back(p->val);p = p->next;}int n = t.size();stack<int> st;vector<int> ans(n);for(int i = 0; i < n; i++) {int x = t[i];while(!st.empty() && x > t[st.top()]) {ans[st.top()] = x;st.pop();}//if(st.empty()) ans[i] = 0;st.push(i);}return ans;}
};

把问题化简之后就好写很多了!就是先遍历一遍链表,同时将链表的值存入数组t中,这样问题就变成了求数组中当前元素的下一个更大的值了

227.基本计算器II(栈)

class Solution {
public:int calculate(string s) {vector<int> st;char preSign = '+';long long num = 0, n = s.size();for(int i = 0; i < n; i++) {if(isdigit(s[i])) num = num*10+s[i]-'0';if(!isdigit(s[i]) && s[i] != ' ' || i == n-1) {switch(preSign) {case '+':st.push_back(num);break;case '-':st.push_back(-num);break;case '*':st.back() *= num;break;default:st.back() /= num;}preSign = s[i];num = 0;}}return reduce(st.begin(), st.end(), 0);}
};

这种求表达式计算的值主要难点就在于分类讨论加上栈的操作,把这两点处理好就能很好的写出这一类题了

856.括号的分数(栈)

class Solution {
public:int scoreOfParentheses(string s) {stack<int> st;st.push(0);for(char c : s) {if(c == '(') st.push(0);else {int t = st.top();st.pop();st.top() += max(2*t, 1);}}return st.top();}
};
func scoreOfParentheses(s string) int {st := []int{}st = append(st, 0)for _, c := range s {if c == '(' {st = append(st, 0)} else {t := st[len(st)-1]st = st[:len(st)-1]st[len(st)-1] += max(2*t, 1)}}return st[len(st)-1]
}

真的模拟不出来啊!!!有一种有力使不出的感觉,一个劲的想模拟这个过程,但是就是无论怎么想都想不出来,题解使用一个max函数就将这个括号得分的情况分类讨论出来了,这真的很妙!

1209.删除字符串中的所有相邻重复项II(栈)

class Solution {
public:string removeDuplicates(string s, int k) {stack<int> cnt;for(int i = 0; i < s.size(); i++) {if(i == 0 || s[i] != s[i-1]) cnt.push(1);else if(++cnt.top() == k) {s.erase(i-k+1, k);cnt.pop();i -= k;}}return s;}
};

用栈来维护上一个字符出现的次数,回顾了一下字符串的操作,删除字符串中的子串。erase(a, b),参数a:下标的起始位置,参数b:删除子串的长度。

2211.统计道路上的碰撞次数(脑经急转弯)

class Solution {
public:int countCollisions(string directions) {int cntL = 0, cntR = 0, cntS = 0, n = directions.size();int i = 0, j = n-1;if(n == 1) return 0;while(i < n && directions[i++] == 'L') cntL++;while(j > 0 && directions[j--] == 'R') cntR++;//cout << cntL << " " << cntR << " " << count(directions.begin(), directions.end(), 'S');cntS = count(directions.begin(), directions.end(), 'S');return n-cntL-cntR-cntS;}
};
func countCollisions(directions string) int {directions = strings.TrimLeft(directions, "L")    //前缀向左的车不会发生碰撞---移除开头Ldirections = strings.TrimRight(directions, "R")   //后缀向右的车不会发生碰撞---移除末尾Rreturn len(directions)-strings.Count(directions, "S") //剩下非停止的车必然会碰撞
}

本来说是一道栈的题目,直接给题解区秒杀了,我的天呐,厉害实在是厉害

1046.最后一块石头的重量(堆)

class Solution {
public:int lastStoneWeight(vector<int>& stones) {priority_queue<int> pq;for(int s : stones) pq.push(s);while(pq.size() > 1) {int a = pq.top();pq.pop();int b = pq.top();pq.pop();if(a > b) pq.push(a-b);}return pq.empty() ? 0 : pq.top();}
};

这个就体现出了选择好的数据结构解题的重要性了。记住记住:优先队列定义默认是大根堆,也就是堆顶保存的是最大的元素,如果要定义小根堆那么需要加上greater<>默顶大反g

2558.从数量最多的堆取走礼物(堆、模拟)

class Solution {
public:long long pickGifts(vector<int>& gifts, int k) {priority_queue<int> pq(gifts.begin(), gifts.end());while(k--) {int t = pq.top();pq.pop();pq.push(sqrt(t));}long long ans = 0;while(!pq.empty()) {ans += pq.top();pq.pop();}return ans;}
};

首先是要想到这一题要用堆来写,然后就模拟堆的过程就行了,堆和栈队列一样,在遍历的时候不能用迭代的方法来遍历,只能弹出判空来遍历这一类型的数据结构

636.函数的独占时间(栈)

typedef pair<int, int> PII;class Solution {
public:vector<int> exclusiveTime(int n, vector<string>& logs) {vector<int> ans(n, 0);stack<PII> st;for(int i = 0; i < logs.size(); i++) {vector<string> temp = split(logs[i], ':');int id = stoi(temp[0]), t1 = stoi(temp[2]);if(temp[1] == "start") st.push({id, t1});else {auto top = st.top();st.pop();int t2 = top.second;ans[top.first] += t1-t2+1;/ans[1]=5-2+1=4if(!st.empty()) ans[st.top().first] -= t1-t2+1;//ans[0]=6-0-(5-2+1)}}return ans;}//手撕split函数vector<string> split(string& s, char delimiter) {vector<string> ans;int left = 0, right = 0;while(right < s.size()) {if(s[right] == delimiter) {ans.push_back(s.substr(left, right-left));left = right+1;}right++;}ans.push_back(s.substr(left, right-left));return ans;}
};

首先要看懂这一题要有一点操作系统的知识,这样才好理解这一题的意思。这一个题解也是我从评论区中看到的我想学的一个题解,pair的应用和手撕split函数。感觉C++没有split函数就复杂了好多,像python和java都可以直接调用split函数可以简化很多步骤。这一题还有个难点就在于这个时间的计算上要对应这个ans的下标需要巧妙的搭配上。以示例一为计算结果的过程放在了代码旁边的注释上了

func exclusiveTime(n int, logs []string) []int {ans := make([]int, n)type pair struct{ idx, timestamp int }st := []pair{}for _, log := range logs {sp := strings.Split(log, ":")idx, _ := strconv.Atoi(sp[0])timestamp, _ := strconv.Atoi(sp[2])if sp[1][0] == 's' {if len(st) > 0 {ans[st[len(st)-1].idx] += timestamp - st[len(st)-1].timestampst[len(st)-1].timestamp = timestamp}st = append(st, pair{idx, timestamp})} else {p := st[len(st)-1]st = st[:len(st)-1]ans[p.idx] += timestamp - p.timestamp + 1if len(st) > 0 {st[len(st)-1].timestamp = timestamp + 1}}}return ans
}

go的计算方法和cpp的计算方法是不一样的,go的计算方法是改变了栈顶的time值,从而来计算ans的,而cpp是直接通过计算来算出ans的,相比来说go的写法更容易发现一些,cpp的计算方法需要发掘出数字之间的关系,想到需要一点时间。

2434.使用机器人打印字典序最小的字符串(栈、哈希表、贪心)

class Solution {
public:string robotWithString(string s) {string ans;stack<char> st;int cnt[26]{};int mn = 0;for(char c : s) cnt[c-'a']++;for(char c : s) {cnt[c-'a']--;while(mn <= 25 && cnt[mn] == 0) mn++;st.push(c);while(!st.empty() && st.top()-'a' <= mn) {ans += st.top();st.pop();}}return ans;}
};
func robotWithString(s string) string {ans := make([]byte, 0, len(s))st := []byte{}cnt := [26]int{}for _, c := range s {cnt[c-'a']++}mn := byte(0)for _, c := range s {cnt[c-'a']--for mn < 26 && cnt[mn] == 0 {mn++}st = append(st, byte(c))for len(st) > 0 && st[len(st)-1]-'a' <= mn {ans = append(ans, st[len(st)-1])st = st[:len(st)-1]}}return string(ans)
}

感觉这个题还是挺难的,如果纯粹的模拟这一题肯定是过不了的,主要是要想到这是一个栈的过程,然后在解题过程中如何展现出"维护剩余字母的最小字母",难点就在于此了。

735.小行星碰撞(栈、邻项消除)

class Solution {
public:vector<int> asteroidCollision(vector<int>& asteroids) {vector<int> st;for(auto aster : asteroids) {bool alive = true;while(alive && aster < 0 && !st.empty() && st.back() > 0) {alive = aster < -st.back();if(st.back() <= -aster) st.pop_back();}if(alive) st.push_back(aster);}return st;}};
func asteroidCollision(asteroids []int) []int {st := []int{}for _, aster := range asteroids {alive := truefor alive == true && aster < 0 && len(st) > 0 && st[len(st)-1] > 0 {alive = aster < -st[len(st)-1];if st[len(st)-1] <= -aster {st = st[:len(st)-1]}}if alive == true {st = append(st, aster)}}return st
}

自己写的屎山代码然后还超时,唉,这次官解都写的特别简单,其实感觉自己能明白这种意思,但是写出来就是没那意思。。。

690.员工的重要性(dfs、哈希表)

class Solution {
public:int getImportance(vector<Employee*> employees, int id) {unordered_map<int, Employee*> mp;for(auto e : employees) mp[e->id] = e;auto dfs = [&](auto&& dfs, int id) -> int {auto e = mp[id];int res = e->importance;for(auto s : e->subordinates) {res += dfs(dfs, s);}return res;};return dfs(dfs, id);}
};
func getImportance(employees []*Employee, id int) int {mp := make(map[int]*Employee, len(employees))for _, e := range employees {mp[e.Id] = e}var dfs func(int) intdfs = func(id int) int {e := mp[id]res := e.Importancefor _, subid := range  e.Subordinates {res += dfs(subid)}return res}return dfs(id)
}

思考这一题的时候点还是到位了,但是写不到位,主要感觉还是dfs不熟练,很明显的一个树结构,加上数据范围不是很大,所以这一题用dfs很合理

2336.无限集中的最小数字(优先队列、有序集合、哈希表)

有序集合写法

class SmallestInfiniteSet {
public:int thres = 1;set<int> s;SmallestInfiniteSet() {}int popSmallest() {if(s.empty()) {int ans = thres++;return ans;}auto ans = *s.begin();s.erase(s.begin());return ans;}void addBack(int num) {if(num < thres) {s.insert(num);}}
};

小根堆+哈希表

class SmallestInfiniteSet {
public:vector<bool> vis;priority_queue<int, vector<int>, greater<>> pq;int idx;SmallestInfiniteSet() : idx(1) {vis.resize(1010, false);}int popSmallest() {if(pq.empty()) {int ans = idx++;return ans;}int ans = pq.top();pq.pop();vis[ans] = false;return ans;}void addBack(int num) {if(num >= idx || vis[num]) return;if(num == idx-1) idx--;else {pq.push(num);vis[num] = true;}}
};

写这一题的时候有两个困扰我的点:1、如何表示这个无限长的正整数集合;2、对堆进行操作的时候如何进行判重的操作。看了题解之后其实就是这两点是答案的所在。这里用idx边界来表示这个正整数集合,使用小根堆+哈希表来达到去重的操作,在添加元素的时候就进行去重,这样就不用担心集合里出现重复的元素了。

2530.执行k次操作后的最大分数(堆、向上取整)

class Solution {
public:long long maxKelements(vector<int>& nums, int k) {long long ans = 0;priority_queue<int> pq;for(auto n : nums) pq.push(n);while(k--) {int t = pq.top();//cout << t << endl;ans += t;pq.pop();t = ceil((double)t/3);pq.push(t);}return ans;}
};
func maxKelements(nums []int, k int) int64 {var ans int64h := hp{nums}heap.Init(&h)   //原地堆化for ; k > 0; k-- {ans += int64(h.IntSlice[0]) //堆顶h.IntSlice[0] = (h.IntSlice[0]+2)/3	//对堆顶进行操作heap.Fix(&h, 0) //调整堆}return ans
}type hp struct{ sort.IntSlice }
func (h hp) Less(i, j int) bool { return h.IntSlice[i] > h.IntSlice[j] } // 最大堆
func (hp) Push(any)             {}
func (hp) Pop() (_ any)         { return }

熟悉堆操作,就是简单的对堆的插入删除,调整堆的操作

3066.超过阈值的最少操作数II(堆、模拟)—1962、2208、2233

class Solution {
public:int minOperations(vector<int>& nums, int k) {int ans = 0;priority_queue<long long, vector<long long>, greater<>> pq;for(auto n :nums) pq.push((long long)n);while(pq.top() < k) {long long x = pq.top(); pq.pop();long long y = pq.top(); pq.pop();pq.push(2*x+y);ans++;}return ans;}
};

自己能顺利ac了,但是看了题解发现写的就是屎山代码,看到题解的简化版本就能顿悟的感觉怎么能不佩服!

3144.分割字符频率相等的最少子字符串(动态规划、记忆化搜索)

class Solution {
public:int minimumSubstringsInPartition(string s) {int n = s.size();vector<int> memo(n, -1);auto dfs = [&](auto&& dfs, int i) -> int {if(i < 0) return 0;int& res = memo[i];if(res > 0) return res;res = INT_MAX;int cnt[26]{}, maxcnt = 0, k = 0;for(int j = i; j >= 0; j--) {k += cnt[s[j]-'a']++ == 0;maxcnt = max(maxcnt, cnt[s[j]-'a']);if(i-j+1 == k*maxcnt) {res = min(res, dfs(dfs, j-1)+1);}}return res;};return dfs(dfs, n-1);}
};

转化成递推

class Solution {
public:int minimumSubstringsInPartition(string s) {int n = s.size();vector<int> dp(n+1, INT_MAX);dp[0] = 0;for(int i = 0; i < n; i++) {int cnt[26]{}, maxcnt = 0, k = 0;for(int j = i; j >= 0; j--) {k += cnt[s[j]-'a']++ == 0;maxcnt = max(maxcnt, cnt[s[j]-'a']);if(i-j+1 == k*maxcnt) {dp[i+1] = min(dp[i+1], dp[j]+1);}}}return dp[n];}
};

如何理解外层循环是从0开始的呢?可以这样理解,由于我求的是dp[n],所以我最后的结果是dp[n],如果我从后往前遍历的话会导致dp[n]早已赋值完毕了,这样我们求的dp[n]就不是答案了,所以正确的外层遍历顺序是从前往后遍历。

208.实现Trie(前缀树)

class Trie {
public:bool isEnd;Trie* next[26];Trie() {isEnd = false;memset(next, 0, sizeof(next));}void insert(string word) {Trie* node = this;for(char c : word) {if(node->next[c-'a'] == NULL) {node->next[c-'a'] = new Trie();}node = node->next[c-'a'];}node->isEnd = true;}bool search(string word) {Trie* node = this;for(char c : word) {node = node->next[c-'a'];if(node == NULL) return false;//node = node->next[c-'a'];}return node->isEnd;}bool startsWith(string prefix) {Trie* node = this;for(char c : prefix) {node = node->next[c-'a'];if(node == NULL) return false;//node = node->next[c-'a'];}return true;}
};

这个东西实现起来感觉有点像多叉树,准确来说应该是26叉树,在写的过程中insert函数我有不同的见解,就是和search一样,将node=node->next[c-‘a’]放在前面,但是这样是不对的,看样子是将node开辟了一个新空间,但是这里的node是个临时变量,其实就是用来遍历这个树的一个指针,所以在insert函数只能是node = node->next[c-'a'];放在循环体末尾。

3153.所有数对中数位差之和(枚举右维护左、拆位)

class Solution {
public:long long sumDigitDifferences(vector<int>& nums) {vector<array<int, 10>> cnt(to_string(nums[0]).size());  //内层用vector或者array都行 行表示当前是哪一位  列表示当前这一位是哪个数字long long ans = 0;for(int i = 0; i < nums.size(); i++) {for(int j = 0, x = nums[i]; x > 0; j++, x /= 10) {ans += i-cnt[j][x%10]++;    //找到数量关系i-cnt[j][x%10]很重要 下标i表示前面有i个数}}return ans;}
};
func sumDigitDifferences(nums []int) (ans int64) {cnt := make([][10]int, len(strconv.Itoa(nums[0])))for i, x := range nums {for j := 0; x > 0; x /=10 {ans += int64(i-cnt[j][x%10])cnt[j][x%10]++j++}}return ans
}

做这一题的思路仅限于想到了要竖着看这些数,但是难在不知道如何实现上,关键是这个数组的定义要搞明白,主要是打消了固定的思维方式,这两层for循环并不是对应的cnt数组下标。当然发掘每个数位差之和这个等量关系也很重要,i-cnt[j] [x%10]

14.最长公共前缀(字符串)

class Solution {
public:string longestCommonPrefix(vector<string>& strs) {string s = strs[0];for(int i = 0; i < s.size(); i++) {for(string str : strs) {if(i == s.size() || s[i] != str[i]) {return s.substr(0, i);  //当下标超过str的长度 或者遇到了不同的字符}}}return s;   //遍历完整个s了 s就是最大的那个公共前缀}
};
func longestCommonPrefix(strs []string) string {s := strs[0]for i := range s {for _, str := range strs {if i == len(str) || str[i] != s[i] {return s[:i]}}}return s
}

从灵神字典树题单中找的,结果是个简单题,我连暴力的写法都想不出,感觉大抵是费了。。。其实感觉和刚刚做过的拆位题很像,也都是要一个一个竖着看,只不过这里需要左对齐竖着看,从这两道题目来看啊,感觉就是熟悉了习惯的遍历方法,突然换一个遍历的方式就感觉很陌生,看来还是需要多适应适应。

3127.构造相同颜色的正方形(举证、枚举)

class Solution {
public:bool canMakeSquare(vector<vector<char>>& grid) {auto check = [&] (int i, int j) -> bool {int cnt[2]{};cnt[grid[i][j]&1]++;cnt[grid[i+1][j]&1]++;cnt[grid[i][j+1]&1]++;cnt[grid[i+1][j+1]&1]++;return cnt[0] != 2;};return check(0, 0) || check(0, 1) || check(1, 0) || check(1, 1);}
};
func canMakeSquare(grid [][]byte) bool {check := func(i, j int) bool {cnt := make([]int, 2)cnt[grid[i][j]&1]++cnt[grid[i][j+1]&1]++cnt[grid[i+1][j]&1]++cnt[grid[i+1][j+1]&1]++return cnt[0] != 2}return check(0, 0) || check(0, 1) || check(1, 0) || check(1, 1)
}

简单题简单做,这一题做出来后看题解也是相同的思路,只不过没有注意到的是这是一个3*3的一个举证,所以只需要枚举四个左上角就行了,写两个for循环确实显得比较臃肿

8月总结

求三角形个数

以3128为例,枚举三角形的中间,然后通过乘法原理的数学等式来累加直角三角形的个数ans+=(rowsum-1)*(colsum-1),主要是发现这个乘法原理吧,再就是对二维矩阵的操作

计数法和栈的应用

这一用法出现在1021,题目意思是要去除最外层的括号,这里就需要用到计数法,当计数第一次到0就代表第一层原语结束,循环体内的if条件的顺序十分重要!

对称字符串的操作

//C++
for(int i = 0, n = nums.size(); i < n-1-i; i++) {swap(nums[i], nums[n-1-i]);
}//Go
for j, n := 0, len(str); j < n-1-j; j++ {str[j], str[n-1-j] = str[n-1-j], str[j]
}

发掘变量之间的关系

对于某些题中,当你发现了某些变量之间的等价关系之后解题就会变得非常简单,以2216为例,下标i-cnt就非常符合题中“删除一个数,其它元素向前移动”,这样就动态的表示出来了数组的下标

字符串和数字之间的转化

//字符串转数字	如果涉及到取模运算,那么在每位累加的时候取模就行
long long num = 0;
while(i < n && s[i] >= '0' && s[i] <= '9') {num = num*10+s[i]-'0';i++;
}
ans += sign*num;//方法二	只适用于小数字,如果字符串非常长,就不适用
stoi("111111")//数字转字符串
to_string(111111)

单调栈

有两种遍历方式:从左到右和从右到左,对于我自己来说,感觉从右到左遍历好像更好理解一点。总结一下这两天做的单调栈的题,有了全新的认识,这类题型感觉可以算是一种模板。两种遍历方式则栈的定义就会不同,下面是模板代码,以1475为例

//从右往左遍历
int n = prices.size();
stack<int> st;	//栈定义为下一个更...的元素值(或下标)的候选项	需要从后往前遍历
vector<int> ans(n);
for(int i = n-1; i >= 0; i--) {while(!st.empty() && ...) st.pop();if(...) ans[i] ...else ans[i] ...st.push(...);
}//从左往右遍历int n = prices.size();
stack<int> st;	//栈定义为还未被使用的元素值(或下标)	需要从前往后遍历
vector<int> ans(n);
for(int i = 0; i < n; i++) {while(!st.empty() && ...) {ans[i]...st.pop();}if(...) ...st.push(...);
}一般来说从右往左遍历栈定义成元素值,从左往右遍历栈定义成下标

省略号的逻辑就是因题而异了,大致模板就是这样,只要将省略号的逻辑写对了这个题就写对了

手撕split函数

vector<string> split(string& s, char delimiter) {vector<string> ans;int left = 0, right = 0;while(right < s.size()) {if(s[right] == delimiter) {ans.push_back(s.substr(left, right-left));left = right+1;}right++;}ans.push_back(s.substr(left, right-left));//首尾操作return ans;
}

去重的思路

哈希表(记忆化搜索,数组去重、堆去重)、set容器

向上取整的应用(ceil函数、+k-1)

cout << ceil(2.3);	//3
cout << ceil(11/3);	//3
cout << ceil()(double)11/3);	//4

在调用这个函数的时候只需要注意一个点,那就是括号里面的数一定要是浮点数,以第二个式子为例,先计算10/3=3,然后再将3调入到ceil函数,那么就变成了ceil(3),自然就会输出3了而不是4,改正的方法是ceil((double)10/3)

还有一种写法就是在除k之前将val先加上k-1,这样也能达到向上取整的效果

cout << (11+2)/3;	//4

前缀树、字典树(trie)模板—可以说成26叉树

class Trie {
public:bool isEnd;Trie* next[26];//或者vector<Trie*> node;这样定义//Trie() : isEnd(false), node(26, null) {}或者这样初始化Trie() {isEnd = false;memset(next, 0, sizeof(next));}void insert(string word) {Trie* node = this;for(char c : word) {if(node->next[c-'a'] == NULL) {node->next[c-'a'] = new Trie();}node = node->next[c-'a'];}node->isEnd = true;}bool search(string word) {Trie* node = this;for(char c : word) {node = node->next[c-'a'];if(node == NULL) return false;//node = node->next[c-'a'];}return node->isEnd;}bool startsWith(string prefix) {Trie* node = this;for(char c : prefix) {node = node->next[c-'a'];if(node == NULL) return false;//node = node->next[c-'a'];}return true;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/53136.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无线麦克风哪个牌子的好,麦克风哪个好,无线麦克风品牌推荐

​在自媒体日益繁荣的当下&#xff0c;内容创作成为了许多人追求的目标。对于视频内容创作者而言&#xff0c;出色的内容是成功的基石&#xff0c;而高质量的设备则是保证作品品质的关键。为了提升视频音质&#xff0c;拥有一款专业的无线麦克风是不可或缺的。 然而&#xff0…

PHP智能匹配轻松预订自习室在线订座系统小程序源码

智能匹配&#xff0c;轻松预订——自习室在线订座系统 &#x1f4da;【开篇&#xff1a;告别排队&#xff0c;迎接智能学习新时代】&#x1f4da; 还在为找不到合适的自习室座位而烦恼吗&#xff1f;是不是每次去图书馆或自习室都要提前好久去排队占位&#xff1f;现在&#…

太速科技-1路万兆光纤SFP+和1路千兆网络 FMC子卡模块

1路万兆光纤SFP和1路千兆网络 FMC子卡模块 一、概述 该板卡是基于kc705和ml605的fmc 10g万兆光纤扩展板设计&#xff0c;提供了1路万兆光纤SFP和1路千兆网络接口。可搭配我公司开发的FPGA载卡使用。载卡可参考&#xff1a;ID204 SFP&#xff08;10 Gigabit Small…

AWS-亚马逊网络服务(基础服务)-AWS 定价计算器-概述与动手部署:

让我们来概述并亲身实践如何使用 AWS 定价计算器来计算 概述&#xff1a; AWS 定价计算器是 Amazon Web Services (AWS) 提供的基于 Web 的工具&#xff0c;可帮助用户估算其特定用例的 AWS 服务成本。欢迎来到雲闪世界。 它允许客户建模他们的基础设施并根据他们打算使用的…

Android 9.0 增加interface audio接口,解决编译报错

最近修改Android接口&#xff0c;报了一个VNDK的错误 我总结了如下几种方式&#xff1a; 1、直接关闭&#xff08;不推荐&#xff09;&#xff1a; 在BoardConfig.mk中加入如下两行&#xff0c;可以在编译的时候不去check VNDK&#xff0c;关掉这个可能会导致XTS某些测项跑不…

Burp Suite Professional 2024.8 for macOS x64 ARM64 - 领先的 Web 渗透测试软件

Burp Suite Professional 2024.8 for macOS x64 & ARM64 - 领先的 Web 渗透测试软件 世界排名第一的 Web 渗透测试工具包 请访问原文链接&#xff1a;https://sysin.org/blog/burp-suite-pro-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页…

Linux内核编程(十五)网络设备驱动

本文目录 一、常见的网络协议二、传输介质三、RJ-45接口 对于网络知识不太熟悉的同学可以查看这篇文章&#xff1a;计算机网络知识点详情总结。 一、常见的网络协议 TCP、UDP协议&#xff1a;详情查看-TCP、UDP系统编程。DNS协议&#xff1a;是互联网中用于将域名&#xff08…

RocketMQ集群搭建,及RocketMQ-Dashboard部署(前RocketMQ-Console)

集群搭建 RocketMQ不支持单主机搭建主从结构集群&#xff0c;当从节点启动时&#xff0c;即使和主节点设置不同的监听端口&#xff0c;他也要去监听主节点端口&#xff0c;也就是说正常启动的从节点会监听四个端口。原因未知&#xff0c;现象后面会列举出来。 1. 准备JAVA环境…

在 Ubuntu 环境下使用 VSCode 和 PlatformIO 下载程序到 Arduino Uno

安装 VSCode 访问 VSCode 官网 下载 .deb 包使用以下命令安装&#xff1a;sudo dpkg -i <下载的文件名>.deb sudo apt-get install -f安装 PlatformIO 扩展 在 VSCode 中&#xff0c;转到扩展市场&#xff08;CtrlShiftX&#xff09;搜索 “PlatformIO IDE”点击 “安装”…

STM32 HAL CAN (TJA1050CAN模块) 通讯(一)理论

1、简介 CAN具备多个设备交互的能力,但是网上大多是两个单片机进行交互,或者单片机通过CAN收发器与上位机进行交互测试,本次通过STM32cubeMX完成CAN通讯配置,并通过多个单片机进行数据交互测试。 2、CAN简介 CAN是一种串行通讯协议,主要有低速、高速CAN两种。 低速CAN…

相亲交友系统商业开发

在快节奏的现代生活中&#xff0c;寻找真爱成为了许多人的渴望。相亲交友系统&#xff0c;作为连接心灵的桥梁&#xff0c;正逐渐成为人们寻找伴侣的首选方式。我们的团队h17711347205致力于开发一款创新的相亲交友系统&#xff0c;旨在通过智能化的匹配算法&#xff0c;为用户…

Android UID 和 userID 以及 appID

我们知道Android 操作系统是基于Linux内核的&#xff0c;所以Android 的UID 是基于 Linux UID的。 Linux UID Linux 本身就是一个多用户操作系统&#xff0c;每一个用户都会有一个UID&#xff0c;不同UID 之间的资源访问是受限的。 其中&#xff0c;Linux的DAC权限模型&#…

【UE5】控件蓝图——树视图(TreeView)的基本使用

目录 前言 效果 步骤 一、显示根节点 二、显示子节点 前言 我们在视口中添加1个方块&#xff0c;2个球体&#xff0c;5个圆柱 它们在大纲视图中的层级关系如下&#xff0c;那么如何将这种层级关系显示在树视图中是本篇文章要解决的问题。 效果 步骤 一、显示根节点 1…

[STM32]从零开始的STM32标准库环境搭建(小白向)

一、我们为什么要搭建STM32标准库开发环境 如果你对STM32有一定的了解&#xff0c;相信你已经认识了STM32的几种开发方式。基于STM32寄存器开发&#xff0c;基于ST官方的标准库开发&#xff0c;基于ST官方的HAL库开发。我们现在来了解一下这些库的优缺点。首先就是基于寄存器开…

macos USB外接键盘ctrl键绑定方法 解决外接USB键盘与mac键盘不一致问题

mac电脑外接USB键盘后我们需要修改一下 ctrl键的绑定后才符合我们的使用习惯,因为标准USB键盘和mac键盘上面的ctrl键是不一样的, mac上面的 command 键 对应我们USB键盘上面的 ctrl 键. 修改方法: 偏好设置 --> 键盘 点击修饰键 后 选择键盘里面选择 USB键盘 ,然后调换…

Leetcode JAVA刷刷站(111)二叉树的最小深度

一、题目概述 二、思路方向 在Java中&#xff0c;要找出二叉树的最小深度&#xff0c;我们可以使用递归的方法。基本思路是&#xff0c;对于给定的根节点&#xff0c;如果它是空的&#xff0c;那么最小深度为0&#xff08;但实际上&#xff0c;空树没有深度&#xff0c;但在…

Sui Hacker House曼谷站报名开启:在Devcon 2024期间探索Sui区块链创新

Sui 曼谷 Hacker House 报名开启 Sui Bangkok Hacker House 将在曼谷于 2024 年 11 月 4 日至 17 日举办。诚邀开发者深入学习 Move 语言,在 Sui 网络上构建 MVP ,在充满活力的曼谷中度过难忘的两周。 诚挚地邀请开发者加入为期两周的 Sui Bangkok Hacker House。 你将与其他…

构建全景式智慧文旅生态:EasyCVR视频汇聚平台与AR/VR技术的深度融合实践

在科技日新月异的今天&#xff0c;AR&#xff08;增强现实&#xff09;和VR&#xff08;虚拟现实&#xff09;技术正以前所未有的速度改变着我们的生活方式和工作模式。而EasyCVR视频汇聚平台&#xff0c;作为一款基于云-边-端一体化架构的视频融合AI智能分析平台&#xff0c;可…

ubuntu 常见问题的收录

在使用过程中&#xff0c;发现ubuntu的问题一点不必windows少。因为每次遇到问题都需要要上网找&#xff0c;所以收集起来之后就会方便一些。 版本体验 24.04&#xff1a;整体的风格大变样&#xff0c;更趋近于“现代化”&#xff1f;反正我是更喜欢了 &#x1f604;。就着风…

C语言 ——— 宏和##的使用

目录 ##的作用 宏和##的使用 ##的作用 ##可以把位于它两边的符号合成一个符号它允许宏定义从分离的文本片段创建标识符 宏和##的使用 代码演示&#xff1a; #include<stdio.h>#define CAT(x,y) x##yint main() {int RecepInteger 2024;printf("%d\n",CAT…