力扣刷题部分笔记

Leetcode

力扣刷题笔记,记录了几个月来的题目记录,将会继续保持刷题~

2024.01

1768.交替合并字符串

创建字符串不需要声明长度(动态分配内存),push_back()可以加入某个字符,append()一般用于添加字符串,用while循环直到两个字符串都遍历完。

1071.字符串的最大公因子

一般想法就是枚举法,从两个字符串中长度小的开始从后往前比较是否相等,C++中自带辗转相除求最大公因数的函数 gcd()。

class Solution {
public:string gcdOfStrings(string str1, string str2) {//辗转相除法return (str1 + str2 == str2 + str1) ? str1.substr(0, gcd(str1.size(), str2.size())) : "";}
};

2596.检查骑士巡视方案

构建vector类型的indices存放二维棋盘所有格子,array<int,2>表示存放横纵坐标{i, j},语法基础还得熟悉下~

遍历二维棋盘,存放所有格子,故时间和空间复杂度都为O(N2)

class Solution {
public:bool checkValidGrid(vector<vector<int>>& grid) {if (grid[0][0] != 0) return false;int n = grid.size();vector<array<int, 2>> indices(n * n); //array用来接收下标i,jfor (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {indices[grid[i][j]] = {i ,j};}}for (int i = 1 ; i < indices.size(); i++) {int dx = indices[i][0] - indices[i - 1][0];int dy = indices[i][1] - indices[i - 1][1];if (abs(dx * dy) != 2) return false; //8种情况的规律dx*dy绝对值=2}return true;}
};

198.打家劫舍

动态规划入门题目,四步法

  • 定义子问题
  • 写出子问题的递推关系
  • 确定DP数组的计算顺序
  • 空间优化(可选)

从后向前找最大值,自己写的时候像跳台阶、斐波那契数列一样,空间复杂度为O(n),借用空间优化的方法,空间复杂度从O(n)降为O(1) (力扣大佬还是挺多的)

int rob(vector<int>& nums) {int prev = 0;int curr = 0;// 每次循环,计算“偷到当前房子为止的最大金额”for (int i : nums) {// 循环开始时,curr 表示 dp[k-1],prev 表示 dp[k-2]// dp[k] = max{ dp[k-1], dp[k-2] + i }int temp = max(curr, prev + i);prev = curr;curr = temp;// 循环结束时,curr 表示 dp[k],prev 表示 dp[k-1]}return curr;
}

213.打家劫舍II

思路:happy:

如果偷 nums[0],那么 nums[1] 和 nums[n−1]不能偷,问题变成从 nums[2]到 nums[n−2] 的非环形版本,调用 198 题的代码解决;
如果不偷 nums[0],那么问题变成从 nums[1]到 nums[n−1]的非环形版本,同样调用 198 题的代码解决。
这两种方案覆盖了所有情况(毕竟 nums[0]只有偷与不偷,没有第三种选择),所以取两种方案的最大值,即为答案。

class Solution {// 198. 打家劫舍int rob1(vector<int> &nums, int start, int end) { // [start,end) 左闭右开int f0 = 0, f1 = 0;for (int i = start; i < end; ++i) {int new_f = max(f1, f0 + nums[i]);f0 = f1;f1 = new_f;}return f1;}
public:int rob(vector<int> &nums) {int n = nums.size();return max(nums[0] + rob1(nums, 2, n - 1), rob1(nums, 1, n));}
};

337.打家劫舍III

代码随想录之递归三部曲和动规五部曲

1.确定递归函数的参数和返回值

这里我们要求一个节点 偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组。

别忘了在递归的过程中,系统栈会保存每一层递归的参数

vector<int> robTree(TreeNode* cur) {

2.确定终止条件

在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回

if (cur == NULL) return vector<int>{0, 0};

这也相当于dp数组的初始化

3.确定遍历顺序

// 下标0:不偷,下标1:偷
vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右
// 中

4.确定单层递归的逻辑

vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右// 偷cur
int val1 = cur->val + left[0] + right[0];
// 不偷cur
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};

5.举例推导dp数组

最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱

class Solution {
public:int rob(TreeNode* root) {vector<int> result = robTree(root);return max(result[0], result[1]);}vector<int> robTree(TreeNode* cur) {if (cur == nullptr) return vector<int>{0, 0};vector<int> left = robTree(cur->left);vector<int> right = robTree(cur->right);int val1 = cur->val + left[0] + right[0];int val2 = max(left[0], left[1]) + max(right[0], right[1]);return {val1, val2};}
}

时间复杂度:O(n),每个结点只遍历一次

空间复杂度:O(log n),算上递推系统栈的空间

2591.将钱分给最多的儿童

仔细审题+考虑清楚!!!

给你一个整数 money ,表示你总共有的钱数(单位为美元)和另一个整数 children ,表示你要将钱分配给多少个儿童。

你需要按照如下规则分配:

