编码技巧C++

编码技巧C++

非零都是true

在c++环境下不等于0的数值都被认为是true

在判断一个值是否为0时以下代码是等效的,但第一种效率更高

int i = 123;
if (i) cout << "i不为0";
if (i != 0) cout << "i不为0";

不需要用到下标的计数循环可以不用for语句

int n; cin >> n;
while(n--) {
    
}

质数判断不用从2除到n-1

提高执行效率质数判断仅需要从2除到\(\sqrt{n}\)即可

注意这里是需要除到\(\sqrt{n}\)的,循环条件为\(i \leqslant \sqrt{n}\)

计算机运行开根号运算效率低,采用 i * i <= n作为循环条件

int n;
bool prm = true; // 假设是质数 
for (int i = 2; i * i <= n; ++i) {
    if (n % i == 0) { // 如果能整除,则不是质数,退出循环
        prm = false;
        break;
    }
}

更高效的方法不用每次都计算\(i\times i\),而是在循环外计算出\(\sqrt{n}\)

int n;
int sqn = floor(sqrt(n));
bool prm = true; // 假设是质数 
for (int i = 2; i <= sqn; ++i) {
    if (n % i == 0) { // 如果能整除,则不是质数,退出循环
        prm = false;
        break;
    }
}

通过按位与判断一个数是不是偶数

整数在二进制表示最低位如果是1代表一定是奇数

按位与判断奇偶效率比对2求余数效率更高,以下代码等效,但第一种效率更高

uint8_t a = 0b00000001;
int i = 8;

if (i & a) cout << "i是奇数";
if (i % 2) cout << "i是奇数";

通过异或操作交换两个数

int x = 3, y = 5;
x ^= y;
y ^= x;
x ^= y;

此方法和加减法交换类似,但是效率比加减法高。通常数值交换最多使用的是中值交换法

int x = 3, y = 5;
x = x + y;
y = x - y;
x = x - y;

通过右移一位来完成除以2的操作

下面的右移操作和除以2的操作等效,第一种效率更高

int n = 100;
n >>= 1; //右移一位
n /= 2; //除以2


数组通过取模换算下标,环绕取元素

如果一个下标值超过数组可以通过对下标值取模来环绕取元素

int idx = 12345;
int a[100] = {}; // 使用大括号会自动将数组置零
int n = a[idx % 100];

// 如果数组空间没有完全使用,可以根据对已使用长度取模方法来环绕取元素
int lenUsed = 50;
n =  a[idx % lenUsed];

使用getline函数从流中读取一行

cin.getline()方法需要使用字符数组及长度两个参数

使用getline函数可直接读取一行到string类型

// cin.getline()方法的使用方法
char s[101] = {0};
cin.getline(s,100); 

//getline()函数的使用方法
string s;
getline(cin, s);

注意:使用cin.getline()方法和直接使用getline()函数的时候需要考虑上一行末尾的回车,如下代码

int n;
char a[100];
cin >> n; // 此处读取n之后缓存中还有一个回车未被读取
cin.getline(a, 100); // 由于有一个回车未被读取,所以这里读取到的是空字符串
cout << a;

采用getline()函数的方法优化为

int n;
string s = "";
cin >> n;
do {
    getline(cin, s);
} while(s.empty()); // 如果读取后为空则继续读取
cout << s;

局部数组不会被初始化

全局数组会被自动初始化为0,局部数组需要手动初始化

int a[10] = {0}; 
for (int i = 0; i < 10; ++i)
    cout << a[i] << " ";

输出:

0 0 0 0 0 0 0 0 0 0

如果只初始化部分内容,其余部分自动填零

int a[10] = {1, 2};
for (int i = 0; i < 10; ++i)
    cout << a[i] << " ";

输出:

1 2 0 0 0 0 0 0 0 0

使用memset函数初始化数组为0

注意:多维数组初始化应使用此函数

