1. 基础算法(I)
1.1 排序算法
排序算法(英语:Sorting algorithm)是一种将一组特定的数据按某种顺序进行排列的算法。排序算法多种多样,性质也大多不同。我们评价一种排序算法,主要考虑以下
- 稳定性:即排序后数组内相同的数的相对顺序是否发生了变化。更为形式化地,若在原本的数组中
且 在 之前,排序后的数组中 仍然在 之前,则称该排序算法是稳定的,否则称该排序算法是不稳定的。 - 时间复杂度:即该排序算法的时间复杂度的上限。基于比较的排序算法的最优时间复杂度是
的。 - 空间复杂度:即该算法所占的空间规模(相对考虑较少)。
下图(图
1.1.1 基本排序算法
以下的“排序”皆指将数组从小到大排序,且下标默认从
1.1.1.1 选择排序
选择排序的基本过程:
- 遍历整个未排序的数组,找到最小的一个数,将其与数组的首个未排序元素交换。
- 重复第 1 步,直至所有数都已经排序。
例如,将数组
选择排序是一种不稳定的排序算法,时间复杂度
1.1.1.2 冒泡排序
冒泡排序的基本过程:
- 遍历整个数组,若
,则交换 。 - 重复第 1 步,直至
,不存在 。
例如,将数组
冒泡排序是一种稳定的排序算法,时间复杂度见下表:
最好情况 | 平均情况 | 最坏情况 |
---|---|---|
1.1.1.3 插入排序
插入排序的基本过程:
- 将所有元素分为“已排序”和“待排序”两组。
- 将“待排序”中的第
个元素插入至“已排序”中正确的位置。 - 重复第 2 步,直到所有元素都位于“已排序”中。
例如,将数组
插入排序是一种稳定的排序算法,时间复杂度见下表:
最好情况 | 平均情况 | 最坏情况 |
---|---|---|
1.1.1.4 其他排序算法
- 计数排序 稳定,
。 - 基数排序 在值域较小时可以使用,稳定,
。 - 希尔排序 对插入排序的改进,不稳定,最优
,最优的最坏复杂度为 。 - 堆排序 不稳定,
。 - 桶排序 稳定,最优
(当 时接近 ),最坏 。
1.1.2 快速排序
题目:给你一个长度为
思路:
运用分治的思想。对区间
- 依据参照值
将数列划分为两部分,小于等于 的放在左边,大于 的放在右边。 - 将左右两边分别进行快速排序。
- 直接将左右两边拼接在一起。
那么只要解决了第 1 步,我们就完成了快速排序。
快速排序是一种不稳定的排序算法,最优和平均时间复杂度为
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n;
int q[N];
void qsort(int q[], int l, int r) {
if (l >= r) return ; //边界条件
int x = q[(l+r)/2]; //选取界点x
int i = l-1, j = r+1;
while (i < j) { //将数组以x为分界点划分为两部分
do i ++; while (q[i] < x);
do j --; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
qsort(q, l, j), qsort(q, j+1, r); //继续递归划分两边
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &q[i]);
qsort(q, 1, n);
for (int i = 1; i <= n; ++i) printf("%d ", q[i]);
return 0;
}
题目:给你一个长度为
思路:对数组
1.1.3 归并排序
题目:给你一个长度为
思路:
运用分治的思想。对区间
- 递归排序区间
和 。 - 定义数组
,将 之间的数按从小到大的顺序存入 中。 - 将
中的数覆盖至区间 上。
归并排序的重点在于第 2 步。维护三个指针
归并排序是稳定的排序算法,时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n;
int q[N], tmp[N];
void msort(int q[], int l, int r) {
if (l >= r) return ; //边界条件
int mid = l + r >> 1;
msort(q, l, mid), msort(q, mid+1, r); //递归排序区间[l,mid]和[mid+1,r]
int i = l, j = mid+1, k = 1; //[l,mid]和[mid+1,r]已经排好序,将它们按从小到大的顺序存入tmp中
while (i <= mid && j <= r) { //每次将ai,aj中较小的一个存入tmp中
if (q[i] <= q[j]) tmp[k ++] = q[i ++];
else tmp[k ++] = q[j ++];
}
while (i <= mid) tmp[k ++] = q[i ++]; //如果左边还没有存完,就继续存
while (j <= r) tmp[k ++] = q[j ++]; //如果右边还没有存完,就继续存
for (int i = l, j = 1; i <= r; ++i, ++j) q[i] = tmp[j]; //将tmp中的数覆盖至区间[l,r]上
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &q[i]);
msort(q, 1, n);
for (int i = 1; i <= n; ++i) printf("%d ", q[i]);
return 0;
}
题目:我们定义:在数组
思路:
定义

