本周复习的知识点有:排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并。
算法总结
快速排序
void qsort(int a[], int l, int r)
{
if (l >= r) return;
int x = a[l + r >> 1];
swap(a[l + r >> 1], a[l]);
int i = l, j = r;
while (i < j)
{
while (i < j && a[j] > x) j -- ;
if (i < j) a[i ++ ] = a[j];
while (i < j && a[i] < x) i ++ ;
if (i < j) a[j -- ] = a[i];
}
a[i] = x;
qsort(a, l, i - 1);
qsort(a, i + 1, r);
}
时间复杂度:O(nlogn)
归并排序
void msort(int a[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
msort(a, l, mid);
msort(a, mid + 1, r);
int i = l, j = mid + 1;
int b[N], k = 0;
while (i <= mid && j <= r)
{
if (a[i] < a[j]) b[k ++ ] = a[i ++ ];
else b[k ++ ] = a[j ++ ];
}
while (i <= mid) b[k ++ ] = a[i ++ ];
while (j <= r) b[k ++ ] = a[j ++ ];
k = 0;
for (int i = l; i <= r; i ++ ) a[i] = b[k ++ ];
}
时间复杂度:O(nlogn)
位运算
求数n的二进制第k位数字:n >> (k – 1) & 1
返回数n的最后一位1:lowbit(n) = n & -n
双指针算法
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
//具体逻辑
}
时间复杂度:O(n)
二分
模板1
int bsearch1(int l, int r)
{
while (l < r)
{
int mid = (l + r)/ 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
模板2
int bsearch2(int l, int r)
{
while (l < r)
{
int mid = (l + r + 1) / 2;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
时间复杂度:O(logn) * check的时间复杂度
高精度
加法
string add(string a, string b)
{
string c = "";
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int t = 0; //进位
int n = max(a.size(), b.size());
for (int i = 0; i < n; i ++ )
{
if (i < a.size()) t += a[i] - '0';
if (i < b.size()) t += b[i] - '0';
c += '0' + t % 10;
t /= 10;
}
if (t) c += '0' + t % 10;
reverse(c.begin(), c.end());
return c;
}
减法
bool check(string a, string b) //判断大小
{
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
}
string sub(string a, string b)
{
bool f = false;
if (check(a, b))
{
swap(a, b);
f = true;
}
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
string c;
int t = 0; //退位
for (int i = 0; i < a.size(); i ++ )
{
t += a[i] - '0';
if (i < b.size()) t -= b[i] - '0';
if (t < 0)
{
c += '0' + t + 10;
t = -1;
}
else
{
c += '0' + t;
t = 0;
}
}
reverse(c.begin(), c.end());
int i = 0;
while (c[i] == '0' && i + 1 < c.size()) i ++ ; //去除前导零
c = c.substr(i, c.size() - i);
if (c.size() == 0) c = '0';
if (f) c = '-' + c;
return c;
}
乘法
大数 × int型
string mul(string a, int b)
{
string c;
reverse(a.begin(), a.end());
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += (a[i] - '0') * b;
c += t % 10 + '0';
t /= 10;
}
while (t)
{
c += t % 10 + '0';
t /= 10;
}
reverse(c.begin(), c.end());
int i = 0;
while (c[i] == '0' && i + 1 < c.size()) i ++ ;
c = c.substr(i, c.size() - i);
return c;
}
除法
大数 ÷ int型
string div(string a, int b, int &d) //d为余数
{
string c;
for (int i = 0; i < a.size(); i ++ )
{
d = d * 10 + a[i] - '0';
c += d / b + '0';
d = d % b;
}
int i = 0;
while (i + 1 < c.size() && c[i] == '0') i ++ ;
c = c.substr(i, c.size() - i);
return c;
}
大数 ÷ 大数 可以用高精度减法实现,每次减去除数即可
其它总结
- sscanf、sprintf函数可用于字符串和数字间的互换
sscanf用法
int sscanf( const char *buffer, const char *format, ... );
返回值为成功赋值的个数,其它用法和scanf相同,string类型需要用c_str()转换
sprintf用法
int sprintf(char *string, char *format [,argument,...]);
-
long long格式化输出时,Linux环境下为
"lld"
Windows环境下为"l64d"
-
1 << k
表示2的k次方 -
二维
vector<vector<int>> a
行数:a.size()
列数:a[0].size()
-
INT_MAX是int型的最大值,判断是a * 10是否超限,用
a > INT_MAX / 10
INT_MAX=2^31-1
-
int型负数转换为unsigned int时,相当于第32数位变为1
-
位运算是补码进行运算
-
二分查找函数(基于已排序的序列)
lower_bound (beg,end,val)
返回一个迭代器,指向非递减序列[first, last)中的第一个大于等于(>=)val的位置
upper_bound (beg,end,val)
返回一个迭代器,指向非递减序列[first, last)中的第一个大于 (>) val的位置 -
具有二分性不一定是对排序的序列,也可能是满足某条件
本周训练拓展题
- 差分 acwing101
- 离散化 acwing103
- 前缀和 leetcode 1504
- 双指针 acwing1528
- 位运算 acwing90
- 区间合并 acwing422
- 归并排序 acwing107
- 二分 acwing102
- 高精度乘法 acwing1481
- 枚举 acwing1537