#学习自用#
算法性能分析
时间复杂度O()
时间复杂度就是算法计算的次数。
for(int i=0;i<=n;i++)
{
ans++;
}
ans++;
这串代码时间复杂度为O(n),实际时间复杂度为O(n+1)。如果把i++改为i+=2,时间复杂度仍然为为O(n),实际时间复杂度变为O(n/2 +1)。时间复杂度比较像极限里面的抓大头,实际时间复杂度就是字面意思很好理解。
常见的时间复杂度:如果是有限次数的都是O(1)、O(log n)、O(n*log n)、O(n)、O(n^2)、O(n^2)。
空间复杂度
处理算法时,额外产生的空间。
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
for(int j=n-1;j>0;j--){
for(int i=0;i<n-1;i++){
if(a[i]>a[i+1]){
swap(a[i],a[i+1]);
}
}
}
在这个冒泡排序算法中,a[i] 是储存原始数据所需要的数组所以不算在额外空间中,而算法的部分看似没有额外的变量,实际上是需要一个临时变量来存储数据以实现交换的,只需要有限的额外空间,空间复杂度为O(1)。
常见的空间复杂度:O(1)、O(log n)、O(n*log n)
稳定性
通常指的是排序算法的一种特性。在排序算法中,如果两个相等的元素在排序前后的相对位置保持不变,那么这个算法就是稳定的。
高精度计算
用于处理可能会越界的大数。
高精度加法
这里我们需要用到string和整型数组的特性,string作为字符串变量不管怎么输入都不会越界,而整型数组可以用每个元素储存一个数字。
#include<iostream>
#include<string>
using namespace std;
void StrtoInt(const string&s,int* a)
{
for (int i = 0; i < s.size(); i++)
{
a[s.size() - 1 - i] = s[i] - '0';//将数字反转存入数组,目的是将个位对齐。
}
}
int main()
{
string s1, s2;
int a[100] = {}, b[100] = {}, c[100] = {};
cin >> s1 >> s2;
StrtoInt(s1, a);
StrtoInt(s2, b);
int la = s1.size(), lb = s2.size();
int lc = max(la, lb) + 1;//计算结果可能的最大位数
int CarryBit = 0;//设置进位
for (int i = 0; i < lc; i++)
{
c[i] = a[i] + b[i]+CarryBit;
if (c[i] >= 10)
{
c[i] -= 10;
CarryBit = 1;
}
else
CarryBit = 0;
}
while (c[lc - 1] == 0 && lc > 1)
lc--;
for (int i = 0; i < lc; i++)
{
cout << c[lc - 1 - i];
}
cin.get();
}
将字符1赋值给int类型,本质上是把字符1的ASC码值赋值给变量,得到整型的数字就需要把数字字符与字符0相减。将数字反转存放是为了使各个数位上的数字对齐,如果不反转,需要想办法在数位更低的数组,在其数字最高位前面补零,相当麻烦 。while (c[lc - 1] == 0 && lc > 1)是为了去除前置零,即最高位为0时,将输出位数减少。
高精度减法
这里依旧使用string与数组的特性。
#include<iostream>
#include<string>
using namespace std;
void StrtoInt(const string&s,int* a)
{
for (int i = 0; i < s.size(); i++)
{
a[s.size() - 1 - i] = s[i] - '0';//将数字反转存入数组,目的是将个位对齐。
}
}
bool MyStrcmp(const string& str1,const string& str2)
{
if (str1.size() != str2.size())//位数不同
return str1.size() > str2.size();
else
return str1 > str2;//位数相同
}
int main()
{
string A, B;
int a[100] = {}, b[100] = {}, c[100] = {};
cin >> A >> B;
if (MyStrcmp(A, B))
{
StrtoInt(A, a);
StrtoInt(B, b);
}
else
{
StrtoInt(B, a);
StrtoInt(A, b);
cout << '-';
}//保证更大的数字赋值给数组a
//执行减法
int lc = max(A.size(), B.size());
for (int i = 0; i < lc; i++)
{
if (a[i] - b[i] >= 0)
c[i] = a[i] - b[i];
else
{
a[i + 1] -= 1;
c[i] = 10 + a[i] - b[i];
}
}
//反转输出,去前置零
while (c[lc - 1] == 0 && lc > 1)
lc--;
for (int i = 0; i < lc; i++)
cout << c[lc - 1 - i];
cin.get();
}
与加法不同,减法需要考虑从高位退位,以及相减为负数的情况。相减为负数时,负数的值依旧是大数减去小数,而符号我们可以提前输出,如果不这样处理,输出的高位以及输出中间都可能出现负数。
不管是加法还是减法都记得把在数组定义时初始化,否则数组中全是一些随机数,如果被减数与减数位数不同,不将数组初始化,c[i]=a[i]-b[i]运行到随机数出现的地方出错。
高精度乘法
#include<iostream>
#include<string>
using namespace std;
void StrtoInt(const string&s,int* a)
{
for (int i = 0; i < s.size(); i++)
{
a[s.size() - 1 - i] = s[i] - '0';//将数字反转存入数组,目的是将个位对齐。
}
}
int main()
{
string A, B;
int a[100] = {}, b[100] = {}, c[100] = {};
cin >> A >> B;
StrtoInt(A, a);
StrtoInt(B, b);
int la = A.size(), lb = B.size(),lc=la+lb;
for(int j=0;j<lb;j++)
for (int i = 0; i < la; i++)
{
c[i + j] += a[i] * b[j];
}
for (int i = 0; i < lc - 1; i++)
{
c[i + 1] += (c[i] / 10);
c[i] %= 10;
}
while (c[lc - 1] == 0 && lc > 1)
lc--;
for (int i = 0; i < lc; i++)
cout << c[lc - 1 - i];
cin.get();
}
高精度乘法的关键是确定相乘后最多有几位,这里通过一个例子来理解,结果位数越多代表结果越大,而要得到最大的乘积,两个乘数也必须是最大的,例如9x9=81,这里乘积的位数是两个乘数位数之和,通过数学归纳法我们可以知道,乘积的位数最多是两个乘数位数之和。
高精度除法
准确来说是高精度除以低精度。
#include<iostream>
#include<string>
using namespace std;
void StrtoInt2(const string& s, int* a)
{
for (int i = 0; i < s.size(); i++)
{
a[i] = s[i] - '0';
}
}
int main()
{
string A;
int B;
int a[100] = {}, c[100] = {};
cin >> A;
cin >> B;
StrtoInt2(A, a);
int lc=0;
int la = A.size();
int temp = 0;//记录余数
for (int i = 0; i < la; i++)
{
c[i]=a[i] / B;
temp=(a[i] % B);
a[i + 1] += temp * 10;
}
while (c[lc] == 0 && lc < la)lc++;
for (int i = lc; i < la; i++)
cout << c[i];
cout <<endl<< temp;
}
高精除以高精
#include<iostream>
#include<string>
using namespace std;
void Reverse_StrtoInt(const string& str, int* arr)
{
for (int i = 0; i < str.size(); i++)
{
arr[str.size() - 1 - i] = str[i] - '0';
}
}
bool Judge(int* M, int* N, const int lb)//做除法不会出现对齐之后前面两位还有余数的情况,最多前面一位会有
{
//M对应结果位所在元素位置
if (M[lb] > 0)return true;
//超出位没有余数,从对齐的最高位比较
for (int i = lb - 1; i >= 0; i--)
{
if (M[i] > N[i])return true;//依旧是按第一次符合条件的结果返回
else if (M[i] < N[i])return false;
}
return true;
}
void Division(int* M, const int la, int* N, const int lb,int* result,const int lc)
{
if(la>=lb)
{
for(int i=lc-1;i>=0;i--)//i是输出元素位置
while (Judge(M + i, N, lb))
{
result[i] += 1;
for (int j = 0; j < lb; j++)
{
if (M[i + j] >= N[j])
M[i + j] -= N[j];
else
{
M[i + j+ 1] -= 1;
M[i + j] = 10 + M[i + j] - N[j];
}
}
}
}
}
void Del_PresetZero(int* result, int& lc)
{
while (result[lc - 1] == 0 && lc > 1)lc--;
}
void ReversePrint(int* result, const int lc)
{
for (int i = 0; i < lc; i++)
cout << result[lc - 1 - i];
cout << endl;
}
int main()
{
int M[300] = {}, N[300] = {}, result[300] = {};
string A, B;
cin >> A;
cin >> B;
int la = A.size(), lb = B.size();
int lc =la-lb+1;//这个lc是长度不是输出元素位置
Reverse_StrtoInt(A, M);
Reverse_StrtoInt(B, N);
if (la < lb)
{
cout << "0\n";
cout << A;
}
else
{
Division(M, la, N, lb, result, lc);
Del_PresetZero(result, lc);
Del_PresetZero(M, la);
ReversePrint(result, lc);
ReversePrint(M, la);
}
}
高精除高精的难点在于判断是否可以除(被除数位数大于等于除数的情况),只要被除数最高位数字大于除数其实就可判断可以除,但是如果相等就得判断下一位,如果直到最后一位也相等那也是可以除的情况,也就是judge函数里面的for循环语句以及最后的return表示的情况。
if (M[lb] > 0)return true;主要是判断上一位除完之后,余数的情况,只需要判断比除数位数高一位的情况,是因为从高位除的过程中不可能出现比除数高两位的地方还有余数的情况。