算法--数组--数论

本文介绍了数论中的素数概念及其筛选算法,包括Eratosthenes筛法。接着,详细讲解了多个简单的编程题目,如奇数长度子数组和、最短距离、拆炸弹等,提供了详细的解题思路和代码实现,涉及位运算、循环、条件判断等编程技巧。此外,还涵盖了数组加法和矩阵判断等复杂问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、知识点介绍(数论)

1. 素数

素数的概念:
        1. 素数又称质数,素数首先满足条件是要大于等于2,并且除了1和它本身外,不能被其他任何自然数整除;
         2. 1既不是素数也不是合数;
         3. 2是唯一的偶素数。

素数筛选法:

#define maxp 65536
#define ll long long

int primes[maxp];
bool notprime[maxp];                                                        // 1
 
void Eratosthenes() {
    notprime[1] = true;                                                    
    primes[0] = 0;
    for (int i = 2; i < maxp; i++) {
        if (!notprime[i]) {
            primes[++primes[0]] = i;                                       // 2 
            
            for (ll j = (ll)i*i; j < maxp; j += i) {
                notprime[j] = true;                                        // 3
            }
        }
    }
}

二、练习题目

        (1) 所有奇数长度子数组的和      简单
        (2) 到目标元素的最小距离     简单
        (3) 拆炸弹     简单
        (4) 能否连接形成数组     简单
        (5) 599. 两个列表的最小索引总和
        (6) 674. 最长连续递增序列
        (7) 989. 数组形式的整数加法
        (8) 2319. 判断矩阵是否是一个 X 矩阵

三、算法思路

1. 所有奇数长度子数组的和

        (1) 因为数据量特别小,所以暴力枚举就好了
        (2) 位运算判断一个数字是不是奇数:形如(num & 1)如果为真的话,num就是奇数。

class Solution {
public:
    int sumOddLengthSubarrays(vector<int>& arr) {
        int ans = 0;
        int i, j;
        int sum;
        for(i = 0; i < arr.size(); ++i) {
            sum = 0;
            for(j = i; j < arr.size(); ++j) {
                sum += arr[j];
                if((j-i+1) & 1) {
                    ans += sum;
                }
            }
        }
        return ans;

    }
};

2. 到目标元素的最小距离

        (1) 这道题按照题目要求一步步写下来就好了
        (2) 可以用 abs() 方法,用来取绝对值的; 我一开始对这个方法不熟,具体用法就是:abs(需要取绝对值的式子或者变量)。

class Solution {
public:
    int getMinDistance(vector<int>& nums, int target, int start) {
        int min = 10020;
        int i;
        int temp = 0;
        for(i = 0; i < nums.size(); ++i) {
            if(nums[i] == target) {
                temp = i -start;
                if(temp< 0) temp = -temp;
                if(temp < min) min = temp;
            }
        }
        return min;


    }
};

3. 拆炸弹

        (1) 数据量比较小,所以按照题目要求一步步写下来就好了。
        (2) 唯一需要纠结一下的地方就是,当k>0和k<0时,对数组的取值,为了防止越界,要对数组大小就是code.size()取模运算。

class Solution {
public:
    vector<int> decrypt(vector<int>& code, int k) {
        vector<int> ans;
        int i, j;
        int n = code.size();
        for(i = 0; i < n; ++i) {
            int cnt = 0;
            int temp = 0;
            if(k > 0) {
                for(j = (i+1)%n; cnt < k; cnt++) {
                    temp += code[j];
                    j = (j+1) %n;
                }
                ans.push_back(temp);
            }
            if(k == 0) ans.push_back(0);
            if(k < 0) {
                for(j = (n+i-1) % n ; cnt > k; cnt--) {
                    temp += code[j];
                    j = (j-1+n) % n;
                }
                ans.push_back(temp);
            }
        }
        return ans;

    }
};

4. 能否连接形成数组

        (1) 这道题也是暴力循环。要注意的是:要以arr数组为主,和pieces二维数组进行比较,没找到对应的值应该要continue继续找下去,不应该break。
        (2) 需要设置一个flag,当数组中元素不对应的时候,flag设为false;对应的时候设为true
        (3) 当arr遍历到了最后一个元素,且最后一个元素符合要求返回true
        (4) 这道题建议多做几遍,我感觉自己不熟,有一些细节会出错,而且应该有更好的解决方法。

