当前位置: 首页 > news >正文

【算法】算法基础入门详解:轻松理解和运用基础算法

😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪

在这里插入图片描述

文章目录

  • 前言
  • 算法基础
    • 一、快速排序
    • 二、归并排序
    • 三、二分算法
    • 四、高精度算法
    • 五、前缀和与差分
    • 六、双指针算法
    • 七、位运算
    • 八、离散化
    • 九、区间合并
  • 后记

前言


大家好呀,我是白晨😜!这段时间身边🐏的人挺多的,白晨就老老实实在家摸鱼,真不是有意拖更的(bushi)。本次白晨想要分享的是新手学习必会的几种基础算法,由于这篇文章是新手向的,所以白晨这次对于算法思想尽量讲解的细致生动,代码实现尽量简洁易懂,同时我会贴上练习算法的题目链接,大家看完算法思路一定要自己去动手敲一遍,争取能把基础算法背下来。

算法的代码风格是偏向于快速实用的,没有像工程向代码一样严谨缜密、缩进和换行严格要求,两种代码风格各有优势,本篇文章大多数算法代码采用算法风格。

本篇文章内容分享的算法有:

  • 快速排序
  • 归并排序
  • 二分算法
  • 高精度算法
  • 前缀和与差分
  • 双指针算法
  • 位运算
  • 离散化
  • 区间合并

算法基础


一、快速排序


快速排序是一种高效且使用广泛的排序算法。它的基本思想是选取一个记录作为枢轴,经过一趟排序,将整段序列分为两个部分,其中一部分的值都小于枢轴,另一部分都大于枢轴。然后再对左右区间重复这个过程,直到各区间只有一个数。

实现快速排序算法的关键在于先在待排序的数组中选择一个数字作为枢轴,然后把数组中的数字分成两部分,比枢轴小的数字移动到数组的左边,比枢轴大的数字移动到数组的右边;之后用递归的思路分别对左右两边进行排序。

下面用两道例题带领大家快速理解掌握快速排序:

image-20221226163301389

🍬题目链接:快速排序

🍎算法思想:

  • 快速排序的通用思路:

    1. 选择一个枢轴元素,通常选择数组的第一个元素或者中间的元素。
    2. 将数组中小于枢轴元素的值移动到它的左边,将大于枢轴元素的值移动到它的右边。
    3. 对枢轴元素左右两边的子数组递归执行步骤1和2,直到子数组只剩下一个元素。
  • 模板快速实现详解:

    1. 定义 quick_sort 函数,接受数组指针、左边界和右边界作为参数。
    2. quick_sort 函数中,首先判断左右边界是否合法,如果不合法则直接返回。
    3. 选择一个枢轴元素 key,这里选择的是数组中间的元素。
    4. 使用双指针 ij 分别从左右两端开始扫描数组,当 i 指向的元素小于枢轴元素时,i 向右移动;当 j 指向的元素大于枢轴元素时,j 向左移动。当 i < j 时交换两个指针所指向的元素。
    5. 最后递归对枢轴元素左右两边的子数组进行快速排序。

🍊具体实现:

#include <iostream>using namespace std;const int N = 100010;int q[N];void quick_sort(int* q, int left, int right)
{if (left >= right) // 如果左右边界不合法,直接返回return;int i = left - 1, j = right + 1;int key = q[left + right >> 1]; // 选择枢轴元素while (i < j){do i++; while (q[i] < key); // i之前的数全部小于等于keydo j--; while (q[j] > key); // j之后的数全部大于等于keyif (i < j) swap(q[i], q[j]); // 如果i<j,交换两个指针所指向的元素}quick_sort(q, left, j); // 对左边子数组进行快速排序quick_sort(q, j + 1, right); // 对右边子数组进行快速排序
}int main()
{int n;scanf("%d", &n);for (int i = 0; i < n; i++) scanf("%d", &q[i]);quick_sort(q, 0, n - 1); // 对整个数组进行快速排序for (int i = 0; i < n; i++) printf("%d ", q[i]);return 0;
}

二、归并排序


image-20221226163947134

🍬题目链接:归并排序

  1. 归并排序是一种分治算法,它将一个大数组分成两个小数组,然后递归地对这两个小数组进行排序,最后将两个排好序的小数组合并成一个有序的大数组。
  2. merge_sort函数中,首先判断待排序区间长度是否小于等于1,如果是,则直接返回。否则计算中点mid,然后对左半部分和右半部分分别进行归并排序。
  3. 在对左右两部分排好序之后,使用双指针技术将两个有序的小数组合并成一个有序的大数组。具体来说,定义三个指针ijk,其中ij分别指向左右两部分的起始位置,而k指向辅助数组tmp的起始位置。当i和j都在各自的区间内时,比较v[i]和v[j]的大小关系,并将较小者放入tmp中,并移动相应的指针。当其中一个区间内所有元素都已经放入tmp中时,则依次将另一个区间内剩余元素放入tmp中。
  4. 最后将排好序的tmp复制回原数组v中。
#include <iostream>using namespace std;const int N = 1e5 + 10; // 定义常量N为100010int v[N], tmp[N]; // 定义整型数组v和tmp,大小均为N// 归并排序函数,参数为待排序数组v,以及待排序区间[left, right]
void merge_sort(int v[], int left, int right)
{if (left >= right) // 如果待排序区间长度小于等于1,则直接返回return;int mid = left + right >> 1; // 计算中点midmerge_sort(v, left, mid); // 对左半部分进行归并排序merge_sort(v, mid + 1, right); // 对右半部分进行归并排序int i = left, j = mid + 1, k = left; // 定义三个指针i、j、kwhile (i <= mid && j <= right) // 当i和j都在各自的区间内时{if (v[i] < v[j]) tmp[k++] = v[i++]; // 如果v[i] < v[j],则将v[i]放入tmp中,并移动指针i和kelse tmp[k++] = v[j++]; // 否则将v[j]放入tmp中,并移动指针j和k}while (i <= mid) // 如果左半部分还有剩余元素,则依次放入tmp中tmp[k++] = v[i++];while (j <= right) // 如果右半部分还有剩余元素,则依次放入tmp中tmp[k++] = v[j++];for (i = left; i <= right; ++i) // 将排好序的tmp复制回原数组v中v[i] = tmp[i];
}int main()
{int n;cin >> n; // 输入元素个数nfor (int i = 0; i < n; ++i)scanf("%d", &v[i]); // 输入n个元素merge_sort(v, 0, n - 1); // 对整个数组进行归并排序for (int i = 0; i < n; ++i)printf("%d ", v[i]); // 输出排好序的结果return 0;
}