如图
:即 均位于 左边。这种情况的逆序对数量显然是 。 :即 均位于 右边。这种情况的逆序对数量显然是 。 :即 位于 左边, 位于 右边。不妨先把区间 和 都从小到大排好序(并不会影响逆序对数量)。那么对于若 ,则 也均大于 。
以上的
时间复杂度
主要代码:
int msort(int q[], int l, int r) { //归并排序求逆序对
if (l >= r) return 0; //如果只有1个数,就不可能有逆序对,直接返回0
int mid = l + r >> 1;
int ans = msort(q, l, mid) + msort(q, mid+1, r); //第1种情况和第2种情况
int i = l, j = mid+1, k = 1;
while (i <= mid && j <= r) { //计算第3种情况
if (q[i] <= q[j]) tmp[k ++] = q[i ++];
else tmp[k ++] = q[j ++], ans += mid-i+1; //如果ai>aj,则a{i+1}~a{mid}均大于aj,可以与j构成逆序对
}
while (i <= mid) tmp[k ++] = q[i ++];
while (j <= r) tmp[k ++] = q[j ++];
for (int i = l, j = 1; i <= r; ++i, ++j) q[i] = tmp[j];
return ans;
}
1.2 二分
1.2.1 整数二分
题目:给你一个长度为 -1 -1
)。
思路:

由于题目已经排好序了,所以我们可以采用二分的方法。如何进行二分呢?如图
- 寻找
的起始位置时,取 。 由于 是不严格单调递增的,当 时, 一定在区间 中;否则,当 时, 一定在区间 中。 - 寻找
的结束位置时,取 。 当 时, 一定在区间 中;否则,当 时, 一定在区间 中。
如上,整数二分写法有
- 缩小范围时,
或 ,取中间值时, 。 - 缩小范围时,
或 ,取中间值时, 。
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, q;
int a[N];
int findl(int k) { //寻找k的起始位置,注意这里下标从1开始,没找到返回0
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= k) r = mid;
else l = mid+1;
}
return (a[l] == k) ? l : 0;
}
int findr(int k) { //寻找k的结束位置
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1; //注意一定要加上1,不然会出现错误
if (a[mid] <= k) l = mid;
else r = mid-1;
}
return (a[l] == k) ? l : 0;
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
while (q -- ) {
int k;
scanf("%d", &k);
int l = findl(k)-1; //由于题目中说了下标从1开始,所以要减去1
int r = findr(k)-1;
printf("%d %d\n", l, r);
}
return 0;
}
1.2.2 实数二分
题目:给你一个整数
思路:类似整数二分,只需确定好所需的精度
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const double eps = 1e-9; //实数二分所取的误差值一般为所需求的小数位的1/1000左右
double n;
double cube_root(double n) { //求n的三次方根
double l = -100, r = 100;
while (r-l > eps) {
double mid = (l+r) / 2;
if (mid*mid*mid > n) r = mid;
else l = mid;
}
return l;
}
int main() {
cin >> n;
double ans = cube_root(n);
printf("%.6f\n", ans);
return 0;
}
1.3 高精度
1.3.1 高精度加法
题目:给你两个不含前导
思路:模仿竖式加法。
比如,计算