使用cstring中的函数memset将数组中的字节设置为零,注意数值代表的是一个字节的值

#include <iostream>
#include <cstring>

using namespace std;

int main() {
    int a[10];
    memset(a,0,sizeof(a));
    for (int i = 0; i < 10; ++i) 
        cout << a[i] << " ";
    cout << endl;
    memset(a,255,sizeof(a));
    for (int i = 0; i < 10; ++i)
        cout << a[i] << " ";
}

输出:

0 0 0 0 0 0 0 0 0 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1

在for循环中使用无符号类型循环变量不能用小于零来进行判断

size_tuint32_t等无符号类型不能用 i >= 0来进行判断,因为无符号永远大于等于0

// 此处i始终是大于等于0的,所以循环会一直执行
for (size_t i = 31; i >= 0; --i) 
    cout << i << endl;

超长整数进制转换

原数不断除以新进制并取余,然后组成新进制数

#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;

// 此题要点:如果当前位不够整除新进制,则循环按照旧进制向高位借位
typedef vector<int> Vint;

// 进制
string base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

// 返回对应字符的数值
size_t ValOfC(char c) {
    return base.find(c);
}

// 返回数值对应的字符
char COfVal(int val) {
    return base[val];
}

// 将一个字符串中的字符转换成数字逆序存入vector
void StrToVec(string& s, Vint& v) {
    for (char c: s) {
        v.push_back(ValOfC(c));
    }
}

// 判断v是否为0
bool ISZero(Vint & v){
    bool r = true;
    for (int i: v) {
        if (i != 0) {
            r = false;
            break;
        }
    }
    return r;
}


// 执行进制转换
string Trans(int From, string s, int To) {
    string r = s;
    if (From != To) { // 如果进制不同
        Vint v; // 用于保存数据
        Vint ToV; // 用于保存结果的
        StrToVec(s, v);
        while (!ISZero(v)) {
            int rem = 0; // 余数
            int tmp = 0; // 临时变量
            for(int & i: v) { // 遍历原始vector
                rem = (i + tmp * From) % To;
                i = (i + tmp * From) / To;
                tmp = rem;
            }
            ToV.push_back(rem);
        }
        r = "";
        for (int i : ToV) r = COfVal(i) + r;
    }
    if (r == "") r = "0";
    return r;
}

int main() {
    int n;
    cin >> n;
    for (size_t i = 0; i < n; i++) {
        string t; // 临时变量用于接收一行
        stringstream ss; //
        cin >> t;
        for (char c : t) {
            if (c != ',') ss << c;
            else ss << endl;
        }
        int From, To;
        string s;
        ss >> From >> s >> To;
        cout << Trans(From, s, To) << endl;
    }
}

核心部分代码

while (!ISZero(v)) {
    int rem = 0; // 余数
    int tmp = 0; // 临时变量
    for(int & i: v) { // 遍历原始vector
        rem = (i + tmp * From) % To;
        i = (i + tmp * From) / To;
        tmp = rem;
    }
    ToV.push_back(rem);
}

递归二进制最大公约数算法

递归终止条件

gcd(m, m) = m

递归关系

  • m < n 时:gcd(m, n) = gcd(n, m)
  • m为偶数,n为偶数:gcd(m, n) = 2 * gcd(m/2, n/2)
  • m为偶数,n为奇数时:gcd(m, n) = gcd(m/2, n)
  • m为奇数,n为偶数时:gcd(m, n) = gcd(m, n/2)
  • m和n都为奇数时:gcd(m, n) = gcd(n, m - n)
int gcd(int m, int n) {
    if (m == n) return m;
    else if (n > m) return gcd(n, m);
    if (m & 1) return (n & 1) ? gcd(m - n, n): gcd(m, n / 2);
    else return (n & 1) ? gcd(m / 2, n) : 2 * gcd(m / 2, n / 2);
}
posted @ 2024-04-25 22:19  iamy  阅读(34)  评论(0)    收藏  举报