复习FIRST

1、快速排序

第一行输入n为数字个数: 5

第二行输入n个数字为其排序:5 1 3 2 4

输入:

5
3 1 2 4 5

快速排序是在先找定中间数a[m]的基础上,从左找比a[m]大的数,从右找比a[m]小的数,交换这两个数:
经过第一次寻找后的状态: 2 1 3 5 4
此时 i=0,j=3
在(i<j)的情况下继续进行上一步直到跳出;
跳出时i=3;
这是为了保证在中间数的左右侧分别是比中间数小和大的数;
注意:a[m]的值是可以变的,所以最后 j 的位置才是分割两部分的位置;
此时进行下一步:

quick_sort(l,j);
quick_sort(j+1,r);

这是为了单独对中间数左和右侧进行上述操作:
经过一层递归后的状态:1 2 3 4 5
此时排序完成。

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+5;
int a[N],n;

void quick_sort(int l,int r) {
	if(l>=r) return;
	int i=l-1,j=r+1,m=(l+r)>>1;
	while(i<j) {
		do i++;
		while(a[i]<a[m]);
		do j--;
		while(a[j]>a[m]);
		if(i<j) {
			swap(a[i],a[j]);
		}
	}
	quick_sort(l,j);
	quick_sort(j+1,r);
}

int main() {
	cin>>n;
	for(int i=0; i<n; i++) {
		cin>>a[i];
	}
	quick_sort(0,n-1);
	for(int i=0; i<n; i++) {
		cout<<a[i]<<" ";
	}
	return 0;
}

2、归并排序

第一行输入n为数字个数: 5

第二行输入n个数字为其排序:5 1 3 2 4

输入:

5
3 1 2 4 5

//排序之外多加一个:输出逆序数(注意当排列中有相同的数字时,就不能用这个方法求逆序数)

归并排序用的就是一种分而治之和递归的思想;

可以用一个gif动画来理解:
在这里插入图片描述
对原数组先对半分,分到只有两个开始进行对比和排序,再递归。以中点为界,左边从 L 开始,右边从 mid+1 开始,用一个新的数组从小到大存储 LR 中的数,再用一个for循环更新原来的数组。

另外可以用归并排序来计算排列中的逆序数:
因为 i 始终在 j 的左侧,所以当 a[j]<a[i] 时,mid-i+1就是 a[j] 此时前移的位置,存储在ans中,最后的值就是排列的逆序数;

mid-i+1:此时 i 指向的是前半部分已经取出的下一个数,因为当ans增加时,a[j] 所移动到的位置必然在前面被取出的 j之后,所以 a[j] 前移的长度只与 i 有关;

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+5;
int a[N],t[N],n,ans;//ans用来存总逆序数

void merge_sort(int l,int r) {
	if(l>=r) return;
	int mid=(l+r)>>1;
	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]) t[k++]=a[i++];
		else {
			t[k++]=a[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid) t[k++]=a[i++];
	while(j<=r) t[k++]=a[j++];
	for(i=l,j=0; i<=r; i++,j++) {
		a[i]=t[j];
	}
	return;
}

int main() {
	cin>>n;
	for(int i=0; i<n; i++) {
		cin>>a[i];
	}
	merge_sort(0,n-1);
	for(int i=0; i<n; i++) {
		cout<<a[i]<<" ";
	}
	cout<<"ans: "<<ans<<endl;
	return 0;
}

3、二分

第一行输入 n,m : 6 3
第二行输入 n 个升序的数字:1 2 2 3 3 4
后 m 输入每一个要查询的数字,输出他的起始位置和终止位置:3 4 5
若数字不存在输出:-1 -1

输入:

6 3
1 2 2 3 3 4
3
4
5

二分查找缩短时间复杂度,暴力复杂度为O(n),二分复杂度为O(logn)

整一个查找分为左偏和右偏两个部分
左偏:

while(l<r){
int mid=(l+r)>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}

此时 l 寻找的是这个数的起始位置,所以当 q[mid]>=xr 指向 mid ,当 q[mid]<x 时,l 指向 mid+1 ,而 l 最后指向的应该是第一个大于或等于 x 的值;

右偏:

while(l<r){
int mid=(l+r+1)>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}

此时 l 寻找的是这个数的终止位置,所以当 q[mid]>x 时,r 指向 mid-1,当 q[mid]<=xl 指向 mid ,而 l 最后指向的应该是最后一个等于 x 的值;

注意此时 mid=(l+r+1)>>1,这也是右偏特色,如果不加1会导致死循环,例如:查找3时,当 l 指向4,r 指向5时,对应的数字分别是 3,4,mid=4+5>>1=4q[mid]=3<=3l=3,陷入死循环…

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+5;

int main(){
	int n,m;
	int q[N];
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>q[i];
	}
	while(m--){
		int x;
		cin>>x;
		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) cout<<"-1 -1"<<endl;
		else {
			cout<<l<<" ";
			int l=0,r=n-1;
			while(l<r){
				int mid=(l+r+1)>>1;
				if(q[mid]<=x)  l=mid;
				else r=mid-1;
			}
			cout<<l<<endl;
		}
	}
	return 0;
} 

3.1、数的三次方根

输入浮点数 n:

1000.00

