算法练习1

复数乘法

问题描述:

输入两个表示复数的字符串,输出它们相乘的结果的字符串
复数字符串用a+bi表示(a, b 为整数, i为虚数单位,i2=1)
输入例子:
1±2i
3+4i
输出例子:
11±2i
例子说明:
(1±2i)(3+4i) = (3 + 4i - 6i - 8i * i) = 11±2i

#include<iostream>
#include<string>
#include<vector>
using namespace std;

int strToInt(string& str)
{
    int ret = 0;
    int flag = 1;
    for(auto& ch : str)
    {
        if(ch == '-')
            flag = -1;
        if(ch >= '0' && ch <= '9')
            ret = ret * 10 + ch - '0';
    }
    
    return ret * flag;
}
int main()
{
    string str1, str2;
    cin >> str1 >> str2;
    
    string sr1 = str1.substr(0, str1.find('+'));
    string sf1 = str1.substr(str1.find('+')+1);
    string sr2 = str2.substr(0, str2.find('+'));
    string sf2 = str2.substr(str2.find('+')+1);
    
    int srInt1 = strToInt(sr1);
    int sfInt1 = strToInt(sf1);
    int srInt2 = strToInt(sr2);
    int sfInt2 = strToInt(sf2);
    
    int sr = srInt1 * srInt2 - sfInt1 * sfInt2;
    int sf = srInt1 * sfInt2 + sfInt1 * srInt2;
    
    cout << sr << "+" << sf << "i" << endl;
    
    return 0;
}

一年中的第几天

问题描述:

输入一个"YYYY-MM-dd"格式的日期字符串,输出该天是当年的第几天(1 月 1 日是每年的第 1 天)
输入描述:
一个"YYYY-MM-dd"格式的表示日期的字符串
输出描述:
该天是当年的第几天

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int strToInt(string s)
{
    int ret = 0;
    for(auto ch : s)
    {
        ret = ret * 10 + ch - '0';
    }
    
    return ret;
}

int main()
{
    string s;
    cin >> s;
    vector<string> strV;
    
    string tmp = "";
    for(int i = 0; i < s.size(); i++)
    {
        if(s[i] == '-')
        {
            strV.push_back(tmp);
            tmp = "";
        }
        else
        {
            tmp += s[i];
        }
    }
    strV.push_back(tmp);
    
    int year = strToInt(strV[0]);
    int month = strToInt(strV[1]);
    int day = strToInt(strV[2]);
    
    int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int ret = 0;
    for(int i = 1; i < month; i++)
    {
        ret += days[i];
    }
    ret += day;
    if(month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0))
        ++ret;
    
    cout << ret << endl;
    
    return 0;
}

k个一组翻转链表

问题描述:

给你一个链表,每 k 个节点一组进行翻转,请返回翻转后的链表。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
输入描述:
第一行:依次输入链表中的各个元素,以"#“结束
第二行:每组数量k
输出描述:
处理后的链表中的各个元素,以”->"连接

#include <iostream>
#include <string>
#include <vector>
using namespace std;

void reverseStr(vector<string>& sv, int start, int end)
{
    while(start < end)
        sv[start++].swap(sv[end--]);
}
int main()
{
    string s;
    vector<string> sv;
    while(cin >> s)
    {
        if(s != "#")
            sv.push_back(s);
        else
            break;
    }
    int k;
    int start = 0;
    cin >> k;
    while(sv.size() - start >= k)
    {
        reverseStr(sv, start, start + k - 1);
        start += k;
    }
    for(int i = 0; i < sv.size() - 1; i++)
    {
        cout << sv[i] << "->";
    }
    cout << sv[sv.size()-1] << endl;
    
    return 0;
}

递增子序列

问题描述:

判断一个无序数组中是否存在长度为3的递增子序列。(不要求连续)(满足O(n)的时间复杂度和O(1)的空间复杂度。)
输入描述:
第一行一个正整数 1 <= n <= 100000
第二行n个整数a1,a2,…,an,(1<=ai<=1e9)
输出描述:
如果存在,输出"true",否则输出"false"。(不含引号)。

记录遍历当前位置时的最小的两个值,如果当前位置的值大于第二小的值,则可以说明存在这样的递增序列。第二小的值必须在第一小的值之后,所以如果更新了第一小的值,则也需要更新第二小的值。

#include <iostream>
#include <vector>
#include <limits.h>
using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i++)
        cin >> a[i];
    int first = a[0];
    int second = INT_MAX;
    for(int i = 0; i < n; i++)
    {
        if(a[i] < first)
        {
            first = a[i];
            //如果更新了最小值,则second需要在后面找,所以先给一个最大值
            second = INT_MAX;
        }
        else if(a[i] > first && a[i] < second)
        {
            second = a[i];
        }
        else if(a[i] > second )
        {
            cout << "true" << endl;
            return 0;
        }
    }
    cout << "false" <<endl;
    return 0;
}

求表达式f(n)结果末尾0的个数

问题描述:

输入一个自然数n,求表达式 f(n) = 1! * 2! * 3!..n! 的结果末尾有几个连续的0?

如果要产生0,则需产生因子10,相当于需要计算出10的数量。但10不是最小的因子,他还可以分解成2*5,所以需要找出2和5的个数并取两者中最小的,即为0的个数。(也可以直接取5的个数)
例如:1053000000 = 1053 *10 *10 *10 *10 *10 *10
而f(10): 1!2!3!..10! : 2!3!… 10!都包含2
5!6!… 10!都包含5
所以 i:i中2或5的个数 * 表达式中i的个数
例如 i = 4:2的个数是2,5的个数是0;4!5!6!… 10!都包含4
即 f(i)中2的个数:2 *(10 - 4 +1) 5的个数:0

