当前位置: 首页 > article >正文

算法实战练习

持续更新~~

目录

前言

1. 奇1偶0

2. 最大消费额

3. 目录树生成

4. 75. 颜色分类

5. 合并两个有序数组

6. 206. 反转链表


前言

算法的学习离不开实战。经过一段时间的练习,我深刻体会到,只有通过解决实际问题,才能真正掌握算法的精髓,并将其灵活运用。这个博客系列记录了我通过实战练习后的心得和总结,涵盖了我从基础到进阶的解题思路、优化技巧以及代码实现。

在实战中,我逐渐意识到,算法不仅仅是“写对代码”,更是理解问题本质、设计高效解决方案的过程。每一道题目都让我对算法有了更深的理解,也让我更加注重细节和逻辑的严谨性。

这个系列将分享我在实战中遇到的典型问题,以及我的解题过程和优化思路。希望这些内容能为你的算法学习提供一些启发和帮助,也欢迎一起交流,共同进步!

1. 奇1偶0

给出一个数,这个数的每位如果是奇数就变为1,如果是偶数就变为0.

输入:2222 输出:0

输入:123   输出:101

#include <iostream>
#include <cmath>
using namespace std;

int modify_number(int n) {
    int result = 0;
    int place = 1;  // 用于记录每一位的权重,类似于十进制的位数

    while (n > 0) {
        int digit = n % 10;  // 获取当前数字的最低位
        if (digit % 2 == 0) {
            result += 0 * place;  // 偶数变为 0
        } else {
            result += 1 * place;  // 奇数变为 1
        }

        n /= 10;  // 去掉最低位
        place *= 10;  // 更新权重
    }
    
    return result;
}

int main() {
    // 测试
    cout << modify_number(222) << endl;  // 输出 0
    cout << modify_number(123) << endl;  // 输出 101
    
    return 0;
}

2. 最大消费额

第一行给出 m 张桌子和 n 批客人。
第二行给出 m 个数字,代表每张桌子能容纳的人数
接下来的 n 行给出每批客人的人数消费金额
你需要计算在不拼桌的情况下,求出最大消费金额

输入:

3 5
2 4 2
3 100
2 50
1 30
4 80
2 40
输出:

230

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

int main() {
    int m, n;
    cin >> m >> n; // m 是桌子数量,n 是客人批次数量

    vector<int> table_capacity(m); // 桌子的容量
    for (int i = 0; i < m; ++i) {
        cin >> table_capacity[i];
    }

    vector<pair<int, int>> customers(n); // 每批客人的人数和消费金额
    for (int i = 0; i < n; ++i) {
        cin >> customers[i].first >> customers[i].second;
    }

    // 排序:桌子容量从小到大,客人按消费金额从大到小排序
    sort(table_capacity.begin(), table_capacity.end());
    sort(customers.begin(), customers.end(), [](pair<int, int> a, pair<int, int> b) {
        return a.second > b.second; // 按消费金额降序排序
    });

    int total_consumption = 0;
    vector<bool> used_table(m, false); // 标记桌子是否已经被用过

    // 遍历每批客人
    for (auto& customer : customers) {
        int customer_people = customer.first;
        int customer_money = customer.second;

        // 尝试找到合适的桌子
        for (int i = 0; i < m; ++i) {
            if (!used_table[i] && table_capacity[i] >= customer_people) {
                // 找到一个未使用且足够容纳客人的桌子
                total_consumption += customer_money;
                used_table[i] = true; // 标记这个桌子已使用
                break; // 一旦找到合适的桌子就跳出循环
            }
        }
    }

    cout << total_consumption << endl; // 输出最大消费金额
    return 0;
}

3. 目录树生成

编写一个程序,解析给定的多个文件路径,并以树形结构打印出各个目录的层级关系。路径格式为Unix风格的路径,即各个目录由 / 分隔。根目录的子目录不需要前缀 -,其他目录按层级递归打印,子目录前面需要加上 - 并根据层级缩进。

示例输入:

5
/a/b/c
/a/b/d
/a/e
/f/g
/a/b/c/d/e

示例输出:

 a
  - b
    - c
      - d
        - e
    - d
  - e
f
  - g
#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

class Node {
public:
    string name;
    map<string, Node*> children;

    Node(const string& name) : name(name) {}
};

vector<string> split(const string& path) {
    vector<string> parts;
    string part;
    for (char c : path) {
        if (c == '/') {
            if (!part.empty()) {
                parts.push_back(part);
                part.clear();
            }
        } else {
            part += c;
        }
    }
    if (!part.empty()) {
        parts.push_back(part);
    }
    return parts;
}

Node* buildTree(const vector<vector<string>>& allParts) {
    Node* root = new Node("");
    for (const auto& parts : allParts) {
        Node* parent = root;
        for (const string& part : parts) {
            if (parent->children.find(part) == parent->children.end()) {
                parent->children[part] = new Node(part);
            }
            parent = parent->children[part];
        }
    }
    return root;
}

void printTree(Node* node, int indent) {
    string spaces(indent, ' ');
    cout << spaces << "- " << node->name << endl;
    for (const auto& childEntry : node->children) {
        printTree(childEntry.second, indent + 2);
    }
}