求出他的三次方根并保存六位小数输出;

这题就是用的二分思想查找三次方根;

while(r-l>1e-8)

这里是为了保证 lr 之间相差足够小时跳出,这样精度更高;

注意:浮点数不能使用位运算,还是老老实实 /2 吧;

#include<iostream>

using namespace std;

int main(){
	double x;
	cin>>x;
	double l=-100,r=100;
	while(r-l>1e-8){
		double mid=(l+r)/2;
		if(mid*mid*mid>=x) r=mid;
		else l=mid;
	}
	printf("%.6lf\n",l);
	return 0;
}

4、高精度

接触STL,利用vector来做高精度:

4.1 大加

因为 int 最多存九位,所以将字符串分成每九位存进 vector 容器中,在计算时直接进行+运算,同时用1000000000取模后存入容器 c 中,最后输出 c ;

#include<bits/stdc++.h>

using namespace std;
const int N=1000000000;

vector<int> add(vector<int> &a,vector<int> &b) {
	if(a.size()<b.size()) return add(b,a);
	vector<int> c;
	int t=0;
	for(int i=0;i<a.size();i++){
		t=a[i];
		if(i<b.size()) t+=b[i];
		c.push_back(t%N);
		t/=N;
	}
	if(t) c.push_backe(t);
	return c;
}

int main() {
	string n,m;
	cin>>n>>m;
	vector<int> a,b;
	for(int i=n.size()-1,s=0,j=0,t=1; i>=0; i--) {
		s+=(n[i]-'0')*t;
		j++,t*=10;
		if(j==9||i==0) {
			a.push_back(s);
			s=j=0;
			t=1;
		}
	}
	for(int i=m.size()-1,s=0,j=0,t=1; i>=0; i--) {
		s+=(m[i]-'0')*t;
		j++,t*=10;
		if(j==9||i==0) {
			b.push_back(s);
			s=j=0;
			t=1;
		}
	}
	auto c=add(a,b);
	cout<<c.back();
	for(int i=c.size()-2;i>=0;i--){
		cout<<c[i];
	}
	return 0;
}

4.2 大减

大减就是先将字符串转变成数字倒序存入 vector 中,用 cmp 判定减的顺序,保证大的数减小的数,如果相减顺序与输入顺序相反,则输出 “-” ;

t 的作用是存储借位,如果前一位是小的数减大的数,t 存 1 ,t=a[i]-t 就是实现借位的过程;

#include<bits/stdc++.h>

using namespace std;

bool cmp(vector<int> &a,vector<int> &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];
		}
	}
}

vector<int> sub(vector<int> &a,vector<int> &b) {
	vector<int> c;
	for(int i=0,t=0;i<a.size();i++){
		t=a[i]-t;
		if(i<b.size()) t-=b[i];
		c.push_back((t+10)%10);
		if(t<0) t=1;
		else t=0;
	}
	while(c.size()>1&&c.back()==0) c.pop_back();
	return c;
}

int main() {
	string n,m;
	cin>>n>>m;
	vector<int> a,b;
	for(int i=n.size()-1;i>=0;i--){
		a.push_back(n[i]-'0');
	}
	for(int i=m.size()-1;i>=0;i--){
		b.push_back(m[i]-'0');
	}
	vector<int> c;
	if(cmp(a,b)) c=sub(a,b);
	else c=sub(b,a),cout<<"-";
	for(int i=c.size()-1;i>=0;i--){
		cout<<c[i];
	}
	return 0;
}

4.3 大乘

因为一般大乘都是一个超大数和一个整型数相乘,所以读入一个 string 和一个 int 来进行乘法运算;

#include<bits/stdc++.h>

using namespace std;

vector<int> mul(vector<int> &a,int b) {
	vector<int> c;
	int t=0;
	for(int i=0; i<a.size()||t; i++) {
		if(i<a.size()) t+=a[i]*b;
		c.push_back(t%10);
		t/=10;
	}
	while(c.size()>1&&c.back()==0) c.pop_back();
	return c;
}

int main() {
	string n;
	int b;
	cin>>n>>b;
	vector<int> a;
	for(int i=n.size()-1; i>=0; i--) {
		a.push_back(n[i]-'0');
	}
	auto c=mul(a,b);
	for(int i=c.size()-1; i>=0; i--) {
		cout<<c[i];
	}
	return 0;
}

4.4 大除

正向输入,正向相除;

因为最后需要去掉前置0,所以需要加一个 reverse 函数使 vector 变量翻转;

#include<bits/stdc++.h>

using namespace std;

vector<int> div(vector<int> &a,int b,int &r) {
	vector<int> c;
	r=0;
	for(int i=0;i<a.size();i++) {
		r=r*10+a[i];
		c.push_back(r/b);
		r%=b;
	}
	reverse(c.begin(),c.end());
	while(c.size()>1&&c.back()==0) c.pop_back();
	return c;
}

int main() {
	string n;
	int b;
	cin>>n>>b;
	vector<int> a;
	for(int i=0;i<n.size();i++) {
		a.push_back(n[i]-'0');
	}
	int r;
	auto c=div(a,b,r);
	for(int i=c.size()-1; i>=0; i--) cout<<c[i];
	cout<<endl<<r<<endl;
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值