class Solution {
public:
    bool canFormArray(vector<int>& arr, vector<vector<int>>& pieces) {
        bool ans = false;
        int i = 0, j, k;
        while(i < arr.size()) {
            ans = false;
            for(j = 0; j < pieces.size(); ++j) {
                if(arr[i] != pieces[j][0]) {
                    continue;
                }
                for(k = 0; k < pieces[j].size(); ++k) {
                    if(pieces[j][k] == arr[i]) {
                        i++;
                        ans = true;
                    } else {
                        return false;
                    }
                }
                if(i == arr.size()) {
                    return true;
                }
            }
            if(!ans) return false;
        }
        return true;
    }
};

5. 两个列表的最小索引总和

        (1) 先遍历找出两个数组当中元素相同的索引值,再把最小的索引数的和存下来
        (2) 再重新遍历一遍,当元素相同,并且索引值也等于最小索引数的值存入答案数组中

class Solution {
public:
    vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) {
        int i, j;
        int ans = 3000;
        vector<string> res;
        for(i = 0; i < list1.size(); ++i) {
            for(j = 0; j < list2.size(); ++j) {
                if(list1[i] == list2[j]) {
                    ans = min(ans, i+j);
                }
            }
        }
        for(i = 0; i < list1.size(); ++i) {
            for(j = 0; j < list2.size(); ++j) {
                if(list1[i] == list2[j] && (i+j) == ans) {
                    res.push_back(list1[i]);
                }
            }
        }
        return res;
    }
};

6. 最长连续递增序列

        (1) 遍历整个数组,如果递增就cnt++;再更新ans的值,ans保存cnt的最大值;
        (2) 如果不递增,cnt初始化为1;

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int cnt = 1;
        int ans = 1;
        int i;
        for(i = 1; i < nums.size(); ++i) {
            if(nums[i] > nums[i-1]) {
                cnt++;
                ans = max(ans, cnt);
            } else {
                cnt = 1;
            }
        }
        return ans;
    }
};

7. 数组形式的整数加法

        (1) 这道题略复杂,用java写会更简单;
        (2) C++的话,要先把num数组翻转
        (3) 把k的值,按位存入一个新的数组中
        (4) 然后在按位做加法,用一个pre存放进位的数,因为数组中最大的数为9,所以最多进1位;
        (5) 加完之后要判断一个特殊情况,就是最后一位要进位的话,要再push_back一个1;
        (6) 最后不要忘记翻转回去。

class Solution {
    void address(vector<int>& num, int n, int pre) {
        if(n == num.size()-1) {
            if(pre > 0) {
                num.push_back(1);
                pre = 0;
            }
            if(num[n] > 9) {
                num.push_back(1);
                num[n] %= 10;
            }
        } else {
            if(pre > 0) {
                num[n+1]++;
                pre = 0;
                address(num, n+1, pre);
            }
            if(num[n] > 9) {
                num[n+1] += 1;
                pre = 0;
                num[n] %= 10;
                address(num, n+1, pre);
            }
        }
    }
public:
    vector<int> addToArrayForm(vector<int>& num, int k) {
        int i, j;
        int pre = 0;
        vector<int> saveK;
        vector<int> temp;
        while(k) {
            saveK.push_back(k%10);
            k /= 10;
        }
        reverse(num.begin(), num.end());
        if(saveK.size() > num.size()) {
            temp = saveK;
            saveK = num;
            num = temp;
        }
        for(i = 0; i < saveK.size(); ++i) {
            num[i] = num[i] + saveK[i] + pre;
            if(num[i] > 9) {
                pre = 1;
                num[i] %= 10;
            } else {
                pre = 0;
            }
            if(i == saveK.size()-1) {
                address(num, saveK.size()-1, pre);
            }
        }
        reverse(num.begin(), num.end());
        return num;

    }
};

8. 判断矩阵是否是一个 X 矩阵

        (1) 遍历整个矩阵,对角线的数字有一个特点就是i==j;
        (2) 副对角线有一个特点就是(i+j) == grid.size()-1;
        (3) 再按照题意写就好了。

class Solution {
public:
    bool checkXMatrix(vector<vector<int>>& grid) {
        int i, j;
        int n = grid.size();
        bool ans, flag = false;
        for(i = 0; i < n; ++i) {
            for(j = 0; j < n; ++j) {
                if(i == j && grid[i][j] != 0) {
                    ans = true;
                }else if((i+j) == n-1 && grid[i][j] != 0){
                    ans = true;
                }else if((i != j) && ((i+j)!=n-1) && grid[i][j] == 0) {
                    ans = true;
                }else {
                    ans = false;
                    flag = true;
                    break;
                }
            }
            if(flag) break;
        }
        return ans;
    }
};

四. 总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值