那如何让计算机模拟加法的计算呢?主要有以下
- 将输入的
反过来; - 从最高位(即原来的最低位)开始,一位一位计算加法,同时记录每次的进位;
- 倒序输出最后的结果。
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B) {
int t = 0; //t记录进位
vector<int> C; //C存储结果
for (int i = 0; i < A.size() || i < B.size() || t; ++i) {
if (i < A.size())
t += A[i];;
if (i < B.size())
t += B[i];
C.push_back(t % 10);
t /= 10;
}
return C;
}
int main() {
string a, b;
cin >> a >> b;
vector<int> A, B;
for (int i = a.size()-1; i >= 0; --i) //将A,B反过来
A.push_back(a[i]-'0');
for (int i = b.size()-1; i >= 0; --i)
B.push_back(b[i]-'0');
vector<int> C = add(A, B);
for (int i = C.size()-1; i >= 0; --i) //倒序输出A,B之和
printf("%d", C[i]);
return 0;
}
1.3.2 高精度减法
题目:给你两个不含前导
思路:与高精度加法类似,模仿竖式计算即可。时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
bool cmp(vector<int> &A, vector<int> &B) { //比较A,B的大小
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size()-1; i >= 0; --i) if (A[i] != B[i]) return A[i] > B[i];
return 1;
}
vector<int> sub(vector<int> &A, vector<int> &B) { //高精减
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); ++i) {
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t+10) % 10);
t = (t >= 0) ? 0 : 1;
}
while (C.back() == 0 && C.size() > 1) C.pop_back();
return C;
}
int main() {
string a, b;
cin >> a >> b;
vector<int> A, B;
for (int i = a.size()-1; i >= 0; --i) A.push_back(a[i]-'0');
for (int i = b.size()-1; i >= 0; --i) B.push_back(b[i]-'0');
vector<int> C;
if (cmp(A, B)) C = sub(A, B);
else putchar('-'), C = sub(B, A); //如果B>A,说明A-B为负数,先输出负号,再计算B-A
for (int i = C.size()-1; i >= 0; --i) printf("%d", C[i]);
return 0;
}
1.3.3 高精度乘法
1.3.3.1 高精乘低精
题目:给你两个不含前导
思路:将
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> mul(vector<int> &A, int b) { //高精乘
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; ++i) {
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while (C.back() == 0 && C.size() > 1) C.pop_back();
return C;
}
int main() {
string a; int b;
cin >> a >> b;
vector<int> A;
for (int i = a.size()-1; i >= 0; --i) A.push_back(a[i]-'0');
vector<int> C = mul(A, b);
for (int i = C.size()-1; i >= 0; --i) printf("%d", C[i]);
return 0;
}
1.3.3.2 高精乘高精
题目:给你两个不含前导
思路:
与高精加不同的是,高精乘高精的进位在最后统一处理。其余与竖式计算类似:
循环遍历
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> mul(vector<int> &A, vector<int> &B) {
vector<int> C(A.size()+B.size()+10, 0); //开一个大小为A.size()+B.size()+10的vector,初始化全为0
for (int i = 0; i < A.size(); ++i) {
for (int j = 0; j < B.size(); ++j)
C[i+j] += A[i] * B[j];
}
int t = 0; //处理进位
for (int i = 0; i < C.size(); ++i) {
t += C[i];
C[i] = t % 10;
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back(); //去前导0
return C;
}
int main() {
string a, b;
cin >> a >> b;
vector<int> A, B;
for (int i = a.size()-1; i >= 0; --i)
A.push_back(a[i]-'0');
for (int i = b.size()-1; i >= 0; --i)
B.push_back(b[i]-'0');
vector<int> C = mul(A, B);
for (int i = C.size()-1; i >= 0; --i)
cout << C[i];
return 0;
}
1.3.4 高精度除法
题目:给你两个不含前导
思路:

如图
除以 ,无法除。余 。 除以 ,商 余 。 除以 ,商 余 。
所以
仿照上述过程,不难写出竖式除法的代码。注意,高精加、减、乘都是从低位到高位计算,而除法是从高位到低位计算。
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
vector<int> div(vector<int> &A, int b, int &r) { //高精除
r = 0; //r为余数
vector<int> C; //C为商
for (int i = A.size()-1; i >= 0; --i) {
r = r*10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end()); //为了最后倒序输出,C需要额外反转一次
while (C.size() > 1 && C.back() == 0) C.pop_back(); //去前导0
return C;
}
int main() {
string a; int b;
cin >> a >> b;
vector<int> A;
for (int i = a.size()-1; i >= 0; --i) A.push_back(a[i]-'0'); //反向读入
int r = 0;
vector<int> C = div(A, b, r);
for (int i = C.size()-1; i >= 0; --i) printf("%d", C[i]); //倒序输出
puts("");
printf("%d", r);
return 0;
}
1.4 前缀和与差分
1.4.1 前缀和
1.4.1.1 一维前缀和
题目:给你一个包含
思路:
暴力计算,时间复杂度
不妨设
接下来思考如何计算
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, m;
int a[N], s[N]; //s[i]表示前i个数之和
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
s[i] = s[i-1] + a[i]; //预处理前缀和,即1.2
}
while (m -- ) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r]-s[l-1]); //式1.1
}
return 0;
}
1.4.1.2 二维前缀和
题目:给你一个
思路:
仿照一维前缀和,定义