三、二分算法


二分法,即二分搜索法,是通过不断缩小解可能存在的范围,从而求得问题最优解的方法。例如,如果一个序列是有序的,那么可以通过二分的方法快速找到所需要查找的元素,相比线性搜索要快不少。此外二分法还能高效的解决一些单调性判定的问题。

// 模板一
// 求满足check条件的最左下标
#include <iostream>using namespace std;template<class T>
int binary_search1(T* v, int l, int r)
{while (l < r){int mid = l + r >> 1;if (check(v[mid])) // check中 v[mid] 永远放在前面,eg. v[mid] >= ar = mid;elsel = mid + 1;}return mid;
}// 模板二
// 求满足check条件的最右下标
#include <iostream>using namespace std;template<class T>
int binary_search1(T* v, int l, int r)
{while (l < r){int mid = l + r + 1 >> 1; // 必须加一,避免死循环if (check(v[mid])) // eg.v[mid] <= al = mid;elser = mid - 1;}return mid;
}
  • 数的范围

image-20221226164131064

🍬题目链接:数的范围

// 数的范围#include <iostream>
#include <vector>using namespace std;int main()
{int n, m;cin >> n >> m; // 输入元素个数n和查询次数mvector<int> v(n); // 定义整型向量v,大小为nfor (int i = 0; i < n; ++i)cin >> v[i]; // 输入n个元素while (m--) // 循环执行m次查询操作{int num;cin >> num; // 输入要查询的元素num// 先找等于num的最左下标int l = 0, r = n - 1; // 定义双指针l和rwhile (l < r) // 当l<r时循环执行以下操作{int mid = l + r >> 1; // 计算中点midif (v[mid] >= num) // 如果v[mid] >= num,则在左半部分继续查找r = mid;else // 否则在右半部分继续查找l = mid + 1;}if (v[l] != num) // 如果没有找到num,则输出"-1 -1"{cout << "-1 -1" << endl;continue;}else{cout << l << " "; // 输出最左下标ll = 0, r = n - 1; // 重新定义双指针l和r// 再找等于num的最右下标while (l < r) // 当l<r时循环执行以下操作{int mid = l + r + 1 >> 1; // 计算中点midif (v[mid] <= num) // 如果v[mid] <= num,则在右半部分继续查找l = mid;else r = mid - 1;}cout << l << endl;}}return 0;
}

四、高精度算法


高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘等运算的方法,它用于处理大数字的数学计算。

比如,C++的long long类型最多处理2642^{64}264这么大的数,再大的数则无法处理。

  • 高精度加法

我们可以模拟正常的加法,从个位开始,逐位相加,模拟过程中要注意的是:

  • 我们取出字符串的每一个元素都是字符,所以不能直接将其相加,必须要减去'0' 才能得到这个数的真实值。
  • 当一个数的每一位都已经遍历完了,如果另一个数还没遍历完,则在这个数的高位补0。
  • 如果两个数字之和大于等于10,要进位。
  • 每次向要返回字符串插入一个本次相加得到的个位数
  • 最后得到的返回字符串是反的,我们要将其反转。

image-20221226164328456

🍬题目链接:高精度加法

// 高精度加法#include <iostream>
#include <iostream>
#include <vector>
#include <string>using namespace std;vector<int> add(vector<int>& a, vector<int>& b)
{int t = 0; // 进位vector<int> c;// 当a,b大数没有遍历完或者进位不为0时,继续循环for (int i = 0; i < a.size() || i < b.size() || t != 0; ++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;// 字符串接收数据,用数组来保存数据,其实也可以直接使用字符串计算,为了方便理解以及美观好记// 暂时使用数组来存储大数的每一位,但是有一点要注意,数组存储大数时最好反着存,也即0下标存最低位,高下标存高位,类似于小端// 因为加法可能要进位,如果0下标存最高位,进位必须整体向后移动一位,这样太耗费时间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 = add(a, b);for (auto rit = c.rbegin(); rit != c.rend(); ++rit)printf("%d", *rit);return 0;
}

压位优化版(了解即可)

它首先定义了一个常量 base1e9,表示每个元素存储9位数。然后定义了一个 add 函数,用于计算两个大数的和。在主函数中,首先读入两个字符串 ab,然后将它们转换为大数存储在向量 AB 中。最后调用 add 函数计算它们的和并输出结果。

#include <iostream>
#include <vector>
#include <string>using namespace std;const int base = 1e9;vector<int> add(vector<int>& A, vector<int>& B)
{int t = 0; // 进位vector<int> C;// 当A,B大数没有遍历完或者进位不为0时,继续循环for (int i = 0; i < A.size() || i < B.size() || t != 0; ++i){// 数字没有遍历完才加if (i < A.size()) t += A[i];if (i < B.size()) t += B[i];C.push_back(t % base);t /= base;}if (t) C.push_back(t);return C;
}int main()
{string a, b;cin >> a >> b;vector<int> A, B;// 压位,一次将9个数字存储到一个位置,可以节省空间以及加快运算for (int i = a.size() - 1, s = 0, t = 1, j = 0; i >= 0; --i){s += (a[i] - '0') * t;j++, t *= 10;if (j == 9 || i == 0){A.push_back(s);j = 0, s = 0, t = 1;}}for (int i = b.size() - 1, s = 0, t = 1, j = 0; i >= 0; --i){s += (b[i] - '0') * t;j++, t *= 10;if (j == 9 || i == 0){B.push_back(s);j = 0, s = 0, t = 1;}}vector<int> C = add(A, B);cout << C.back();// 高位为0的需要用前导0填充for (int i = C.size() - 2; i >= 0; --i) printf("%09d", C[i]);return 0;
}
  • 高精度减法

和加法大体思路相同,不过要先判断被减数和减数的大小,确定结果的正负。

image-20221226164419455

🍬题目链接:高精度减法

// 高精度减法#include <iostream>
#include <vector>
#include <string>using namespace std;// a大于等于b,返回true;反之,返回false
bool compare(string& a, string& b)
{if (a.size() != b.size())return a.size() > b.size();for (int i = 0; i < a.size(); ++i)if (a[i] != b[i])return a[i] > b[i];return true;
}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];// 无论借没借位,先加上10再模10,就是这一位的值c.push_back((t + 10) % 10);// 如果没有借位a[i] - t >= b[i],那么t最后是大于等于0// 反之,小于0if (t < 0) t = 1;else t = 0;}while (c.size() > 1){if (c.back() != 0)break;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 (compare(a, b))c = sub(a, b);else{c = sub(b, a);cout << "-";}for (int i = c.size() - 1; i >= 0; --i){printf("%d", c[i]);}return 0;
}
  • 高精度乘法

