AcWing基础算法课学习记录

#创作灵感#(题目均来源于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;
}

注意:

  1. 快速排序需要找到一个参照,最好是找中间的,因为找两边的话可能会出现边界问题。
  2. 由于采用的是do..while...语句,所以一开始的左右指针分别需要左移右移一位。
  3. 递归调用需要注意是 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;
}

注意:

  1. 二分法基本上就是上面两个模板,需要注意的是如果更新r = mid - 1 的时候,mid的更新方法为 l+r+1>>1。
  2. if(满足条件的区间),找的是满足这个区间的边界,一定要关注
  3. 与小数二分不同的是 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);	
} 

注意:

  1. 需要区分整数二分与小数二分的循环结束条件的区别
  2. 注意到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;
}

注意:

  1. 前缀和可以同输入同时进行,可以减少时间复杂度
  2. 输出的是区间的求和,需要用减法表示出来
  3. 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;
}

注意:

  1. 差分和前缀和本质上是一对逆运算
  2. 假设: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; 
} 

注意:

  1. lowbit()函数的意义:找到最后一个1的位置

区间和与区间合并省略......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值