#include <iostream>
using namespace std;
int num(int i, int n)
{
    int cnt = 0;
    while(i >= n && i%n == 0)
    {
        cnt++;
        i /= n;
    }
    return cnt;
}
int main()
{
    int n;
    cin >> n;
    int num2 = 0, num5 = 0;
    for(int i = 1; i <= n; i++)
    {
        num2 += num(i, 2) * (n - i + 1);
        num5 += num(i, 5) * (n - i + 1);
    }
    cout << min(num2, num5);
    return 0;
}

字符串压缩算法

输入一串字符,请编写一个字符串压缩程序,将字符串中连续出现的重复字母进行压缩,并输出压缩后的字符串。
例如:
aac 压缩为 1ac
xxxxyyyyyyzbbb 压缩为 3x5yz2b

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    getline(cin, s);
    for(int i = 0; i < s.size(); i++)
    {
        int cnt = 0;
        while(s[i] == s[i+1])
        {
            ++cnt;
            ++i;
        }
        if(cnt != 0)
            cout << cnt << s[i];
        else
            cout << s[i];
    }
    cout << endl;
    return 0;
}

硬币划分

问题描述:

有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱(n <= 100000),有多少中组合可以组成n分钱?
输入描述:输入整数n.(1<=n<=100000)
输出描述:输出组合数,答案对1e9+7取模。

本题可以使用DFS,但如果n过大可能会超出时间限制

动态规划:
1.状态F(i, j): 用i种硬币组成j分钱的方法总数
2.转移方程:
如果第j个硬币面值大于j,则只能用i-1种硬币进行组合。F(i, j) = F(i-1, j);
如果第j个硬币面值小于j,则可以用,也可以不用,两种选择互斥F(i, j) = F(i - 1, j) + F(i, j - coins[i - 1])
此处F(i, j - coins[i - 1])表示:第i个硬币可能使用了多次
3.初始化:
F(0, j) = 0
F(i, 0) = 1 //辅助值
4.返回值
F(coins.size(), n)
在这里插入图片描述

#include <iostream>
#include <vector>
using namespace std;
//f(i, j) 前i种硬币 j分钱 = f(i-1, j) + f(i, j-a[i])
int main()
{
    int n;
    cin >> n;
    vector<int> coins = {1, 2, 5, 10};
    vector<vector<int>> result(5, vector<int>(n+1, 0));
    //初始化 f(i, 0)=1;//辅助值
    for(int i = 0; i < 5; i++)
        result[i][0] = 1;
    
    for(int i = 1; i < 5; i++){
        for(int j = 0; j <= n; j++){
            if(j >= coins[i-1]){
                result[i][j] = (result[i-1][j] + result[i][j-coins[i-1]]) % 1000000007;
            }
            else{
                result[i][j] = result[i-1][j];
            }
        }
    }
    
    cout << result[4][n];
    
    return 0;
}

合并二叉树

问题描述:

已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替。
1.输入描述:第一行输入整数n,m。(分别表示树1和树2的节点数,1<=n,m<=100)接下来n行,第i行三个整数l,r,v, (0<=l,r<=n , 0<=v<=100) ,表示第一棵树的第i个结点的左儿子编号,右儿子编号和权值。接下来m行,第i行三个整数l,r,v, (0<=l,r<=n , 0<=v<=100) ,表示第二棵树的第i个结点的左儿子编号,右儿子编号和权值。(对于左右儿子,如果编号为0表示空。保证如果儿子不为空,则儿子编号大于父亲编号。)
2.输出描述: 输出合并后树按层遍历的各结点权值,相邻权值之间以一个空格相间。
输入例子1:
4 5
2 3 1
4 0 3
0 0 2
0 0 5
2 3 2
0 4 1
0 5 3
0 0 4
0 0 7
输出例子1: 3 4 5 5 4 7

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Node{
    int val;
    Node* left;
    Node* right;
    Node(int x = 0)
        :val(x)
        ,left(nullptr)
        ,right(nullptr)
    {}
};
Node* creatTree(vector<Node>& tree, int num)
{
    //从第一个孩子结点开始
    for(int i = 1; i <= num; i++){
        int l, r, v;
        cin >> l >> r >> v;
        if(l != 0){
            tree[i].left = &tree[l];
        }
        if(r != 0){
            tree[i].right = &tree[r];
        }
        tree[i].val = v;
    }
    return &tree[1];
}
Node* mergeTree(Node* root1, Node* root2)
{
    if(root1 != nullptr && root2 != nullptr)
    {
   
        root1->left = mergeTree(root1->left, root2->left);
        root1->right = mergeTree(root1->right, root2->right);
        root1->val += root2->val;
    }
    return root1 != nullptr ? root1 : root2;
}
void DFS(Node* root)
{
    queue<Node*> q;
    q.push(root);
    while(!q.empty()){
        Node* curNode = q.front();
        q.pop();
        if(curNode->left)
            q.push(curNode->left);
        if(curNode->right)
            q.push(curNode->right);
        cout << curNode->val << " ";
    }
}
int main()
{
    int n, m;
    cin >> n >> m;
    vector<Node> tree1(n+1);
    vector<Node> tree2(m+1);
    Node* root1 = creatTree(tree1, n);
    Node* root2 = creatTree(tree2, m);
    Node* root = mergeTree(root1, root2);
    DFS(root);
    
    return 0;
}
  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值