  • 所有的钱都必须被分配。
  • 每个儿童至少获得 1 美元。
  • 没有人获得 4 美元。

请你按照上述规则分配金钱,并返回 最多 有多少个儿童获得 恰好 8 美元。如果没有任何分配方案,返回 -1

class Solution {
public:int distMoney(int money, int children) {money -= children; // 每人至少 1 美元if (money < 0) return -1;int ans = min(money / 7, children); // 初步分配,让尽量多的人分到 8 美元money -= ans * 7;children -= ans;if (children == 0 && money || // 必须找一个前面分了 8 美元的人,分完剩余的钱children == 1 && money == 3) // 不能有人恰好分到 4 美元--ans;return ans;}
};

146.LRU缓存

class Node {
public:int key, value;Node *prev, *next;Node(int k = 0, int v = 0) : key(k), value(v) {}
};class LRUCache {
private:int capacity;Node *dummy; // 哨兵节点unordered_map<int, Node*> key_to_node;// 删除一个节点(抽出一本书)void remove(Node *x) {x->prev->next = x->next;x->next->prev = x->prev;}// 在链表头添加一个节点(把一本书放在最上面)void push_front(Node *x) {x->prev = dummy;x->next = dummy->next;x->prev->next = x;x->next->prev = x;}Node *get_node(int key) {auto it = key_to_node.find(key);if (it == key_to_node.end()) // 没有这本书return nullptr;auto node = it->second; // 有这本书remove(node); // 把这本书抽出来push_front(node); // 放在最上面return node;}public:LRUCache(int capacity) : capacity(capacity), dummy(new Node()) {dummy->prev = dummy;dummy->next = dummy;}int get(int key) {auto node = get_node(key);return node ? node->value : -1;}void put(int key, int value) {auto node = get_node(key);if (node) { // 有这本书node->value = value; // 更新 valuereturn;}key_to_node[key] = node = new Node(key, value); // 新书push_front(node); // 放在最上面if (key_to_node.size() > capacity) { // 书太多了auto back_node = dummy->prev;key_to_node.erase(back_node->key);remove(back_node); // 去掉最后一本书delete back_node; // 释放内存}}
};/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/

151.反转字符串中的单词

双指针解法,快慢指向处理字符串(数组)问题。中等难度,当时做没考虑全😂

class Solution {
public:void reverse(string& s, int start, int end){ //翻转单词,区间写法:左闭右闭 []for (int i = start, j = end; i < j; i++, j--) {swap(s[i], s[j]);}}void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.htmlfor (int i = 0; i < s.size(); ++i) { //if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。s[slow++] = s[i++];}}}s.resize(slow); //slow的大小即为去除多余空格后的大小。}string reverseWords(string s) {removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。reverse(s, 0, s.size() - 1);int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。for (int i = 0; i <= s.size(); ++i) {if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。start = i + 1; //更新下一个单词的开始下标start}}return s;}
};

334.递增的三元子序列

思路 巧妙!

首先,新建两个变量 small 和 mid ,分别用来保存题目要我们求的长度为 3 的递增子序列的最小值和中间值。

接着,我们遍历数组,每遇到一个数字,我们将它和 small 和 mid 相比,若小于等于 small ,则替换 small;否则,若小于等于 mid,则替换 mid;否则,若大于 mid,则说明我们找到了长度为 3 的递增数组!

上面的求解过程中有个问题:当已经找到了长度为 2 的递增序列,这时又来了一个比 small 还小的数字,为什么可以直接替换 small 呢,这样 small 和 mid 在原数组中并不是按照索引递增的关系呀?

Trick 就在这里了!假如当前的 small 和 mid 为 [3, 5],这时又来了个 1。假如我们不将 small 替换为 1,那么,当下一个数字是 2,后面再接上一个 3 的时候,我们就没有办法发现这个 [1,2,3] 的递增数组了!也就是说,我们替换最小值,是为了后续能够更好地更新中间值!

另外,即使我们更新了 small,这个 small 在 mid 后面,没有严格遵守递增顺序,但它隐含着的真相是,有一个比 small 大比 mid 小的最小值出现在 mid 之前。因此,当后续出现比 mid 大的值的时候,我们一样可以通过当前 small 和 mid 推断的确存在着长度为 3 的递增序列。所以,这样的替换并不会干扰我们后续的计算!

class Solution {
public:bool increasingTriplet(vector<int>& nums) {int len = nums.size();if (len < 3) return false;int small = INT_MAX, mid = INT_MAX;for (auto num : nums) {if (num <= small) {small = num;} else if (num <= mid) {mid = num;} else if (num > mid) {return true;}}return false;    }
};

443.压缩字符串

思路:利用双指针,一个表示修改后数组长度,另一个表示指向当前数组第一个位置的序号,临界条件是遍历数组时前后元素不一样,后用to_string()将整型数组转换为字符串类型。

class Solution {
public:int compress(vector<char>& chars) {int n = chars.size();//双指针,一个表示修改后数组长度,另一个表示指向的数组第一个位置int len = 0, left = 0;for (int i = 0; i < n; i++) {if (i == n - 1 || chars[i] != chars[i + 1]) {chars[len++] = chars[i];int nums = i - left + 1;if (nums > 1) {for (char num : to_string(nums)) {chars[len++] = num;}}left = i + 1;}}return len; }
};

复杂度分析

时间复杂度:O(n),其中 n 为字符串长度,我们只需要遍历该字符串一次。

空间复杂度:O(1),我们只需要常数的空间保存若干变量。

392.判断子序列

判断s是否为t的子序列,动态规划求解~

class Solution {
public:bool isSubsequence(string s, string t) {int n = s.size(), m = t.size();vector<vector<int> > f(m + 1, vector<int>(26, 0));for (int i = 0; i < 26; i++) {f[m][i] = m;}for (int i = m - 1; i >= 0; i--) {for (int j = 0; j < 26; j++) {if (t[i] == j + 'a')f[i][j] = i;elsef[i][j] = f[i + 1][j];}}int add = 0;for (int i = 0; i < n; i++) {if (f[add][s[i] - 'a'] == m) {return false;}add = f[add][s[i] - 'a'] + 1;}return true;}
};

1004.最大连续1的个数III

为了获取最大连续1的个数,会将0进行翻转,所以会用滑动窗口,但是大小如何知道,可以结合左右指针动态遍历,判断0的个数是否大于条件中的k,最后再进行取最大值

claas Solution {
public:int longestOnes(vector<int>& nums, int k) {int res = 0, zeros = 0, left = 0;for (int right = 0; right < nums.size(); right++) {if (nums[right] == 0) zeros++;while (zeros > k) { //找到需要定位的leftif (nums[left++] == 0) zeros--;}res = max(res, right - left + 1);}return res;}
}

1207.独一无二的出现次数

仔细读懂题目的意思后,我立刻就想到了map和set,map可以很容易统计不同数字的数量,set可以把相同数量合并,这样直接比较最后set和map的大小就可以,代码如下

class Solution {
public:bool uniqueOccurrences(vector<int>& arr) {map<int,int> mp;for(int i = 0; i < arr.size(); i ++){mp[arr[i]] ++;}set<int> res;for(auto m : mp){res.insert(m.second);}return res.size() == mp.size();}
};
  • 时间复杂度:O(N),其中N为数组的长度,遍历原始数组需要O(N),遍历过程中产生哈希表又需要O(N)
  • 空间复杂度:O(N)

2390.从字符串中移除星号

模拟栈操作

class Solution {
public:string removeStars(string s) {string res;for (auto c : s) {if (c != '*') res.push_back(c);else res.pop_back();}return res;}
};
  • 时间和空间复杂度O(N),若要空间复杂度O(1),即原地操作,则可以使用双指针代替
class Solution {
public:string removeStars(string s) {int n = s.size();int l = 0;for (int i = 0; i < n; i++) {if (s[i] != '*') {swap(s[i], s[l++]);} else {l--;}}return s.substr(0, l);}
};

394.字符串解码

class Solution {
public:string decodeString(string s) {string res = "";stack <int> nums;stack <string> strs;int num = 0;int len = s.size();for(int i = 0; i < len; ++ i){if(s[i] >= '0' && s[i] <= '9'){num = num * 10 + s[i] - '0';}else if((s[i] >= 'a' && s[i] <= 'z') ||(s[i] >= 'A' && s[i] <= 'Z')){res = res + s[i];}else if(s[i] == '[') //将‘[’前的数字压入nums栈内, 字母字符串压入strs栈内{nums.push(num);num = 0;strs.push(res); res = "";}else //遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律{int times = nums.top();nums.pop();for(int j = 0; j < times; ++ j)strs.top() += res; //strs.top()是作为一个整体的元素res = strs.top(); //之后若还是字母,就会直接加到res之后,因为它们是同一级的运算;若是左括号,res会被压入strs栈,作为上一层的运算strs.pop();}}return res;}
};

649.Dota2 参议院

队列,R和D一般都会选择消除后面的不同变量,则可以根据序号进行比较(可能涉及多轮比较,故比较完的会放入队列尾部)

class Solution {
public:string predictPartyVictory(string senate) {int n = senate.size();queue<int> radiant, dire; //队列是先进先出(FIFO)for (int i = 0; i < n; ++i) {if (senate[i] == 'R') {radiant.push(i);}else {dire.push(i);}}while (!radiant.empty() && !dire.empty()) { //不断比较直到非空if (radiant.front() < dire.front()) {radiant.push(radiant.front() + n);}else {dire.push(dire.front() + n);}radiant.pop();dire.pop();}return !radiant.empty() ? "Radiant" : "Dire";}
};
  • 时间复杂度O(N) while和for循环,N为字符串长度,循环并非嵌套,实际上为2*N
  • 空间复杂度O(N) 生成辅助队列插入元素,N为原字符串长度

2095.删除链表的中间节点

//官方题解
class Solution {
public:ListNode* deleteMiddle(ListNode* head) {if (head->next == nullptr) {return nullptr;}ListNode* slow = head;ListNode* fast = head;ListNode* pre = nullptr;while (fast && fast->next) {fast = fast->next->next;pre = slow;slow = slow->next;}pre->next = pre->next->next;return head;}
};
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* deleteMiddle(ListNode* head) {ListNode* tmp = head;int cnt = 0;if (head == nullptr || head->next == nullptr) return nullptr;while (tmp != nullptr) {tmp = tmp->next;cnt++;}int mid = cnt / 2;//对于中间位置的结点删除ListNode* p = head;for  (int i = 0; i < mid; i++) { p = p->next;          if (mid < 2) {head->next = head->next->next;break;}//待删除结点的前一个结点if (i == mid - 2) {p->next = p->next->next;}    }return head;}
};

对于链表的操作,直接将head进行遍历输出,最后为[],若用tmp,最后输出原链表;所以只有用到指向操作时才会对原来的链表进行变化,上面的p。知识遗忘很正常,需要反复总结哈。

2024.02

872.叶子相似的树

深度优先搜索算法,遍历叶结点,用数组保存数字值,最后比较vector中元素是否相同(只是需要比较所有元素)

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:void dfs(TreeNode* root, vector<int>& seq) {if (root == nullptr) return;if (root->left == nullptr && root->right == nullptr) return seq.push_back(root->val);dfs(root->left, seq);dfs(root->right, seq);}bool leafSimilar(TreeNode* root1, TreeNode* root2) {//深度优先搜索,可以获取遍历次数,如何获取末尾值~ (引入数组vector记录元素)vector<int> seq1, seq2;dfs(root1, seq1);dfs(root2, seq2);return seq1 == seq2;}
};

时间复杂度:为O(N1+N2),其中N1与N2为树的结点数量

空间复杂度:为O(L1+L2),L1+L2为叶子数量,递归空间取决于树的高度

同步遍历方法,利用栈来保存辅助元素值,

class Solution {
public:bool leafSimilar(TreeNode* root1, TreeNode* root2) {stack<TreeNode*> s1, s2; //栈中保存有左右子树的右节点,为了比较顺序结点异同s1.push(root1), s2.push(root2);while(!s1.empty() && !s2.empty()){TreeNode* node1 = s1.top(); s1.pop();TreeNode* node2 = s2.top(); s2.pop();while (node1->left || node1->right){if (node1->left){if (node1->right) s1.push(node1->right);node1 = node1->left;}elsenode1 = node1->right;}//同样的操作对树2进行一遍while (node2->left || node2->right){if (node2->left){if (node2->right) s2.push(node2->right);node2 = node2->left;}elsenode2 = node2->right;}//此时node1与node2分别指向树1与树2的叶子节点if (node1->val != node2->val) return false;}//到此两种情况://1. 两个栈都空了,并且叶子节点都相等,因此返回true//2. 只有一颗树空了,证明另一棵树一定还有别的叶子节点, 返回false;return s1.empty() && s2.empty();}
};

1372.二叉树中的最长交错路径

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int maxAns;/* 0 => left, 1 => right */void dfs(TreeNode* o, bool dir, int len) {maxAns = max(maxAns, len);if (!dir) {if (o->left) dfs(o->left, 1, len + 1);if (o->right) dfs(o->right, 0, 1);} else {if (o->right) dfs(o->right, 0, len + 1);if (o->left) dfs(o->left, 1, 1);}} int longestZigZag(TreeNode* root) {if (!root) return 0;maxAns = 0;dfs(root, 0, 0);dfs(root, 1, 0);return maxAns;}
};

1161.最大层内元素和

利用广度优先搜索方法,结合队列(递归也可,一般方法的通用模板可以记下)

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int maxLevelSum(TreeNode* root) {queue <TreeNode* > que;que.push(root);int max_val = INT_MIN;int a = 1; //记录变化层数int res = 1; //记录返回层数while (!que.empty()) {int t = 0;int num = que.size();for (int i = 0; i < num; i++) {TreeNode* p = que.front();t += p->val;if (p->left) que.push(p->left);if (p->right) que.push(p->right);que.pop();}if (max_val < t) {res = a;max_val = t;}a++;}return res;}
};

1926.迷宫中离入口最近的出口

广度优先搜索算法

class Solution {
public:int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {// 思路:从入口开始按轮BFS,遇到邻居为“.”则入队,并记录轮数// 结束条件时,邻居为边缘格子,返回轮数// 若BFS结束也没有找到出口,返回-1const int m = maze.size();const int n = maze[0].size();const int dr[4] = {0, 0, 1, -1};const int dc[4] = {1, -1, 0, 0};queue<pair<int, int>> que;// 初始化int erow = entrance[0], ecol = entrance[1];que.emplace(erow, ecol);maze[erow][ecol] = '-';  // 表示已经搜索过// 按轮BFSint epoch = 0;while (!que.empty()) {int counter = que.size();epoch++;// 一轮for (int k = 0; k < counter; k++) {auto [r, c] = que.front();que.pop();// 邻居找'.'入队for (int i = 0; i < 4; i++) {int nr = r + dr[i], nc = c + dc[i];if (nr >= 0 && nr < m && nc >= 0 && nc < n && maze[nr][nc] == '.') {// 是边沿直接返回if (nr == 0 || nr == m - 1 || nc == 0 || nc == n - 1) return epoch;// 不是边沿,入队maze[nr][nc] = '-';  // 表示已经搜过que.emplace(nr, nc); }}}}return -1;}
};

841.钥匙和房间

class Solution {
public:bool canVisitAllRooms(vector<vector<int>>& rooms) {vector<bool> visited(rooms.size(), false);dfs(rooms, 0, visited);//检查是否都访问到了for (int i : visited) {if (i == false) return false;}return true;}void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {if (visited[key]) {return;}visited[key] = true;vector<int> keys = rooms[key];for (int key : keys) {//深度优先搜索遍历dfs(rooms, key, visited);}}
};

图的深度优先搜索,

  • 时间复杂度:O(n+m),其中n是房间的数量,m是所有房间中的钥匙数量的总数
  • 空间复杂度:O(n),n是房间的数量,主要为栈空间的开销

994.腐烂的橘子

class Solution {
public:int orangesRotting(vector<vector<int>>& grid) {int min = 0, fresh = 0;queue<pair<int, int>> q;    //队列元素对for (int i = 0; i < grid.size(); i++) {for (int j = 0; j < grid[0].size(); j++) {if (grid[i][j] == 1) fresh++;else if (grid[i][j] == 2) q.push({i, j});}}vector<pair<int, int>> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};while (!q.empty()) {int n = q.size();bool rotten = false;for (int i = 0; i < n; i++) {auto x = q.front();q.pop();for (auto cur : dirs) {int i = x.first + cur.first;int j = x.second + cur.second;if (i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == 1) {grid[i][j] = 2;q.push({i, j});fresh--;rotten = true;}}}if (rotten) min++;}return fresh ? -1 : min;}
};

复杂度分析:

  • 时间复杂度:O(nm),即进行一次广度优先搜索的时间,其中n=grid.size(),m=grid[0].size()。
  • 空间复杂度:O(nm),需要额外记录数据,且队列存放的状态最多nm个。

17.电话号码的字母组合

class Solution {
private:const string letterMap[10] = {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs", // 7"tuv", // 8"wxyz", // 9};
public:vector<string> result;string s;void backtracking(const string& digits, int index) {if (index == digits.size()) {result.push_back(s);return;}int digit = digits[index] - '0';        // 将index指向的数字转为intstring letters = letterMap[digit];      // 取数字对应的字符集for (int i = 0; i < letters.size(); i++) {s.push_back(letters[i]);            // 处理backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了s.pop_back();                       // 回溯}}vector<string> letterCombinations(string digits) {s.clear();result.clear();if (digits.size() == 0) {return result;}backtracking(digits, 0);return result;}
};

Tips:回溯三部曲,1、确定回溯函数参数;2、确定终止条件;3、确定单层遍历逻辑

(1中index表示遍历第一个数字,字符串s用来保存返回字符串(一层层),2中index=digits.size(),3中确定了处理递归回溯操作,作为遍历逻辑)

2023.03

3.无重复字符的最长字串

图片.png

滑动窗口解法:时间和空间复杂度都是O(N)

int lengthOfLongestSubstring(string s) {vector<int> m(128, 0);int ans = 0;int i = 0;for (int j = 0; j < s.size(); j++) {i = max(i, m[s[j]]);	//重复字母的下一个位置m[s[j]] = j + 1;ans = max(ans, j - i + 1);	//无重复的最长字串}return ans;}

正接下题:

int lengthOfLongestSubstring(string s) {unordered_map<char, int> window;int left = 0, right = 0;int res = 0; // 记录结果while (right < s.size()) {char c = s[right];right++;// 进行窗口内数据的一系列更新window[c]++;// 判断左侧窗口是否要收缩,是否有重复字母while (window[c] > 1) {char d = s[left];left++;// 进行窗口内数据的一系列更新window[d]--;}// 在这里更新答案res = max(res, right - left);}return res;
}

438.找到字符串中所有字母异位词

img

好诗好诗(手动狗头)

大致框架如下:

int left = 0, right = 0;
while (right < s.size()) {// 增大窗口window.add(s[right]);right++;while (window needs shrink) {// 缩小窗口window.remove(s[left]);left++;}
}	//时间复杂度O(N)

利用以上框架,整体代码如下

vector<int> findAnagrams(string s, string t) {unordered_map<char, int> need, window;for (char c : t) need[c]++;int left = 0, right = 0;int valid = 0;vector<int> res; // 记录结果while (right < s.size()) {char c = s[right];right++;// 进行窗口内数据的一系列更新if (need.count(c)) {window[c]++;if (window[c] == need[c]) valid++;}// 判断左侧窗口是否要收缩while (right - left >= t.size()) {// 当窗口符合条件时,把起始索引加入 resif (valid == need.size())res.push_back(left);char d = s[left];left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d])valid--;window[d]--;}}}return res;
}

果然还是有方法的,暴力硬解不是好法子,而且还不一定解决!

239.滑动窗口最大值

hard难度,题意好理解,有效实现不容易

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {MyQueue que;vector<int> res;int n = nums.size();for (int i = 0; i < k; i++) {que.push(nums[i]);}for (int i = k; i < n; i++) {res.push_back(que.front());que.pop(nums[i - k]); //移除滑动窗口第一个元素que.push(nums[i]); //加入滑动窗口后一个元素}res.push_back(que.front()); //最后一次放入return res;}private:class MyQueue { //单调队列(从大到小)public:deque<int> que; //使用deque来实现单调队列//每次弹出的时候判断要弹出的数值是否等于队列出口元素的数值,如果相等则弹出//同时pop之前判断队列当前是否为空void pop(int value) {if (!que.empty() && value == que.front()) {que.pop_front();}}//如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值//这样就保持了队列里的数值是单调从大到小的void push(int value) {while (!que.empty() && value > que.back()) {que.pop_back();}que.push_back(value);}//保持当前队列里的最大值 直接返回队列前端也就是front就可以了int front() {return que.front();}};
};

76.最小覆盖子串

class Solution {
public:string minWindow(string s, string t) {unordered_map<char, int> hs, ht;for (auto c : t) ht[c]++;int cnt = 0; //用于记录匹配字符数string res; //最后返回的字符串for (int i = 0, j = 0; i < s.size(); i++) {hs[s[i]]++;if (hs[s[i]] <= ht[s[i]]) {cnt++;}//滑动窗口处理,双指针(j为前端指针,i为后端指针)while (hs[s[j]] > ht[s[j]]) {hs[s[j]]--;j++;}//在满足条件的寻找最短字符串if (cnt == t.size()) {if (res.empty() || i - j + 1 < res.size()) res = s.substr(j, i - j + 1);}}return res;}
};

双指针遍历,严格递增最多移动n次,总的时间复杂度为O(N)。

56.合并区间

class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {vector<vector<int>> res;if (intervals.size() == 0) return res; // 区间集合为空直接返回// 排序的参数使用了lambda表达式sort(intervals.begin(), intervals.end(), [](const vector<int> a, const vector<int> b){return a[0] < b[0];});// 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并res.push_back(intervals[0]);for (int i = 1; i < intervals.size(); i++) {if (res.back()[1] >= intervals[i][0]) {// 合并区间,只更新右边界就好,因为result.back()的左边界一定是最小值,因为我们按照左边界排序的res.back()[1] = max(res.back()[1], intervals[i][1]);}else res.push_back(intervals[i]);}return res;}
};
  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(logN),排序需要用到的空间开销

238.除自身以外数组的乘积

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int length = nums.size();vector<int> answer(length);// answer[i] 表示索引 i 左侧所有元素的乘积// 因为索引为 '0' 的元素左侧没有元素, 所以 answer[0] = 1answer[0] = 1;for (int i = 1; i < length; i++) {answer[i] = nums[i - 1] * answer[i - 1];}// R 为右侧所有元素的乘积// 刚开始右边没有元素,所以 R = 1int R = 1;for (int i = length - 1; i >= 0; i--) {// 对于索引 i,左边的乘积为 answer[i],右边的乘积为 Ranswer[i] = answer[i] * R;// R 需要包含右边所有的乘积,所以计算下一个结果时需要将当前值乘到 R 上R *= nums[i];}return answer;}
};

复杂度分析

  • 时间复杂度:O(N),其中N指的是数组nums的大小
  • 空间复杂度:O(1),输出数组不算进复杂度中,因此我们只需要常数的空间存放变量

41.缺失的第一个正数

难度为困难,不容易想到

置换法,nums[0] = 0+1, nums[1] = 1+1,~

class Solution {
public:int firstMissingPositive(vector<int>& nums) {int n = nums.size();for (int i = 0; i < n; ++i) {while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {swap(nums[nums[i] - 1], nums[i]);}}for (int i = 0; i < n; ++i) {if (nums[i] != i + 1) {return i + 1; //寻找是否有空缺位置}}return n + 1;}
};

时间:O(N),N为数组的长度;空间:O(1)

160.相交链表

方法一:哈希集合

先遍历链表headA,并将链表headA中的结点加入到哈希集合中,然后遍历链表headB,对于遍历到的每个结点,判断该结点是否在哈希集合中

class Solution
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {unorder_set<ListNode *> visited;ListNode *temp = headA;while (temp != nullptr) {visited.insert(temp);temp = temp->next;}temp = headB;while (temp != nullptr) {if (visited.count(temp)) {return temp;}temp = temp->next;}return nullptr;}
};

时间复杂度:O(m+n),其中m和n分别是链表headA和headB的长度,需要遍历两个链表各一次

空间复杂度:O(m),其中m是链表headA的长度,需要使用哈希集合存储链表headA中的全部节点

方法二:双指针

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if (headA == nullptr && headB == nullptr) return nullptr;ListNode *A = headA, *B = headB;while (A != B) { //a+c+b=a+b+cA = A != nullptr? A->next: headB;B = B != nullptr? B->next: headA;}return A; }
};

时间复杂度O(m+n),空间复杂度为O(1).

141.环形链表

class Solution {
public:bool hasCycle(ListNode *head) {//判断是否存在环,双指针ListNode *fast = head;ListNode *slow = head;while (fast != nullptr) {fast = fast->next;if (fast != nullptr) fast = fast->next;if (fast == slow) return true;//存在pos=-1的不合规情况slow = slow->next;}return false;}
};

双指针法

  • 时间复杂度:O(N),其中N是链表中的结点数,当链表中不存在环时,快指针先于慢指针到达链表尾部,链表中的每个结点至多被访问两次;当链表中存在环时,每一轮移动后,快慢指针距离将减小一,而初始距离为环的长度,因此至多移动N轮。
  • 空间复杂度:O(1),我们只使用了两个指针的额外空间。

2.两数相加

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode *dummy = new ListNode(); // 哨兵结点ListNode *cur = dummy;int carry = 0; // 进位while (l1 || l2 || carry) { // 有一个不是空结点,或者还有进位,就继续迭代carry += (l1 ? l1->val : 0) + (l2 ? l2->val : 0); // 结点值和进位相加cur = cur->next = new ListNode(carry % 10); // 每个结点保存一个数位carry /= 10; // 新的进位if (l1) l1 = l1->next; // 下一个结点if (l2) l2 = l2->next; // 下一个结点}return dummy->next; // 哨兵结点地下一个结点就是头结点}
};

复杂度分析:

  • 时间复杂度:O(n),n为l1长度和l2长度地最大值
  • 空间复杂度:O(1),返回值不计入

还有一个递归法,灵茶山艾府666!

class Solution {
public:// l1 和 l2 为当前遍历的节点,carry 为进位ListNode *addTwoNumbers(ListNode *l1, ListNode *l2, int carry = 0) {if (l1 == nullptr && l2 == nullptr) // 递归边界:l1 和 l2 都是空节点return carry ? new ListNode(carry) : nullptr; // 如果进位了,就额外创建一个节点if (l1 == nullptr) // 如果 l1 是空的,那么此时 l2 一定不是空节点swap(l1, l2); // 交换 l1 与 l2,保证 l1 非空,从而简化代码carry += l1->val + (l2 ? l2->val : 0); // 节点值和进位加在一起l1->val = carry % 10; // 每个节点保存一个数位l1->next = addTwoNumbers(l1->next, (l2 ? l2->next : nullptr), carry / 10); // 进位return l1;}
};

138.随机链表的复制

class Solution {
public:Node* copyRandomList(Node* head) {if(head == nullptr) return nullptr;Node* cur = head;unordered_map<Node*, Node*> map;// 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射while(cur != nullptr) {map[cur] = new Node(cur->val);cur = cur->next;}cur = head;// 4. 构建新链表的 next 和 random 指向while(cur != nullptr) {map[cur]->next = map[cur->next];map[cur]->random = map[cur->random];cur = cur->next;}// 5. 返回新链表的头节点return map[head];}
};

应用哈希表的方法,相比普通链表的构造,多了random指针,类似方法进行遍历

复杂度分析:

  • 时间复杂度O(N):两轮遍历链表,使用O(N)时间
  • 空间复杂度O(N):哈希表dic使用线性大小的额外空间

94.二叉树的中序遍历

1、递归法

首先确定递归函数的参数和返回值,其次确定终止条件,最后确定单层递归的逻辑

class Solution {
public:vevtor<int> inorderTraversal (TreeNode* root) {vector<int> res;inorder(root, res);return res;}// 确定递归函数的参数和返回值void inorder(TreeNode* root, vector<int>& res) {// 确定终止条件if (root == nullptr) return;// 确定单层递归的逻辑,中序遍历是左中右inorder(root->left, res);res.push_back(root->val);inorder(root->right, res);}
};

2、迭代法

递归的实现是每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数。

递归,就是在运行的过程中调用自己;迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。

class Solution {
public:vectot<int> inorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> result;TreeNode* cur = root;while (cur != nullptr || !st.empty()) {if (cur != nullptr) {st.push(cur);cur = cur->left;} else {cur = st.top();st.pop();result.push_back(cur->val);cur = cur->right;}}return result;}
};

200.岛屿数量

关于图论,了解一下就行,笔试大概率遇不到

深度优先搜索:

class Solution {
private:int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; //四个方向void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {for (int i = 0; i < 4; i++) {int nextx = x + dir[i][0];int nexty = y + dir[i][1];if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; //越界了,直接跳过if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { //没有访问过的,同时是陆地visited[nextx][nexty] = true;dfs(grid, visited, nextx, nexty);}}}
public:int numIslands(vector<vector<char>>& grid) {int n = grid.size(), m = grid[0].size();vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));int result = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (!visited[i][j] && grid[i][j] == '1') {visited[i][j] = true;result++; //遇到没访问过的陆地,+1dfs(grid, visited, i, j);}}}return result;}
};

广度优先搜索:

class Solution {
private:int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; //四个方向void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {queue<pair<int, int>> que;que.push({x, y});visited[x][y] = true; //只要加入队列,立刻标记while (!que.empty()) {pair<int, int> cur = que.front(); que.pop();int curx = cur.first;int cury = cur.second;for (int i = 0; i < 4; i++) {int nextx = curx + dir[i][0];int nexty = cury + dir[i][0];if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; //越界了,直接退出if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') {que.push({nextx, nexty});visited[nextx][nexty] = true; //只要加入队列立刻标记}}}}
public:int numIslands(vector<vector<char>>& grid) {int n = grid.size(), m = grid[0].size();vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));int result = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (!visited[i][j] && grid[i][j] == '1') {visited[i][j] = true;result++; //遇到没访问过的陆地,+1bfs(grid, visited, i, j); //将与其连接的陆地都标记上true}}}return result;}
};

543.二叉树的直径

class Solution {
public:int ans = 0;int diameterOfBinaryTree(TreeNode* root) {// 注意路径可能不经过根节点,所以像下面相加的有小问题(ac95%),错了才知道QAQ// if (root->left == nullptr & root->right == nullptr) return 0;// // 二叉树根节点深度改版,左右结点最大深度之和// return dfs(root->left) + dfs(root->right);dfs(root);return ans;}int dfs(TreeNode* cur) {if (cur == nullptr) return -1; //根节点路径为0int l = dfs(cur->left) + 1;int r = dfs(cur->right) + 1;ans = max(ans, l + r);return max(l, r);}
};
  • 时间复杂度:O(N),其中N为二叉树的结点个数。
  • 空间复杂度:O(N),最坏情况下,二叉树退化成一条链,递归需要O(N)的栈空间。

102.二叉树的层序遍历

套用层序遍历模板,广度优先搜索BFS

class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<vector<int>> result;while (!que.empty()) {int size = que.size();vector<int> vec;// 这里使用固定大小size,不要使用que.size(),因为它不断变化for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop(); // 弹出队首元素vec.push_back(node->val);if (node->left) que.push(node->left);if (node->right) que.push(node->right);}result.push_back(vec);}return result;}
};

递归法,亦可称作深度优先搜索DFS

class Solution {
public:void order(TreeNode* cur, vector<vector<int>>& result, int depth){if (cur == nullptr) return;// 创建不同depth的result[depth]if (result.size() == depth) result.push_back(vector<int>());result[depth].push_back(cur->val);order(cur->left, result, depth + 1);order(cur->right, result, depth + 1);}vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;int depth = 0;order(root, result, depth);return result;}
};

时间空间复杂度为O(N),N为二叉树结点数量。

2024.04

105.从前序和中序遍历序列构造二叉树

class Solution {
private:unordered_map<int, int> index;public:TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {if (preorder_left > preorder_right) {return nullptr;}// 前序遍历中的第一个节点就是根节点int preorder_root = preorder_left;// 在中序遍历中定位根节点int inorder_root = index[preorder[preorder_root]];// 先把根节点建立出来TreeNode* root = new TreeNode(preorder[preorder_root]);// 得到左子树中的节点数目int size_left_subtree = inorder_root - inorder_left;// 递归地构造左子树,并连接到根节点// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);// 递归地构造右子树,并连接到根节点// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int n = preorder.size();// 构造哈希映射,帮助我们快速定位根节点for (int i = 0; i < n; ++i) {index[inorder[i]] = i;}return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);}
};

437.路经总和III

非自顶向下方法,任意结点开始(并非都是根节点)

class Solution {
public:int path = 0;int pathSum(TreeNode* root, int targetSum) {if (!root) return 0;dfs(root, targetSum);// 递归左子树和右子树pathSum(root->left, targetSum);pathSum(root->right, targetSum);return path;}void dfs(TreeNode* root, long target) {// 递归左右结点if (root == nullptr) return;target -= root->val;if (target == 0) path++;dfs(root->left, target);dfs(root->right, target);}
};

236.二叉树的最近公共祖先

Picture2.png

class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {// 寻找最近公共祖先,DFS根节点,如何找到最近的,一种情况是root就为p或q或者root为空,此时返回root// 1 终止条件if (root == nullptr || root == p || root == q) return root;// 2 递推工作TreeNode* left = lowestCommonAncestor(root->left, p, q);TreeNode* right = lowestCommonAncestor(root->right, p ,q);// 3 返回值// 情况1,left和right同时为空,此时并不包含p和qif (left == nullptr && right == nullptr) return nullptr;// 情况3,left为空,right不为空,p和q都不在root左子树中,直接返回rightif (left == nullptr) return right;// 情况4与3同理if (right == nullptr) return left;// 情况2,left和right不同时为空,p和q分别位于root两侧,返回rootreturn root;}
};

复杂度分析:

  • 时间复杂度O(N):其中N为二叉树结点树:最差的情况下,需要递归遍历树的所有结点
  • 空间复杂度O(N):最差情况下,递归深度达到N,系统使用O(N)大小的额外空间

124.二叉树中的最大路径和

非自顶向下方法(树根节点不一定在路径中)

left,right分别为根节点左右子树最大路径和,注意:如果最大路径和<0,意味着该路径和对总路径和做负贡献,因此不要计入到总路径中,将它设置为0

class Solution {
public:int ans = INT_MIN; // 注意节点值可能为负数,因此要设置为最小值int maxPathSum(TreeNode* root) {maxPath(root);return ans;}int maxPath(TreeNode* root){ // 以root为路径起始点的最长路径if (!root) return 0;int left = max(maxPath(root->left), 0);int right = max(maxPath(root->right), 0);ans = max(root->val + left + right, ans);return max(left + root->val, right + root->val); // 返回左右子树较长的路径加上根节点值}
};

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

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

相关文章

MySQL基础【语句执行顺序】

一个SQL语句它的执行顺序对于我们思考题意有着很重要的关系 题意就是&#xff1a;找出哪些只逛超市不买单的人&#xff08;买单0元也算哦&#xff0c;可能是使用的是代金券吧&#xff09; 看到此题关键找出两个数据 参观过的人 和 买单的人 他们的差就是白嫖的人&#xff08;支…

包子凑数【蓝桥杯】/完全背包

包子凑数 完全背包 完全背包问题和01背包的区别就是&#xff0c;完全背包问题每一个物品能取无限次。 思路&#xff1a;当n个数的最大公约数不为1&#xff0c;即不互质时&#xff0c;有无限多个凑不出来的&#xff0c;即n个数都可以表示成kn&#xff0c;k为常数且不为1。当n个…

【美团笔试题汇总】2023-09-02-美团春秋招笔试题-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新美团近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…

31.2k star, 免费开源的白板绘图工具 tldraw

31.2k star, 免费开源的白板绘图工具 tldraw 分类 开源分享 项目名: tldraw -- 无限画布白板 Github 开源地址&#xff1a; https://github.com/tldraw/tldraw 在线测试地址&#xff1a; tldraw 文档地址&#xff1a; tldraw SDK tldraw 是一款开源免费的无限画布白板&…

HTML5动画设计工具 Hype 4 Pro v4.1.14中文激活版

Hype Pro是一款功能丰富、易用且灵活的HTML5动画设计工具&#xff0c;适用于设计师、开发者和创作者创建各种精美的交互式网页动画。它的强大功能和可视化编辑界面使用户能够快速而轻松地实现复杂的动画效果&#xff0c;同时支持多种输出格式和交互方式&#xff0c;满足用户对网…

Word·VBA文档合并

目录 1&#xff0c;复制法&#xff0c;不保留原文档格式2&#xff0c;复制法&#xff0c;保留原文档格式3&#xff0c;插入法&#xff0c;保留原文档格式 之前的文章《WordVBA实现邮件合并》虽然可以生成邮件合并文档结果&#xff0c;但是不能像《python实现word邮件合并》一样…

用户账号和组账号及管理

用户账号和组账号 Linux中每个用户是通过 User Id &#xff08;UID&#xff09;来唯一标识的 新建用户 1-60000 自动分配 0-65535 端口号&#xff0c;系统是靠uid来区分用户身份的&#xff0c;用户的uid 为0 就是超级管理员 1.用户账号的类型 超级管理员:权限最高的用户,roo…

基于单片机PID炉温自动控制系统设计

**单片机设计介绍&#xff0c;基于单片机PID炉温自动控制系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机PID炉温自动控制系统设计是一个综合性的工程任务&#xff0c;旨在通过单片机结合PID控制算法实现炉温的精确和自…

Django源码之路由匹配(下)——图解逐步分析底层源码

目录 1. 前言 2. 路由匹配全过程分析 2.1 请求的入口 2.2 request的封装 2.3 response的源头 2.4 handler的获取 2.5 获取resolver对象 2.6 路由进行匹配 3. 小结 1. 前言 在上一篇文章中&#xff0c;我们谈到了路由的定义&#xff0c;通过URLPattern路由路径对象和Rou…

(一)小案例银行家应用程序-介绍

案例示例如下所示&#xff1a; 登录之后就会出现下面所示&#xff1a; 项目案例流程图如下 ● 首先我们建立四个账号对象&#xff0c;用于登录 const account1 {owner: ItShare,movements: [200, 450, -400, 3000, -650, -130, 70, 1300],interestRate: 1.2, // %pin: 11…

注意力机制篇 | YOLOv8改进之添加多尺度全局注意力机制DilateFormer(MSDA)| 即插即用

前言:Hello大家好,我是小哥谈。多尺度全局注意力机制DilateFormer是一种用图像识别任务的深度学习模型。它是在Transformer模型的基础上进行改进的,旨在提高模型对图像中不同尺度信息的感知能力。DilateFormer引入了多尺度卷积和全局注意力机制来实现多尺度感知。具体来说,…

Rancher介绍

1.什么是Rancher Rancher是一套容器管理平台&#xff0c;专门用于部署和管理容器化应用。以下是关于Rancher的详细介绍&#xff1a; 容器编排与管理&#xff1a;Rancher是一个开源的企业级容器管理平台&#xff0c;它支持Kubernetes作为其容器编排引擎。Rancher可以帮助用户在…

java线程的几种状态

目录 正文&#xff1a; 1.JConsole 2.新建状态(New) 3.运行状态(Runnable) 4.阻塞状态(Blocked) 5.等待状态(Waiting) 6.计时等待状态(Timed Waiting) 7.终止状态(Terminated) 总结&#xff1a; 正文&#xff1a; 1.JConsole JConsole是Java监控和管理控制台工具&…

ABAP 读取EXCEL 文件内容,函数 TEXT_CONVERT_XLS_TO_SAP

EXCEL 内容: 读取内容: 代码: TYPES: BEGIN OF ITAB, FL1(50) TYPE C, FL2(50) TYPE C, FL3(50) TYPE C, FL4(50) TYPE C, FL5(50) TYPE C, FL6(50) TYPE C, END OF ITAB. DATA: T_ITEM TYPE TABLE OF ITAB WITH HEADER LINE. TYPE…

Netty经典32连问

文章目录 1、Netty是什么&#xff0c;它的主要特点是什么&#xff1f;2、Netty 应用场景了解么&#xff1f;3、Netty 核心组件有哪些&#xff1f;分别有什么作用&#xff1f;4、Netty的线程模型是怎样的&#xff1f;如何优化性能&#xff1f;5、EventloopGroup了解么?和 Event…

PWM方式读取AS5600磁编码器数据

PWM方式读取AS5600磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》&#x1f4cc;《HAL STM32 硬件I2C方式读取AS5600磁编码器获取角度例程》&#x1f389;本例程包含&#xff1a;Arduino测试代码、STM32标准库代码、HAL STM32代…

Mac删除软件,动一动手指,几秒就彻底删除 mac删除软件删不掉的解决方法 mac删除软件后怎么删除软件数据

当你入职新公司&#xff0c;接手前任员工使用的Mac电脑时&#xff0c;很可能会遇到一个非常普遍的问题&#xff1a;电脑中装有大量你不需要的软件。这些软件不仅占用宝贵的硬盘空间&#xff0c;还可能影响电脑的运行速度和效率。为了获得一个干净、清爽的使用体验&#xff0c;删…

Java 关键字 this 使用详解(通俗易懂)

this关键字主要有以下三个地方使用 在方法体中引用当前对象&#xff0c;即其方法被调用的对象&#xff0c;以便将当前对象的实例变量或当前对象作为参数传递给其他方法。 ① t this.x; 要在方法中引用当前对象&#xff0c;可以使用关键字 this。 ② return this; 作为当前…

[java]网络编程

网络编程概述 计算机网络&#xff1a; 把分布在不同地理区域的具有独立功能的计算机,通过通信设备与线路连接起来&#xff0c;由功能完善的软件实现资源共享和信息传递的系统。 Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程序…

题库管理系统-基于Springboot实现JAVA+SQL离散数学题库管理系统(完整源码+LW+翻译)

基于Springboot实现JAVASQL离散数学题库管理系统(完整源码LW翻译) 概述&#xff1a; 本系统具体完成的功能如下&#xff1a; 1题库的管理与维护&#xff1a;新题的录入&#xff0c;修改&#xff0c;删除等功能。 2生成试卷&#xff1a;包括自动生成与手工改动&#xff0c;要…