先来看乘法用竖式如何计算:

在这里插入图片描述

  • 我们发现,从右到左, num2 每一位都需要乘以 num1 ,并且每乘完一次 num1 所得的数字的权重要乘10。

  • num2 每一位乘 num1 都是个位数* num1 ,所以我们可以先把个位数乘 num1 的结果保存起来,用的时候直接调用。

  • 得到 num2 每一位乘 num1 的字符串后,保存起来,最后和竖式一样,依次相加每一位的结果,得到最后的答案。

image-20221226164508900

🍬题目链接:高精度乘法

题目中 B 的值小于等于 10000,可以直接用 A 的每一位乘 B,具体实现见代码。

// 高精度乘法#include <iostream>
#include <vector>
#include <string>using namespace std;vector<int> mul(vector<int>& A, const int& b)
{int t = 0; // 进位vector<int> C;for (int i = 0; i < A.size() || t != 0; ++i){if (i < A.size()) t += A[i] * b;C.push_back(t % 10);t /= 10;}// 删去前导0,防止乘0的特殊情况while (C.size() > 1){if (C.back() != 0)break;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;
}
  • 高精度除法

思路和上述高精度算法大体相同,具体见代码。

image-20221226164545568

🍬题目链接:高精度除法

// 高精度除法
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>using namespace std;vector<int> div(vector<int>& A, const int& b, int& r)
{int t = 0;vector<int> C;// 除法要从高位开始for (int i = A.size() - 1; i >= 0; --i){t = t * 10 + A[i];C.push_back(t / b);t %= b;}r = t;// 由于C是从高位开始记录的,返回时需要反转一下,恢复低位存低位,高位存高位的标准reverse(C.begin(), C.end());// 除去前导0while (C.size() > 1 && C.back() == 0)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');int r = 0; // 余数vector<int> C = div(A, b, r);for (int i = C.size() - 1; i >= 0; --i)printf("%d", C[i]);printf("\n%d", r);return 0;
}

五、前缀和与差分


  • 一维前缀和

image-20221226164627343

🍬题目链接:前缀和

首先定义一个函数 get_part_sum,用于计算区间和。在主函数中,首先读入两个整数 nm,然后读入一个长度为 n 的数组 v。接着计算前缀和数组 prefix,最后循环读入查询区间的左右端点 lr,并输出区间和。

// 一维前缀和
#include <iostream>
#include <vector>
using namespace std;// 计算区间和
int get_part_sum(vector<int>& prefix, int l, int r)
{// 区间和 = 前缀和[r] - 前缀和[l - 1]return prefix[r] - prefix[l - 1];
}int main()
{int n, m;cin >> n >> m;vector<int> v(n + 1);for (int i = 1; i <= n; ++i)scanf("%d", &v[i]);// 计算前缀和数组vector<int> prefix(n + 1);for (int i = 1; i <= n; ++i)// 前缀和[i] = 前缀和[i - 1] + v[i]prefix[i] = prefix[i - 1] + v[i];int l, r;while (cin >> l >> r){// 输出区间[l,r]的区间和cout << get_part_sum(prefix, l, r) << endl;}return 0;
}
  • 二维前缀和

image-20221226164730481

🍬题目链接:子矩阵的和

// 二维前缀和
#include <iostream>
#include <vector>using namespace std;int get_part_sum(vector<vector<int>>& prefix, int x1, int y1, int x2, int y2)
{return prefix[x2][y2] - prefix[x1 - 1][y2] - prefix[x2][y1 - 1] + prefix[x1 - 1][y1 - 1];
}int main()
{int n, m, q;cin >> n >> m >> q;vector<vector<int>> vv(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)scanf("%d", &vv[i][j]);vector<vector<int>> prefix(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)prefix[i][j] = prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1] + vv[i][j];// 二维前缀和计算公式:// sum(x1, y1, x2, y2) = prefix(x2, y2) - prefix(x1 - 1, y2) - prefix(x2, y1 - 1) + prefix(x1 - 1, y1 - 1)int x1, y1, x2, y2;while (q--){scanf("%d%d%d%d", &x1, &y1, &x2, &y2);cout << get_part_sum(prefix, x1, y1, x2, y2) << endl;}return 0;
}
  • 一维差分

差分算法是一种利用原数组和差分数组的关系,来快速地对数组中某个区间进行同一操作的方法。例如,如果要对原数组a[]中[l,r]之间的每个数加上c,只需要对差分数组b[]中的b[l]加上c,b[r+1]减去c,然后重新求出a[]即可。

