#创作灵感#(题目均来源于AcWing基础算法课程)
- 记录学习过程
快速排序部分(第一题):快速排序
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N];
void quicksort(int *a , int left , int right) {
if(left >= right )
return ;
//最好用中间的
int temp = a[left+right>>1];
int l = left-1, r = right+1;
while (l < r) {
do l++;
while (a[l] < temp);
do r--;
while (a[r] > temp);
if(l < r )
swap(a[l],a[r]);
}
quicksort(a,left,r);
quicksort(a,r+1,right);
}
int main() {
int m ;
cin>>m;
for (int i = 0 ; i< m ; i++)
cin>>a[i];
//scanf("%d",&a[i]);
quicksort(a,0,m-1);
for(int i = 0 ; i < m ; i++)
cout<<a[i]<<" ";
return 0;
}
注意:
- 快速排序需要找到一个参照,最好是找中间的,因为找两边的话可能会出现边界问题。
- 由于采用的是do..while...语句,所以一开始的左右指针分别需要左移右移一位。
- 递归调用需要注意是 r 和 r+1 ,如果用l的话应该是 l-1 和 l 。
快速排序部分(第二题):第K个数
#include <iostream>
using namespace std;
const int N = 1e5 + 10 ;
int a[N];
void quicksort(int *a,int l, int r) {
if(l >= r) return;
int i = l-1, j = r+1;
int temp = a[(i+j)/2];
while (i < j) {
do i++;
while (a[i] < temp);
do j-- ;
while (a[j] > temp);
if(i < j )
swap(a[i],a[j]);
}
quicksort(a,l,j);
quicksort(a,j+1,r);
}
int main() {
int m ,n ;
cin>>m>>n;
for(int i = 0 ; i < m ; i++){
cin>>a[i];
}
quicksort(a,0,m-1);
cout<<a[n-1];
return 0;
}
- 本质就是快速排序的一个练习
二分法部分(第一题——整数二分):数的范围
//数的范围
//二分法的精髓在于找到边界 ,本质并不是顺序问题
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int a[N];
int main() {
int m,time ;
cin>>m>>time ;
for(int i = 0 ; i < m ; i++)
cin>>a[i];
while(time--) {
int x ;
cin >> x;
//zuobianjie
int l = 0, r = m-1 ;
while (l < r) {
int mid = l + r >> 1 ;
//这个其实就是满足的边界条件
if(a[mid] >= x) r = mid ;
else l = mid + 1 ;
}
if(a[l]==x) {
cout<<l<<" ";
} else cout<<"-1 -1"<<endl;
//youbianjie
l = 0, r = m-1 ;
while (l < r) {
int mid = l + r + 1 >> 1 ;
if(a[mid] <= x) l = mid ;
else r = mid - 1 ;
}
if(a[l] == x) {
cout<<l<<endl;
}
}
return 0;
}
注意:
- 二分法基本上就是上面两个模板,需要注意的是如果更新r = mid - 1 的时候,mid的更新方法为 l+r+1>>1。
- if(满足条件的区间),找的是满足这个区间的边界,一定要关注
- 与小数二分不同的是 r 或者 l 更新的过程中需要在 mid 基础上加减 1 操作
二分法部分(第二题——小数二分):数的三次方
//开三次根
//23的三次方大概是10000
#include <iostream>
using namespace std;
int main(){
double a ;
cin>>a;
double l = -23.0 ,r = 23.0 ;
double mid = 0 ;
//注意循环结束的条件,与整数二分进行对比
while( r-l >= 1e-7 ){
mid = (r + l)/2.0 ;
if(mid*mid*mid >= a ) r = mid ;
else l = mid;
}
printf("%.6lf",mid);
}
注意:
- 需要区分整数二分与小数二分的循环结束条件的区别
- 注意到printf的输出格式,double 对应 lf float对应 f int对应 d
前缀和与差分部分(第一题):前缀和
//前缀和
#include <iostream>
using namespace std;
const int N = 1e5+10;
int a[N];
int main() {
int m , time ;
cin>>m>>time ;
//输入和前缀和计算可以同时进行
for(int i = 1 ; i <= m ; i ++){
cin>>a[i];
a[i] = a[i] + a[i - 1];
}
while (time--) {
int l , r;
cin>> l >> r;
cout<<a[r]-a[l-1]<<endl;
}
return 0;
}
注意:
- 前缀和可以同输入同时进行,可以减少时间复杂度
- 输出的是区间的求和,需要用减法表示出来
- a[0] = 0 ;
前缀和与差分部分(第二题):子矩阵的和
//子矩阵的和
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N];
int main() {
int m ,n ,q;
cin>>m>>n>>q;
for(int i = 1 ; i <= m ; i++)
for(int j = 1 ; j <=n ; j++) {
cin>>a[i][j];
a[i][j] = a[i][j-1] + a[i][j];
}
while(q--) {
int sum = 0 ;
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
for(int i = x1 ; i <= x2 ; i++)
sum += a[i][y2] - a[i][y1-1];
cout<<sum<<endl;
}
return 0;
}
前缀和与差分部分(第三题):差分
//差分
#include <iostream>
using namespace std;
const int N = 1e5 +10 ;
int a[N];
int s[N];
int main() {
int m ,time ;
cin>>m>>time ;
//关键步骤:差分
for(int i = 1 ; i <= m; i++) {
cin>>a[i];
s[i] = a[i] - a[i-1];
}
while (time --) {
int l,r,c;
cin>>l>>r>>c;
s[l] += c;
s[r + 1] -= c ;
}
//前缀和
for(int i = 1 ; i <= m ; i++ ) {
a[i] = s[i] + a[i-1];
cout<<a[i]<<" ";
}
return 0;
}
注意:
- 差分和前缀和本质上是一对逆运算
- 假设:s[N]为我们的原数组,a[N]是我们要求的差分数组。高中学过等差数列就应该知道一个公式,s[n] - s[n - 1] = a[n];前缀和的公式是 s[n] = s[n - 1] + a[n];第一个公式变形可以得到第二个公式,他们都是相通的可以从第一个公式变形为 a[n] = s[n] - s[n - 1];也可以从第二个公式,变形为 a[n] = s[n] - s[n - 1];
前缀和与差分部分(第四题):差分矩阵
//差分矩阵
#include <iostream>
using namespace std;
const int N = 1e3 +10;
int s[N][N];
int a[N][N];
int main() {
int m ,n , time ;
cin>>m>>n>>time;
for(int i = 1 ; i <= m ; i++)
for(int j = 1 ; j <= n ; j++) {
cin>>s[i][j];
//构造差分矩阵
a[i][j] = s[i][j] - s[i][j-1];
}
while (time --) {
int x1,x2,y1,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
for( int i = x1 ; i <= x2 ; i++) {
a[i][y1] += c;
a[i][y2+1] -=c;
}
}
for(int i = 1 ; i <= m ; i++) {
for(int j = 1 ; j <= n ; j++) {
//构造前缀和矩阵
s[i][j] = s[i][j-1] + a[i][j] ;
cout<<s[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
本质上同差分是一样的
双指针算法部分(第一题):最长连续不重复子序列
//最长连续不重复子序列
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int a[N];
int flag[N];
int main() {
int m ;
int ans = 0 ;
cin>>m;
//j:距离当前i最远的位置
for(int i = 0 , j = 0 ; i < m ; i++ ) {
cin>>a[i];
//flag[i]用于记录区间内数的个数
flag[a[i]]++;
//不满足条件了就让j靠近i ,在i向后移动的过程,如果出现重复了,一定是a[i] ,此时将j向后移动
while(j <= i && flag[a[i]] > 1 ) {
//j向后移动
flag[a[j]]--;
j++;
}
//每次满足关系之后更新最大的长度
ans = max(ans , i - j + 1 ) ;
}
cout << ans ;
return 0;
}
双指针算法部分(第二题):数组元素的目标和
二分方法
//数组元素的目标和
#include <iostream>
using namespace std;
const int N = 1e5 + 10 ;
int a[N];
int b[N];
//定义全局变量之后,尽量不要用该名字传参
int erfen(int *c , int len , int x ) {
int l = 0 ;
int r = len - 1 ;
int mid = 0 ;
while (l < r) {
mid = l + r >> 1 ;
if( c[mid] >= x )
r = mid ;
else
l = mid + 1 ;
}
if(c[l] == x) return l;
else
return -1 ;
}
int main() {
int m ,n ,x;
cin>>m>>n>>x;
for(int i = 0 ; i < m ; i ++)
cin>>a[i];
for(int i = 0 ; i < n ; i ++)
cin>>b[i];
for(int i = 0 ; i < m ; i ++) {
if(erfen(b,n,x-a[i])!=-1)
cout<<i<<" "<<erfen(b,n,x-a[i])<<endl;
}
return 0;
}
双指针算法
//数组元素的目标和
#include <iostream>
using namespace std;
const int N = 1e5 + 10 ;
int a[N];
int b[N];
int main() {
int m ,n ,x;
cin>>m>>n>>x;
for(int i = 0 ; i < m ; i ++)
cin>>a[i];
for(int i = 0 ; i < n ; i ++)
cin>>b[i];
for(int i = 0 , j = n-1 ; i < m ; i ++ ) {
while(j >= 0 && a[i] + b[j] > x)
j--;
if(a[i] + b[j] == x) {
cout<<i<<" "<<j;
break;
}
}
return 0;
}
双指针算法部分(第三题):判断子序列
//判断子序列
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int a[N];
int b[N];
int main() {
int m ,n ;
cin>>m>>n;
for(int i = 0 ; i< m; i++) cin>>a[i];
for(int i = 0 ; i< n; i++) cin>>b[i];
int i = 0 , j = 0 ;
while(i<m && j < n) {
if(a[i]==b[j]) i++;
j++;
}
if(i == m) cout<<"Yes";
else cout<<"No";
}
位运算部分(第一题):二进制中1的个数
//二进制中1的个数
#include <iostream>
using namespace std;
int lowbit(int x){
return x & -x ;
}
int main(){
int time ;
cin>>time;
while (time--){
int m ;
cin>>m;
int ans = 0 ;
while(m){
m -=lowbit(m);
ans++;
}
cout<<ans<<" ";
}
return 0;
}
注意:
- lowbit()函数的意义:找到最后一个1的位置
区间和与区间合并省略......