算法基础课
本博客基于acwing算法基础课,所做笔记
目的在于方便复习
课程链接:https://www.acwing.com/activity/content/introduction/11/
主讲人:yxc
上课的时候理解算法的主要思想
课后把模板背过,并且调试题目,多重复,多敲
第一讲 基础算法
快速排序
调整区间的方法:
1、暴力,需要开辟两个数组
2、双指针
1、i往后面扫描,当i指向的数字小于等于x,就继续往后,当指向的数字大于x的时候停下
2、此时j往前面扫描,当j指向的数字大于x,就继续往前,当指向的数字小于x的时候,停下
3、此时i指向的大于x,j指向的小于x,那么这两个数字交换,并且两个指针都往中间移动一位,直到i和j穿过
可以发现,任何时候,i右边的数字都小于等于x,j左边的数字都大于x
模板:
void quick_sort(int q[],int l,int r){
if(l >= r) return; // 如果区间内只有一个或者没有数字,跳出循环
int x = q[l + r >> 1],i = l -1,j = r + 1;
while(i < j){
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i],q[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
快速排序
https://www.acwing.com/problem/content/787/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int q[N];
void quick_sort(int q[],int l,int r){
if(l >= r) return;
int x = q[l + r >> 1],i = l -1,j = r + 1;
while(i < j){
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i],q[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
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;
}
第k个数
https://www.acwing.com/problem/content/788/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],n,k;
int quick_sort(int l,int r,int k){
if(l == r) return a[l];
int i = l - 1,j = r + 1,x = a[(l+r) >> 1];
while(i < j){
while(a[++ i] < x); // 这里只能是++i,不能是i--
while(a[-- j] > x);
if(i < j) swap(a[i],a[j]);
}
int sl = j - l + 1;
if(k <= sl) return quick_sort(l,j,k);
else return quick_sort(j+1,r,k-sl);
}
int main(){
scanf("%d%d",&n,&k);
for(int i = 0 ;i < n;i++) scanf("%d",&a[i]);
printf("%d\n",quick_sort(0,n-1,k));
return 0;
}
快速选择算法
归并排序
如何归并
双指针
归并的思路:
1、先递归,将序列一直分半,直到只剩一个元素
2、归并,归并采取双指针
模板:
void merge_sort(int q[],int l,int r){
if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了
int mid = (l + r) >> 1; // mid是中间那个数字
merge_sort(q,l,mid); // 归并排序先递归
merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了
int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点
while(i <= mid && j <= r){
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(i = l,j = 0;i <= r;i++,j++) q[i] = tmp[j];
}
归并排序
https://www.acwing.com/problem/content/789/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int q[N],tmp[N],n;
void merge_sort(int q[],int l,int r){
if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了
int mid = (l + r) >> 1; // mid是中间那个数字
merge_sort(q,l,mid); // 归并排序先递归
merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了
int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点
while(i <= mid && j <= r){
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(i = l,j = 0;i <= r;i++,j++) q[i] = tmp[j];
}
int main(){
scanf("%d",&n);
for(int i = 0;i < n;i++) scanf("%d",&q[i]);
merge_sort(q,0,n-1);
for(int i = 0;i < n;i++) printf("%d ",q[i]);
return 0;
}
逆序对的数量
https://www.acwing.com/problem/content/790/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],temp[N];
long long merge_sort(int l,int r){
if(l >= r) return 0;
int mid = (l + r) >> 1;
long long res = merge_sort(l,mid) + merge_sort(mid+1,r);
// 归并
int k = 0,i = l,j = mid + 1;
while(i <= mid && j <= r) {
if(a[i] <= a[j]) temp[k++] = a[i++];
else {
temp[k++] = a[j++];
res += mid - i + 1;
}
}
while(i <= mid) temp[k++] = a[i++];
while(j <= r) temp[k++] = a[j++];
for(i = l,k = 0;i <= r;i++,k++) a[i] = temp[k];
return res;
}
int main(){
int n;
scanf("%d",&n);
for(int i = 0;i < n;i++) scanf("%d",&a[i]);
printf("%lld\n",merge_sort(0,n-1));
return 0;
}
二分
模板:https://www.acwing.com/blog/content/31/
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
数的范围
https://www.acwing.com/problem/content/791/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int q[N],m,n;
int main(){
scanf("%d %d",&n,&m);
for(int i = 0;i < n;i++) scanf("%d",&q[i]);
while(m--){
int x;
scanf("%d",&x);
// 先不管check函数,先把l,r和while,mid写好
int l = 0,r = n - 1;
while(l < r){
int mid = (l + r) >> 1;
if(q[mid] >= x) r = mid;
else l = mid + 1;
}
if(q[l] != x) puts("-1 -1");
else{
printf("%d ",l);
l = 0,r = n - 1;
while(l < r){
int mid = (l + r + 1) >> 1;
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
printf("%d\n",l);
}
}
return 0;
}
浮点数二分
开平方
#include <bits/stdc++.h>
using namespace std;
int main(){
double x;
cin >> x;
double l = 0,r = x;
while(r - l > 1e-6){
double mid = (l + r) / 2;
if(mid * mid >= x) r = mid;
else l = mid;
}
printf("%lf\n",l);
return 0;
}
数的三次方根
https://www.acwing.com/problem/content/792/
#include <bits/stdc++.h>
using namespace std;
int main(){
double n;
scanf("%lf",&n);
double l = -10000,r = 10000;
while(r-l > 1e-8){
double mid = (l + r) / 2;
if(mid * mid * mid > n) r = mid;
else l = mid;
}
printf("%lf\n",l);
return 0;
}