如何构造差分数组呢?

构造差分数组的方法很简单,只需要用原数组的相邻元素的差来赋值即可。例如,如果原数组a[]为{1,2,4,6},那么差分数组b[]为{1,1,2,2},因为b[1]=a[1]-a[0],b[2]=a[2]-a[1],以此类推。

反之,从差分数组求原数组就为:a[i] = a[i - 1] + b[i](i > 0)。

image-20221226164807160

🍬题目链接:差分

一维差分数组是一种处理区间修改和查询的方法,它可以在O(1)的时间内实现对原数组某个区间内所有元素加上一个常数。具体来说,如果原数组是a[],差分数组是b[],那么有以下关系:

  • a[i] = b[1] + b[2] + … + b[i]
  • b[i] = a[i] - a[i-1]
// 一维差分
#include <iostream>
#include <vector>using namespace std;void insert(vector<int>& dif, int l, int r, int c)
{dif[l] += c;dif[r + 1] -= c;
}int main()
{int n, m;cin >> n >> m;vector<int> v(n + 2);for (int i = 1; i <= n; ++i)scanf("%d", &v[i]);// 差分公式:mod(l, r, c) : dif(l) + c and dif(r + 1) - c vector<int> dif(n + 2, 0);// 差分数组初始化:假设 前缀和 和 差分数组 都是0开始,前缀和数组(i位置)每插入一个数字相当于 在差分数组(i, i)位置插入v[i]for (int i = 1; i <= n; ++i)insert(dif, i, i, v[i]);int l, r, c;while (m--){cin >> l >> r >> c;insert(dif, l, r, c);}for (int i = 1; i <= n; ++i)v[i] = v[i - 1] + dif[i];for (int i = 1; i <= n; ++i)printf("%d ", v[i]);return 0;
}
  • 二维差分

image-20221226164856458

🍬题目链接:差分矩阵

// 二维差分
#include <iostream>
#include <vector>using namespace std;const int N = 1010;int v[N][N];
int dif[N][N];// 二维差分公式 insert(x1, y1, x2,  y2, c):
// dif[x1][y1] + c && dif[x2 + 1][y1] - c && dif[x1][y2 + 1] - c && dif[x2 + 1][y2 + 1] + c
//  定义一个函数,用于对差分数组进行修改
void insert(int x1, int y1, int x2, int y2, int c)
{dif[x1][y1] += c;dif[x2 + 1][y1] -= c;dif[x1][y2 + 1] -= c;dif[x2 + 1][y2 + 1] += c;
}// 二维差分: dif[n][m] 会直接影响 v[n][m] 及右下方的值
int main()
{int n, m, q;cin >> n >> m >> q;for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)scanf("%d", &v[i][j]);// 初始化差分数组for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)insert(i, j, i, j, v[i][j]);int x1, y1, x2, y2, c;while (q--){cin >> 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)v[i][j] = v[i - 1][j] + v[i][j - 1] - v[i - 1][j - 1] + dif[i][j];for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j)printf("%d ", v[i][j]);printf("\n");}return 0;
}

六、双指针算法


双指针算法是一种在重复遍历对象的过程中,使用两个指针进行访问的方法。它可以利用对象的某种单调性,减少不必要的遍历次数,从而优化时间复杂度。双指针算法有两种常见的类型:快慢指针和对撞指针。

  • 快慢指针是指两个指针以不同的速度或步长在同一个方向上移动,通常用于解决链表中的问题,比如判断链表是否有环,找到链表的中点或倒数第k个结点等。
  • 对撞指针是指两个指针从相反的方向上移动,直到相遇或满足某个条件为止,通常用于解决数组或字符串中的问题,比如寻找两数之和等于目标值的下标,判断一个字符串是否是回文串等。
  • 模板