void deleteTree(Node* root) {
    if (!root) return;
    for (auto& entry : root->children) {
        deleteTree(entry.second);
    }
    delete root;
}

int main() {
    int n;
    cin >> n;
    cin.ignore();

    vector<vector<string>> allParts;
    for (int i = 0; i < n; ++i) {
        string path;
        getline(cin, path);
        allParts.push_back(split(path));
    }

    Node* root = buildTree(allParts);

    for (const auto& entry : root->children) {
        Node* node = entry.second;
        cout << node->name << endl;
        for (const auto& childEntry : node->children) {
            printTree(childEntry.second, 2);
        }
    }

    deleteTree(root);

    return 0;
}

4. 75. 颜色分类

中等

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]
提示:n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
 

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int p0{}, p1{}; // 初始化两个指针p0和p1,分别用于追踪0和1的边界

        int n = nums.size(); // 获取数组的长度

        // 遍历数组
        for(int i{}; i < n; ++i) {
            if(nums[i] == 1) {
                // 如果当前元素是1,将其与p1指针指向的元素交换,然后p1向前移动
                swap(nums[i], nums[p1++]);
            } else if(nums[i] == 0) {
                // 如果当前元素是0,将其与p0指针指向的元素交换
                swap(nums[i], nums[p0]);
                // 如果p0小于p1,说明p0指针刚交换过来的1需要与p1指针处的元素交换
                if(p0 < p1) {
                    swap(nums[i], nums[p1]);
                }
                // p0和p1都向前移动
                ++p0;
                ++p1;
            }
        }
    }
};

5. 合并两个有序数组

给出两个有序数组A和B,它们的元素个数分别是m和n,数组A的数组大小是m+n,将数组B合并到数组A中。

输入:[1,3,5] [2,4,6]

输出:[1,2,3,4,5,6]

#include <iostream>

void mergeSortedArraysInPlace(int a[], int m, int b[], int n) {
    int i = m - 1;  // 数组 a 的末尾
    int j = n - 1;  // 数组 b 的末尾
    int k = m + n - 1;  // 合并后的数组末尾

    // 从后向前合并
    while (i >= 0 && j >= 0) {
        if (a[i] > b[j]) {
            a[k--] = a[i--];
        } else {
            a[k--] = b[j--];
        }
    }

    // 如果数组 b 还有剩余元素
    while (j >= 0) {
        a[k--] = b[j--];
    }
}

int main() {
    // 定义数组 a,假设有足够的空间
    int a[6] = {2, 4, 6};  // 前 3 个元素有效,剩余空间足够
    int b[] = {1, 3, 5};

    // 数组长度
    int m = 3;  // 数组 a 的有效长度
    int n = sizeof(b) / sizeof(b[0]);  // 数组 b 的长度

    // 合并数组 b 到数组 a
    mergeSortedArraysInPlace(a, m, b, n);

    // 输出合并后的数组 a
    std::cout << "Merged Array: ";
    for (int i = 0; i < m + n; i++) {
        std::cout << a[i] << " ";  // 输出: 1 2 3 4 5 6
    }

    return 0;
}

6. 206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000
class Solution {
public:
    // 反转单链表的函数
    ListNode* reverseList(ListNode* head) {
        // 如果链表为空,直接返回空指针
        if(!head) return {};

        // 定义两个指针,pre用于指向反转后链表的前一个节点,cur用于遍历链表
        ListNode* pre = nullptr;  // 初始时pre指向空
        ListNode* cur = head;     // cur指向链表的头节点

        // 遍历链表
        while(cur) {
            // 保存当前节点的下一个节点
            ListNode* node = cur->next;  
            // 反转当前节点的指向
            cur->next = pre;  
            // 更新pre和cur指针
            pre = cur;        // pre指向当前节点
            cur = node;      // cur指向下一个节点
        }

        // 返回新的头节点,即反转后的链表头
        return pre;  
    }
};


http://www.kler.cn/a/567636.html

相关文章:

  • 小红书湖仓架构的跃迁之路
  • 在日常工作中,有一块新磁盘,如何扩容到vm中,具体命令是什么?
  • Python----数据分析(Numpy:安装,数组创建,切片和索引,数组的属性,数据类型,数组形状,数组的运算,基本函数)
  • Spring Framework测试工具MockMvc介绍
  • 使用UA-SPEECH和TORGO数据库验证自动构音障碍语音分类方法
  • Unity3D仿星露谷物语开发30之保存场景状态
  • Compose 手势处理,增进交互体验
  • Python实现GO鹅优化算法优化BP神经网络回归模型项目实战
  • 网络空间安全(6)web应用程序技术
  • 抽象工厂模式:思考与解读
  • java项目之基于ssm的学籍管理系统(源码+文档)
  • 实例分割 | yolov11训练自己的数据集
  • 【Java】Stream API
  • Ollama的底层实现原理分析
  • 【多模态大模型论文精读】MOSHI:双工实时语音对话大模型
  • 网络变压器的主要电性参数与测试方法(2)
  • 【Redis】Redis 入门
  • 《基于鸿蒙系统的类目标签AI功能开发实践》
  • 基于PLC的智能窗控制系统设计
  • java Bean映射转换库 ​MapStruct​