如上图,由容斥原理,可得:
类似地,有:
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N]; //s即为前缀和数组
int main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &a[i][j]);
s[i][j] = s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j]; //式1.4
}
}
while (q -- ) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int ans = s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]; //式1.3
printf("%d\n", ans);
}
return 0;
}
1.4.2 差分
1.4.2.1 一维差分
题目:给你一个长度为
思路:
暴力模拟,时间复杂度
定义
; 。
对于每次修改操作 l r c
,只需将
首先我们来证明将
得证。
然后我们来证明修改操作。同样由
的值变为 ; 的值变为 ;
以此类推,直到
但我们给
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, m;
int a[N], p[N]; //p为差分数组
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
p[i] = a[i] - a[i-1];
}
while (m -- ) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
p[l] += c, p[r+1] -= c; //修改操作
}
for (int i = 1; i <= n; ++i) {
a[i] = a[i-1]+p[i]; //求出修改后的a数组
printf("%d ", a[i]);
}
return 0;
}
1.4.2.2 二维差分
题目:给你一个
思路:
仿照一维差分,定义
对于每次操作,将
最后再对
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m, q;
int p[N][N]; //p即为差分矩阵
void insert(int x1, int y1, int x2, int y2, int c) { //插入操作
p[x1][y1] += c, p[x1][y2+1] -= c, p[x2+1][y1] -= c, p[x2+1][y2+1] += c;
}
int main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
int x;
scanf("%d", &x);
insert(i, j, i, j, x); //相当于将左上角坐标为(i,j),右下角坐标为(i,j)的矩阵增加x
}
}
while (q -- ) {
int x1, y1, x2, y2, c;
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
insert(x1, y1, x2, y2, c);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
p[i][j] = p[i][j] + p[i-1][j] + p[i][j-1] - p[i-1][j-1]; //通过前缀和求出更新后的矩阵
printf("%d ", p[i][j]);
}
puts("");
}
return 0;
}
1.5 双指针算法
题目:给你一个包含
思路:运用双指针算法。
先考虑朴素
维护两个指针
通过反证法,不难证明
然后我们来思考如何判断
每次取
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n;
int a[N], s[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
int ans = 0;
for (int i = 1, j = 1; i <= n; ++i) {
s[a[i]] ++; //将ai加入桶中
while (j <= i && s[a[i]] > 1) s[a[j]] --, j ++; //向右寻找j使得[j,i]合法
ans = max(ans, i-j+1);
}
cout << ans << endl;
return 0;
}
题目:给你两个长度分别为
思路:
由于
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, m, x;
int a[N], b[N];
int main() {
scanf("%d%d%d", &n, &m, &x);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) scanf("%d", &b[i]);
int i = 1, j = m;
while (true) {
while (a[i]+b[j] > x) j --;
if (a[i]+b[j] == x) break;
i ++;
}
printf("%d %d\n", i-1, j-1); //由于这里的下标从1开始,所以要额外减去1
return 0;
}
题目:给你两个长度分别为
思路:
若
初始时,
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, m;
int a[N], b[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) scanf("%d", &b[i]);
int i = 1, j = 1;
while (i <= n && j <= m) { //双指针遍历
while (b[j] != a[i] && j < m) j ++;
i ++, j ++;
}
i --, j --;
if (i == n && a[i] == b[j]) puts("Yes");
else puts("No");
return 0;
}
1.6 位运算
题目:给你
思路:我们可以使用
首先我们要知道
例如,
我们来证明这个结论。由于
回到正题,如何求出
时间复杂度
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n;
int lowbit(int x) { //lowbit函数
return x & -x;
}
int main() {
scanf("%d", &n);
while (n -- ) {
int x;
scanf("%d", &x);
int cnt = 0;
while (x) {
x -= lowbit(x); //每次减去lowbit(x),1的个数减少1
cnt ++;
}
printf("%d ", cnt);
}
return 0;
}
1.7 离散化
题目:
有一根无限长的数轴,初始时每个点的值都为
- 对于每次增加操作,输入两个整数
,表示给数轴上表示点 的点的值增加 ; - 对于每次询问操作,输入两个整数
,输出区间 内所有点的值之和。
其中,
思路:观察到值域很大但实际使用的数很少,考虑运用离散化的思想。
先将每次操作的
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
const int N = 5e5+10;
int n, m;
int a[N], s[N]; //离散化后的前缀和数组
vector<pii> nums, query; //nums存储每次的增加操作,query存储每次询问操作
vector<int> alls; //alls存储所有用到的数
int find(int x) { //在alls中找到等于x的数
int l = 0, r = alls.size()-1;
while (l < r) {
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid+1;
}
return l+1; //由于数组a,s的下标是由1开始的,而alls的下标是从0开始的,所以要额外+1
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
int x, c;
scanf("%d%d", &x, &c);
nums.push_back({x, c}); //存储增加操作
alls.push_back(x);
}
for (int i = 1; i <= m; ++i) {
int l, r;
scanf("%d%d", &l, &r);
query.push_back({l, r}); //存储询问操作
alls.push_back(l), alls.push_back(r);
}
sort(alls.begin(), alls.end()); //排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); //去重
for (auto op : nums) { //处理每次增加操作
int x = op.first, c = op.second;
a[find(x)] += c;
}
for (int i = 1; i <= alls.size(); ++i) s[i] = s[i-1] + a[i]; //预处理前缀和
for (auto op : query) { //处理每次询问操作
int l = op.first, r = op.second;
int ans = s[find(r)] - s[find(l)-1];
printf("%d\n", ans);
}
return 0;
}
1.8 区间合并
题目:给你
思路:
先将
- 若
,则两个区间没有交点,区间数量增加 ; - 否则,将右端点更新为
。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
int n;
vector<pii> segs; //存储每个区间
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
segs.push_back({l, r});
}
sort(segs.begin(), segs.end()); //排序
int cnt = 0;
int l = segs[0].first, r = segs[0].second;
for (int i = 1; i < segs.size(); ++i) {
if (segs[i].first > r) l = segs[i].first, r = segs[i].second, cnt ++; //无法合并,区间数量+1
else r = max(segs[i].second, r); //可以合并,r更新为max(ri,r)
}
cnt ++; //注意还需要统计最后一个区间
printf("%d\n", cnt);
return 0;
}
本文作者:Jasper08
本文链接:https://www.cnblogs.com/Jasper08/p/17461495.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步