// 双指针模板
bool check(int i, int j);void two_pointer()
{int n;for (int i = 0, j = 0; i < n; ++i){while (j <= i && check(i, j)){// ......j++;// ......}// .....}
}
  • 最长连续不重复子序列

image-20221226164934011

🍬题目链接:最长连续不重复子序列

#include <iostream>using namespace std;const int N = 100010; int v[N]; // 存储输入的数组 
int book[N];//判重数组,用于记录每个数字出现的次数int main() 
{ 	int n; // 数组的长度 cin >> n; for (int i = 0; i < n; ++i) cin >> v[i]; int Max = 0; // 记录最长不重复子数组的长度for (int i = 0, j = 0; i < n; ++i) // i和j是两个指针,分别指向子数组的右端点和左端点{book[v[i]]++; // 将当前数字的计数加一// 当有重复数字时while (j <= i && book[v[i]] > 1) // 如果当前数字出现了两次以上,说明子数组中有重复元素,需要缩小左边界{// j指向的数字计数--,并且j++,直到数组中没有重复数字book[v[j]]--;j++;}Max = max(Max, i - j + 1); // 更新最长不重复子数组的长度}cout << Max << endl; // 输出结果return 0;
}
  • 数组元素目标和

image-20221226165041031

🍬题目链接:数组元素的目标和

#include <iostream>using namespace std;const int N = 100010;int v1[N]; // 存储第一个有序数组 
int v2[N]; // 存储第二个有序数组int main() 
{ int n, m, t; // n和m分别是两个数组的长度,t是目标值 cin >> n >> m >> t; for (int i = 0; i < n; ++i) cin >> v1[i]; for (int i = 0; i < m; ++i) cin >> v2[i];int i = 0, j = m - 1; // i和j是两个指针,分别指向第一个数组的左端点和第二个数组的右端点while (i < n && j >= 0) // 当两个指针没有越界时{if (v1[i] + v2[j] == t) // 如果两个指针指向的数字之和等于目标值,输出下标对,并将左指针右移{cout << i << " " << j << endl;i++;}else if (v1[i] + v2[j] > t) // 如果两个指针指向的数字之和大于目标值,说明右边的数字太大,需要将右指针左移j--;else // 如果两个指针指向的数字之和小于目标值,说明左边的数字太小,需要将左指针右移i++;}return 0;
}

七、位运算


  • 获取n的二进制表示中第k位(最低位从0开始)
int n;
int k;
int bitk = n >> k & 1;
  • 获取n的二进制表示中最右边的1
// lowbit:获取n的二进制表示中最右边的1,eg. 输入 10010 返回 00010
int rightbit1 = n & -n; // 等价于 n & (~n + 1)   
  • 二进制中1的个数

image-20221226165156569

🍬题目链接:二进制中1的个数

#include <iostream>using namespace std;const int N = 1e5 + 10;
int v[N];int main()
{int n;cin >> n;for (int i = 0; i < n; ++i)cin >> v[i];for (int i = 0; i < n; ++i){int num = v[i];int cnt = 0;while (num){int x = num & -num; // lowbitcnt++;num ^= x; // 去除最后一个1}cout << cnt << " ";}cout << endl;return 0;
}

八、离散化


  • 离散化定义
整数保序离散化
将数据范围很大,但是数字个数很少的数据按照升序对数字坐标进行映射
如,数据范围 10^9 数字个数 10^5 1 200000 88888888 1e9
将上述数据映射到      0    1      2      3
简而言之,就是一种哈希
  • 区间和

image-20221226165255009

🍬题目链接:区间和

// 区间和// 原版
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;const int N = 3 * 1e5 + 1;vector<int> alls; // 存放所有坐标,将其离散化
vector<pair<int, int>> operates; // 所有操作
vector<pair<int, int>> query; // 所有查询int Hash[N]; // 离散化坐标对应的值
int psum[N]; // 前缀和数组// 以原坐标值查询映射后的坐标值
int find(int x)
{// 二分查找int l = 0, r = alls.size() - 1;while (l < r){int mid = l + r >> 1;if (alls[mid] >= x)r = mid;elsel = mid + 1;}return l + 1;
}int main()
{int n, m;cin >> n >> m;for (int i = 0; i < n; ++i){int x, c;cin >> x >> c;operates.emplace_back(x, c);alls.push_back(x);}for (int i = 0; i < m; ++i){int l, r;cin >> l >> r;query.emplace_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 (int i = 0; i < n; ++i){// 由于前缀和数组的下标要从1开始,所有,所有映射坐标+1int x = find(operates[i].first), c = operates[i].second;Hash[x] += c;}// 构造前缀和数组for (int i = 1; i <= alls.size(); ++i)psum[i] = psum[i - 1] + Hash[i];for (int i = 0; i < m; ++i){int l = find(query[i].first), r = find(query[i].second);cout << psum[r] - psum[l - 1] << endl;}return 0;
}

九、区间合并


  • 区间合并

区间合并算法是一种用于把给定的、可以合并的区间合并的算法。它的基本思想是:

  1. 将所有区间按照左边界值进行从小到大排序。
  2. 遍历所有区间,如果第i+1区间的左边界值比第i区间的右边界值小,即可合并,更新i+1区间的边界值,并且将第i区间打上标记表示该区间被i+1合并过。
  3. 最后输出没有被标记过的区间,即为合并后的结果。

image-20221226165423106

🍬题目链接:区间合并

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;typedef pair<int, int> PII;
vector<PII> v;int main()
{int n;cin >> n;for (int i = 0; i < n; ++i){int l, r;cin >> l >> r;v.emplace_back(l, r);}sort(v.begin(), v.end());int l = -2e9, r = -2e9; // 初始化变量 l 和 r 的值为 -2e9int cnt = 0; // 初始化计数器 cnt 的值为 0for (auto& e : v) // 遍历 vector 中的每个元素 e{int nl = e.first, nr = e.second; if (nl > r) // 如果新区间与当前区间不相交{cnt++; // 区间计数器加一l = nl; }r = max(nr, r); // 更新当前区间右端点}cout << cnt << endl;return 0;
}

后记


在这篇文章中,我们介绍了算法基础的一些概念和方法,包括算法的定义、特征、分类、设计和分析等。我们也通过一些例子展示了如何运用算法解决实际问题,并且分析了算法的效率和优化。我们希望这篇文章能够给你提供一个对算法基础的初步认识和兴趣,让你感受到算法的魅力和价值。

img


如果解析有不对之处还请指正,我会尽快修改,多谢大家的包容。

如果大家喜欢这个系列,还请大家多多支持啦😋!

如果这篇文章有帮到你,还请给我一个大拇指 👍和小星星 ⭐️支持一下白晨吧!喜欢白晨【算法】系列的话,不如关注👀白晨,以便看到最新更新哟!!!

我是不太能熬夜的白晨,我们下篇文章见。

相关文章:

【算法】算法基础入门详解:轻松理解和运用基础算法

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…...

2.9.1 Packet Tracer - Basic Switch and End Device Configuration(作业)

Packet Tracer - 交换机和终端设备的基本 配置地址分配表目标使用命令行界面 (CLI)&#xff0c;在两台思科互联网络 操作系统 (IOS) 交换机上配置主机名和 IP 地址。使用思科 IOS 命令指定或限制对设备 配置的访问。使用 IOS 命令来保存当前的运行配置。配置两台主机设备的 IP …...

AtCoder Beginner Contest 216(F)

F - Max Sum Counting 链接&#xff1a; F - Max Sum Counting 题意 两个 大小为 nnn 的序列 aiaiai 和 bibibi&#xff0c;任意选取一些下标 iii&#xff0c;求 max⁡(ai)>∑bi\max(ai) > \sum{bi}max(ai)>∑bi的方案数。 解析 首先考虑状态 一是和&#xff0c;…...

每天学一点之Stream流相关操作

StreamAPI 一、Stream特点 Stream是数据渠道&#xff0c;用于操作数据源&#xff08;集合、数组等&#xff09;所生成的元素序列。“集合讲的是数据&#xff0c;负责存储数据&#xff0c;Stream流讲的是计算&#xff0c;负责处理数据&#xff01;” 注意&#xff1a; ①Str…...

MatCap模拟光照效果实现

大家好&#xff0c;我是阿赵 之前介绍过各种光照模型的实现方法。那些光照模型的实现虽然有算法上的不同&#xff0c;但基本上都是灯光方向和法线方向的计算得出的明暗结果。 下面介绍一种叫做MatCap的模拟光照效果&#xff0c;这种方式计算非常简单&#xff0c;脱离灯光的计算…...

二十一、PG管理

一、 PG异常状态说明 1、 PG状态介绍 可以通过ceph pg stat命令查看PG当前状态&#xff0c;健康状态为“active clean” [rootrbd01 ~]# ceph pg stat 192 pgs: 192 activeclean; 1.3 KiB data, 64 MiB used, 114 GiB / 120 GiB avail; 85 B/s rd, 0 op/s2、pg常见状态 状…...

SAPUI5开发01_01-Installing Eclipse

1.0 简要要求概述: 本节您将安装SAPUI 5,以及如何在Eclipse Juno中集成SAPUI 5工具。 1.1 安装JDK JDK 是一种用于构建在 Java 平台上发布的应用程序、Applet 和组件的开发环境,即编写 Java 程序必须使用 JDK,它提供了编译和运行 Java 程序的环境。 在安装 JDK 之前,首…...

Qt之高仿QQ系统设置界面

QQ或360安全卫士的设置界面都是非常有特点的,所有的配置项都在一个垂直的ScrollArea中,但是又能通过左侧的导航栏点击定位。这样做的好处是既方便查看指定配置项,又方便查看所有配置项。 一.效果 下面左边是当前最新版QQ的系统设置界面,右边是我的高仿版本,几乎一毛一样…...

JVM概览:内存空间与数据存储

核心的五个部分虚拟机栈&#xff1a;局部变量中基础类型数据、对象的引用存储的位置&#xff0c;线程独立的。堆&#xff1a;大量运行时对象都在这个区域存储&#xff0c;线程共享的。方法区&#xff1a;存储运行时代码、类变量、常量池、构造器等信息&#xff0c;线程共享。程…...

固态存储设备固件升级方案

1. 前言 随着数字化时代的发展&#xff0c;数字数据的量越来越大&#xff0c;相应的数据存储的需求也越来越大&#xff0c;存储设备产业也是蓬勃发展。存储设备产业中&#xff0c;发展最为迅猛的则是固态存储(Solid State Storage&#xff0c;SSS)。数字化时代&#xff0c;海量…...

Python交通标志识别基于卷积神经网络的保姆级教程(Tensorflow)

项目介绍 TensorFlow2.X 搭建卷积神经网络&#xff08;CNN&#xff09;&#xff0c;实现交通标志识别。搭建的卷积神经网络是类似VGG的结构(卷积层与池化层反复堆叠&#xff0c;然后经过全连接层&#xff0c;最后用softmax映射为每个类别的概率&#xff0c;概率最大的即为识别…...

基于Selenium+Python的web自动化测试框架(附框架源码+项目实战)

目录 一、什么是Selenium&#xff1f; 二、自动化测试框架 三、自动化框架的设计和实现 四、需要改进的模块 五、总结 总结感谢每一个认真阅读我文章的人&#xff01;&#xff01;&#xff01; 重点&#xff1a;配套学习资料和视频教学 一、什么是Selenium&#xff1f; …...

Python进阶-----高阶函数zip() 函数

目录 前言&#xff1a; zip() 函数简介 运作过程&#xff1a; 应用实例 1.有序序列结合 2.无序序列结合 3.长度不统一的情况 前言&#xff1a; 家人们&#xff0c;看到标题应该都不陌生了吧&#xff0c;我们都知道压缩包文件的后缀就是zip的&#xff0c;当然还有r…...

win10打印机拒绝访问解决方法

一直以来,在安装使用共享打印机打印一些文件的时候&#xff0c;会遇到错误提示&#xff1a;“无法访问.你可能没有权限使用网络资源。请与这台服务器的管理员联系”的问题&#xff0c;那为什么共享打印机拒绝访问呢&#xff1f;别着急&#xff0c;下面为大家带来相关的解决方法…...

深度学习训练营之数据增强

深度学习训练营学习内容原文链接环境介绍前置工作设置GPU加载数据创建测试集数据类型查看以及数据归一化数据增强操作使用嵌入model的方法进行数据增强模型训练结果可视化自定义数据增强查看数据增强后的图片学习内容 在深度学习当中,由于准备数据集本身是一件十分复杂的过程,…...

Tomcat安装及启动

日升时奋斗&#xff0c;日落时自省 目录 1、Tomcat下载 2、JDK安装及配置环境 3、Tomcat配置环境 4、启动Tomcat 5、部署演示 1、Tomcat下载 直接入主题&#xff0c;下载Tomcat 首先就是别下错了&#xff0c;直接找官方如何看是不是广告&#xff0c;或者造假 搜索Tomc…...

【专项训练】排序算法

排序算法 非比较类的排序,基本上就是放在一个数组里面,统计每个数出现的次序 最重要的排序是比较类排序! O(nlogn)的3个排序,必须要会!即:堆排序、快速排序、归并排序! 快速排序:分治 经典快排 def quickSort1(arr...

Android压测测试事件行为参数对照表

执行参数参数说明颗粒度指标基础参数--throttle <ms> 用于指定用户操作间的时延。 -s 随机数种子&#xff0c;用于指定伪随机数生成器的seed值&#xff0c;如果seed值相同&#xff0c;则产生的时间序列也相同。多用于重测、复现问题。 -v 指定输出日志的级别&#xff0c;…...

【观察】亚信科技:“飞轮效应”背后的数智化创新“延长线”

著名管理学家吉姆柯林斯在《从优秀到卓越》一书中提出“飞轮效应”&#xff0c;它指的是为了使静止的飞轮转动起来&#xff0c;一开始必须使很大的力气&#xff0c;每转一圈都很费力&#xff0c;但达到某一临界点后&#xff0c;飞轮的重力和冲力就会成为推动力的一部分&#xf…...

QT编程从入门到精通之十四:“第五章:Qt GUI应用程序设计”之“5.1 UI文件设计与运行机制”之“5.1.1 项目文件组成”

目录 第五章:Qt GUI应用程序设计 5.1 UI文件设计与运行机制 5.1.1 项目文件组成 第五章:Qt GUI应用程序设计...

(二分)730. 机器人跳跃问题

目录 题目链接 一些话 切入点 流程 套路 ac代码 题目链接 AcWing 730. 机器人跳跃问题 - AcWing 一些话 // 向上取整 mid的表示要写成l r 1 >> 1即可&#xff0c;向下取整 mid l r >> 1 // 这里我用了浮点二分&#xff0c;mid (l r) / 2&#xff0c;最…...

vue3使用nextTick

发现nextTick必须放在修改一个响应式数据之后&#xff0c;才会在onUpdated之后被调用&#xff0c;如果nextTick是放在所有对响应式数据修改之前&#xff0c;则nextTick里面的回调函数会在onBeforeUpdate方法执行前就被调用了。可是nextTick必须等到onUpdated执行完成之后执行&a…...

传统图像处理之颜色特征

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…...

GPS问题调试—MobileLog中有关GPS关键LOG的释义

GPS问题调试—MobileLog中有关GPS关键LOG的释义 [DESCRIPTION] 在mobile log中,有很多GPS相关的log出现在main log和kernel log、properties文件中,他们的意思是什么,通过这篇文档进行总结,以便在处理GPS 问题时,能够根据这些log快速的收敛问题。 [SOLUTION] 特别先提醒…...

【企业管理】你真的理解向下管理吗?

导读&#xff1a;拜读陈老师一篇文章《不会向下负责&#xff0c;你凭什么做管理者&#xff1f;》&#xff0c;引发不少共鸣&#xff0c;“很多管理者有一种错误的观念&#xff0c;认为管理是向下管理&#xff0c;向上负责。其实应该反过来&#xff0c;是向上管理&#xff0c;向…...

Centos7 硬盘挂载流程

1、添加硬盘到Linux&#xff0c;添加后重启系统2、查看添加的硬盘&#xff0c;lsblksdb 8:16020G 0disk3、分区fdisk /dev/sdbmnw其余默认&#xff0c;直接回车再次查看分区情况&#xff0c;lsblksdb1 8:17 0 20G 0 part4、格式化mkfs -t ext4 /dev/sdb15、挂载mkdir /home/new…...

认识vite_vue3 初始化项目到打包

从0到1创建vite_vue3的项目背景效果vite介绍&#xff08;对比和vuecli的区别&#xff09;使用npm创建vitevitevuie3创建安装antdesignvite自动按需引入&#xff08;vite亮点&#xff09;请求代理proxy打包背景 vue2在使用过程中对象的响应式不好用新增属性的使用$set才能实现效…...

【Go】cron时间格式

【Go】cron时间格式 Minutes&#xff1a;分钟&#xff0c;取值范围[0-59]&#xff0c;支持特殊字符* / , -&#xff1b;Hours&#xff1a;小时&#xff0c;取值范围[0-23]&#xff0c;支持特殊字符* / , -&#xff1b;Day of month&#xff1a;每月的第几天&#xff0c;取值范…...

leetcode 55. 跳跃游戏

给定一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 示例 1&#xff1a; 输入&#xff1a;nums [2,3,1,1,4] 输出&#xff1a;true 解释&#xff1a;可以先跳 1 …...

Linux:文件流指针 与 文件描述符

目录一、文件描述符二、文件流指针三、缓冲区之前讲解过了IO库函数和IO接口&#xff0c;库函数是对系统调用接口的封装&#xff0c;也就是说实际上在库函数内部是通过调用系统调用接口来完成最终功能的。 库函数通过文件流指针操作文件&#xff0c;系统调用接口通过文件描述符操…...

基于FPGA实现正弦插值算法

1、正弦插值的算法分析 1.1 信号在时域与频域的映射关系 在进行正弦算法分析之前&#xff0c;我们回顾一下《数字信号处理》课程中&#xff0c;对于信号在时域与频域之间的映射关系&#xff0c;如下图。 对于上图中的原始信号x(t)&#xff0c;使用ADC对信号进行采样&#xff0…...

JavaWeb_会话技术

文章目录会话跟踪技术的概述Cookie概念Cookie工作流程Cookie基本使用发送Cookie获取CookieCookie原理分析Cookie的使用细节Cookie的存活时间Cookie存储中文SessionSession的基本使用概念工作流程Session的基本使用Session的原理分析Session的使用细节Session的钝化与活化Sessio…...

Reactor响应式流的核心机制——背压机制

响应式流是什么&#xff1f; 响应式流旨在为无阻塞异步流处理提供一个标准。它旨在解决处理元素流的问题——如何将元素流从发布者传递到订阅者&#xff0c;而不需要发布者阻塞&#xff0c;或订阅者有无限制的缓冲区或丢弃。 响应式流模型存在两种基本的实现机制。一种就是传统…...

[数据结构]栈的深入学习-java实现

CSDN的各位uu们你们好,今天千泽带来了栈的深入学习,我们会简单的用代码实现一下栈, 接下来让我们一起进入栈的神奇小世界吧!0.速览文章一、栈的定义1. 栈的概念2. 栈的图解二、栈的模拟实现三.栈的经典使用场景-逆波兰表达式总结一、栈的定义 1. 栈的概念 栈&#xff1a;一种…...

网络编程基础

1 互联网的本质硬件设备有了操作系统&#xff0c;然后装上软件之后我们就能够正常使用了&#xff0c;然后也只能自己使用。像这样&#xff0c;每个人都拥有一台自己的机器&#xff0c;然而彼此孤立。如何才能和大家一起愉快的玩耍&#xff1f;什么是网络&#xff1f;简单来说&a…...

华为OD机试题 - 数列还原(JavaScript)| 机考必刷

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:数列还原题目输入输出示例一输入输出Code代码解析版权说明华为O…...

10-Oracle存储过程(创建,修改,使用及管理)

本章内容 1、我们为什么要用存储过程? 2、存储过程是如何定义和维护的? 3、我们如何调用存储过程? 4、存储过程中常用的复合数据处理方式及CTE 5、存储过程如何进行异常处理? 6、存储过程如何进行事务处理? 7、我们应如何优化存储过程? 1、我们为什么要用存储过程…...

创建线程的三种方法

文章目录1、创建一个类实现Runnable接口&#xff0c;并重写run方法。2、创建一个类继承Thread类&#xff0c;并重写run方法。3、实现Callable接口&#xff0c;重写call()方法&#xff0c;这种方式可以通过FutureTask获取任务执行的返回值。4、run()方法和start()方法有什么区别…...

第一章---Pytorch快速入门---第三节---pytorch中的数据操作和预处理

目录 1.高维数组 1.1 回归数据准备 1.2 分类数据准备 2. 图像数据 1.torchvision.datasets模块导入数据并预处理 2.从文件夹中导入数据并进行预处理 pytorch中torch.utils.data模块包含着一些常用的数据预处理的操作&#xff0c;主要用于数据的读取、切分、准备等。 常用…...

【代码随想录训练营】【Day38】第九章|动态规划|理论基础|509. 斐波那契数|70. 爬楼梯|746. 使用最小花费爬楼梯

理论基础 动态规划与贪心的区别并不是学习动态规划所必须了解的&#xff0c;所以并不重要。 想要了解动态规划算法题的特点&#xff0c;可以直接做下面三道入门简单题练练手感&#xff0c;找找感觉&#xff0c;很快就能体会到动态规划的解题思想。 总结成一句话就是&#xf…...

华为OD机试题 - 快递货车(JavaScript)| 机考必刷

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:快递货车题目输入输出示例一输入输出Code解题思路版权说明华为O…...

前端——7.图像标签和路径

这篇文章&#xff0c;我们来讲解一下图像标签 目录 1.图像标签 1.1介绍 1.2实际展示 1.3图像标签的属性 1.3.1 alt属性 1.3.2 title属性 1.3.3 width / height 属性 1.3.4 border属性 1.4注意事项 2.文件夹 2.1目录文件夹和根目录 2.2 VSCode打开目录文件夹 3.路…...

java -- stream流

写在前面: stream流一直在使用&#xff0c;但是感觉还不够精通&#xff0c;现在深入研究一下。 stream这个章节中&#xff0c;会用到 函数式接口–lambda表达式–方法引用的相关知识 介绍 是jdk8引进的新特性。 stream流是类似一条流水线一样的操作&#xff0c;每次对数据进…...

【Spring6】| Bean的四种获取方式(实例化)

目录 一&#xff1a;Bean的实例化方式 1. 通过构造方法实例化 2. 通过简单工厂模式实例化 3. 通过factory-bean实例化 4. 通过FactoryBean接口实例化 5. BeanFactory和FactoryBean的区别&#xff08;面试题&#xff09; 6. 使用FactoryBean注入自定义Date 一&#xff1a…...

01: 新手学SpringCloud前需知道的5点

目录 第一点&#xff1a; 什么是微服务架构 第二点&#xff1a;为什么需要学习Spring Cloud 第三点&#xff1a; Spring Cloud 是什么 第四点&#xff1a; SpringCloud的优缺点 1、SpringCloud优点 2、SpringCloud缺点 第五点&#xff1a; SpringCloud由什么组成 1&…...

ubuntu apt安装arm交叉编译工具

查找查找编译目标为32位的gcc-arm交叉编译器命令apt-cache search arm|awk index($1,"arm")!0 {print}|grep gcc-arm\|g-arm #或者 apt-cache search arm|awk index($1,"arm")!0 {print}|grep -E gcc-arm|g\\-arm输出如下g-arm-linux-gnueabihf - GNU C co…...

阿里云一面经历

文章目录 ES 查询方式都有哪些?1 基于词项的查询term & terms 查询Fuzzy QueryWildcard Query2 基于全文的查询Match QueryQuery String QueryMatch Phrase Query3 复合查询Bool QueryElasticsearch 删除原理ES 大文章怎么存arthas 常用命令arthas 排查问题过程arthas 工作…...

Java Stream中 用List集合统计 求和 最大值 最小值 平均值

对集合数据的统计&#xff0c;是开发中常用的功能&#xff0c;掌握好Java Stream提供的方法&#xff0c;避免自己写代码统计&#xff0c;可以提高工作效率。 先造点数据&#xff1a; pigs.add(new Pig(1, "猪爸爸", 31, "M", false)); pigs.add(new Pig(…...

【Linux】多线程---线程控制

进程在前面已经讲过了&#xff0c;所以这次我们来讨论一下多线程。前言&#xff1a;线程的背景进程是Linux中资源及事物管理的基本单位&#xff0c;是系统进行资源分配和调度的一个独立单位。但是实现进程间通信需要借助操作系统中专门的通信机制&#xff0c;但是只这些机制将占…...

秒杀高并发解决方案

秒杀高并发解决方案 1.秒杀/高并发方案-介绍 秒杀/高并发 其实主要解决两个问题&#xff0c;一个是并发读&#xff0c;一个是并发写并发读的核心优化理念是尽量减少用户到 DB 来"读"数据&#xff0c;或者让他们读更少的数据, 并 发写的处理原则也一样针对秒杀系统需…...