算法刷题分享

这篇具有很好参考价值的文章主要介绍了算法刷题分享。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

(一) 动态规划——背包专题

导语:动态规划是一种常用的算法思想,广泛应用于各类问题的求解中。而背包问题则是动态规划中最经典且常见的问题之一。背包问题涉及在给定容量的背包中选择物品以达到最优解的目标。本篇博客将专注于介绍和讨论与背包问题相关的动态规划算法。我们将探索不同类型的背包问题,并详细讲解其动态规划的解决思路。

题目:01背包问题 LeetCode 416
题目概述:01背包问题是最基础的背包问题之一。给定一组物品,每个物品有重量和价值,背包具有一定的容量,需要在不超过背包容量的前提下,选择物品使得总价值最大化。
题解:使用动态规划求解,定义dp[i][j]表示前i个物品放入容量为j的背包中所能达到的最大价值。状态转移方程如下:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]),其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值。

def knapsack01(weights, values, capacity):
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        for j in range(1, capacity + 1):
            if weights[i - 1] <= j:
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
            else:
                dp[i][j] = dp[i - 1][j]

    return dp[n][capacity]

题目:完全背包问题 - LeetCode 322
题解:完全背包问题是一个经典的背包问题。给定一组面额不同的硬币和一个总金额,求出能够凑成总金额所需的最少硬币数量。使用动态规划求解,定义dp[i]表示凑成金额i所需的最少硬币数量。状态转移方程如下:dp[i] = min(dp[i], dp[i-coin] + 1),其中coin表示硬币的面额。 核心代码段:

def coinChange(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0

    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)

    return dp[amount] if dp[amount] != float('inf') else -1

题目:多重背包问题 - LeetCode 879
题解:多重背包问题是背包问题的一种扩展形式,每个物品有数量限制。给定一组工作,每个工作有所需的成本和利润,求出在不超过预算的前提下,能够获得的最大利润。使用动态规划求解,定义dp[i][j]表示前i个工作在预算不超过j的情况下所能达到的最大利润。状态转移方程如下:dp[i][j] = dp[i-1][j] + dp[i-1][j-cost],其中cost表示第i个工作的成本。 核心代码段:

def profitableSchemes(n, minProfit, group, profit):
    m = len(group)
    MOD = 10 ** 9 + 7
    dp = [[0] * (minProfit + 1) for _ in range(n + 1)]
    dp[0][0] = 1

    for i in range(1, m + 1):
        currGroup, currProfit = group[i - 1], profit[i - 1]
        for j in range(n, currGroup - 1, -1):
            for k in range(minProfit, -1, -1):
                dp[j][k] += dp[j - currGroup][max(0, k - currProfit)]
                dp[j][k] %= MOD

    return sum(dp[n]) % MOD

(二)贪心——区间专题

导语:在算法问题中,区间类问题是一类常见而重要的问题。贪心算法是一种常用的解决区间类问题的方法,它通过每一步选择当前最优解来达到整体最优解的目标。本篇博客将介绍贪心算法在解决区间类问题中的应用,并提供几个示例来说明其实现过程。通过阅读本文,你将了解贪心算法的基本思想以及如何运用它解决不同类型的区间类问题。

题目:区间选点 
题目描述:给定一组区间,从中选取尽可能多的互不相交的区间,使得选取的区间数最大。
题解:贪心法是解决区间选点问题的常用方法。首先,将所有区间按照右端点从小到大进行排序。然后,从第一个区间开始,选择右端点最小的区间,并排除与该区间相交的其他区间。重复该过程,直到遍历完所有区间。选取的区间即为答案。 核心代码段:

void intervalSelection(vector<pair<int, int>>& intervals) {
    sort(intervals.begin(), intervals.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
        return a.second < b.second;
    });

    vector<pair<int, int>> selectedIntervals;
    int lastEnd = INT_MIN;

    for (const auto& interval : intervals) {
        if (interval.first > lastEnd) {
            selectedIntervals.push_back(interval);
            lastEnd = interval.second;
        }
    }

    // 输出选取的区间
    for (const auto& interval : selectedIntervals) {
        cout << "[" << interval.first << ", " << interval.second << "]" << endl;
    }
}

题目:区间覆盖
题目描述:给定一组区间,找到最少的区间,使得它们的并集覆盖整个区间范围。
题解:贪心法是解决区间覆盖问题的常用方法。首先,将所有区间按照右端点从小到大进行排序。然后,从第一个区间开始,选择最小的右端点,并排除与该右端点相交的其他区间。重复该过程,直到覆盖整个区间范围。 核心代码段:

void intervalCover(vector<pair<int, int>>& intervals) {
    sort(intervals.begin(), intervals.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
        return a.second < b.second;
    });

    vector<pair<int, int>> selectedIntervals;
    int lastEnd = INT_MIN;
    int minRight = INT_MAX;

    for (const auto& interval : intervals) {
        if (interval.first > lastEnd) {
            selectedIntervals.push_back(interval);
            lastEnd = interval.second;
            minRight = min(minRight, interval.second);
        }
    }

    // 输出选取的区间
    for (const auto& interval : selectedIntervals) {
        cout << "[" << interval.first << ", " << interval.second << "]" << endl;
    }
}

题目:区间分组
题目描述:给定一组区间,将它们划分为尽可能少的组,使得每组中的区间互不相交。
题解:贪心法是解决区间分组问题的常用方法。首先,将所有区间按照右端点从小到大进行排序。然后,遍历每个区间,将它与当前组中的最后一个区间进行比较。如果不相交,则将该区间加入当前组;如果相交,则新建一组,并将该区间加入新的组。重复该过程,直到遍历完所有区间。 核心代码段:

void intervalGroup(vector<pair<int, int>>& intervals) {
    sort(intervals.begin(), intervals.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
        return a.second < b.second;
    });

    vector<vector<pair<int, int>>> groups;
    groups.push_back({intervals[0]});

    for (int i = 1; i < intervals.size(); ++i) {
        bool placed = false;
        for (auto& group : groups) {
            if (group.back().second < intervals[i].first) {
                group.push_back(intervals[i]);
                placed = true;
                break;
            }
        }
        if (!placed) {
            groups.push_back({intervals[i]});
        }
    }

    // 输出分组结果
    for (int i = 0; i < groups.size(); ++i) {
        cout << "Group " << i + 1 << ":" << endl;
        for (const auto& interval : groups[i]) {
            cout << "[" << interval.first << ", " << interval.second << "]" << endl;
        }
        cout << endl;
    }
}

(三)回溯

导语: 回溯法是一种经典的算法思想,通常用于解决组合、排列、子集等问题。它通过尝试所有可能的选择来递归地搜索解空间,直到找到满足条件的解或遍历完所有可能的选择。本部分博客将汇总一些常见的使用回溯法解决的算法题,并给出它们的简要题解和核心代码段。

  1. 题目:全排列 - LeetCode 46
    题目描述:给定一个没有重复数字的序列,返回其所有可能的全排列。
    题解:使用回溯法递归地生成所有可能的排列。从第一个位置开始,依次将每个数字放置在当前位置,然后继续递归处理下一个位置,直到所有位置都被填满。需要注意的是,在回溯之前要将当前位置的数字恢复原样。 核心代码段:
    void backtrack(vector<int>& nums, vector<vector<int>>& res, int start) {
        if (start == nums.size()) {
            res.push_back(nums);
            return;
        }
        for (int i = start; i < nums.size(); i++) {
            swap(nums[start], nums[i]);
            backtrack(nums, res, start + 1);
            swap(nums[start], nums[i]);
        }
    }
    
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        backtrack(nums, res, 0);
        return res;
    }
    
  2. 题目:组合总和 - LeetCode 39
    题目描述:给定一个无重复元素的正整数数组 candidates 和一个目标整数 target,找出 candidates 中所有可以使数字和为 target 的组合。
    题解:使用回溯法递归地生成所有可能的组合,并根据条件进行剪枝。从第一个位置开始,依次将每个数字放置在当前位置,然后继续递归处理下一个位置,直到满足条件或超出目标值。需要注意的是,在回溯之前要将当前位置的数字恢复原样。 核心代码段:
    void backtrack(vector<int>& candidates, vector<vector<int>>& res, vector<int>& combination, int target, int start) {
        if (target == 0) {
            res.push_back(combination);
            return;
        }
        for (int i = start; i < candidates.size(); i++) {
            if (candidates[i] > target) {
                break;
            }
            combination.push_back(candidates[i]);
            backtrack(candidates, res, combination, target - candidates[i], i);
            combination.pop_back();
        }
    }
    
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> combination;
        backtrack(candidates, res, combination, target, 0);
        return res;
    }
    
  3. 题目:子集 - LeetCode 78
    题目描述:给定一个不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
    题解:使用回溯法递归地生成所有可能的子集。从第一个位置开始,依次选择包含当前元素和不包含当前元素两种情况,然后继续递归处理下一个位置。需要注意的是,在回溯之前要将当前元素加入或移除子集中。 核心代码段:
    void backtrack(vector<int>& nums, vector<vector<int>>& res, vector<int>& subset, int start) {
        res.push_back(subset);
        for (int i = start; i < nums.size(); i++) {
            subset.push_back(nums[i]);
            backtrack(nums, res, subset, i + 1);
            subset.pop_back();
        }
    }
    
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> subset;
        backtrack(nums, res, subset, 0);
        return res;
    }
    

(四) 双指针问题

导语: 双指针法是一种常用的算法思想,通过两个指针在数组或链表等数据结构上的移动,来解决一些特定的问题。本部分博客将汇总一些常见的算法题,并给出它们的双指针法解决方案。希望这些例题能够帮助你理解和掌握双指针法的应用。文章来源地址https://www.toymoban.com/news/detail-820138.html

  1. 题目:两数之和 - LeetCode 1
    题目描述:给定一个升序排列的整数数组和一个目标值,找出数组中和为目标值的两个数的下标。
    题解:使用两个指针分别指向数组的首尾元素,根据两个指针指向的元素之和与目标值的关系,逐步调整指针的位置,直到找到目标值或遍历完整个数组。 核心代码段:
    // 两数之和问题
    vector<int> twoSum(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                // 找到目标值,返回结果
                return {left, right};
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        // 没有找到目标值,返回默认结果
        return {-1, -1};
    }
  2. 题目:反转字符串- LeetCode 344
    题目描述:给定一个字符串,将其原地反转。
    题解:使用两个指针分别指向字符串的首尾字符,交换两个指针所指向的字符,并逐步向中间移动指针,直到两个指针相遇。 核心代码段:
    // 反转字符串问题
    void reverseString(string& s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            swap(s[left], s[right]);
            left++;
            right--;
        }
    }
  3. 题目:删除排序数组中的重复项- LeetCode 26
    题目描述:给定一个有序数组,删除数组中重复的元素,使每个元素只出现一次,并返回新数组的长度。
    题解:使用两个指针,一个指针用于遍历数组,另一个指针用于记录非重复元素的位置,当遍历到不重复的元素时,将其复制到非重复元素的位置,并更新非重复元素指针的位置。 核心代码段:
    // 删除排序数组中的重复项问题
    int removeDuplicates(vector<int>& nums) {
        int i = 0, j = 1;
        while (j < nums.size()) {
            if (nums[j] != nums[i]) {
                i++;
                nums[i] = nums[j];
            }
            j++;
        }
        return i + 1;
    }
  4. 题目:盛最多水的容器- LeetCode 11 
    题目描述:给定一个非负整数数组,每个元素表示一个竖直线的高度,找出两条线与 x 轴共同构成的容器可以容纳的最大水量。
    题解:使用两个指针分别指向数组的首尾元素,计算当前指针构成的容器的面积,并记录最大面积,然后根据两个指针所指向的元素的高度来决定移动哪个指针。 核心代码段:
    // 盛最多水的容器问题
    int maxArea(vector<int>& height) {
        int left = 0, right = height.size() - 1;
        int maxArea = 0;
        while (left < right) {
            int area = min(height[left], height[right]) * (right - left);
            maxArea = max(maxArea, area);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return maxArea;
    }

到了这里,关于算法刷题分享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分享一个免梯子的GPT,刷题和学习的好帮手

    使用了这个问答工具后,感觉前后端都要被替代了,太强了。 由于本人之前很想体验,但是一直难搞,最近发现了一个免梯子的,重要事情说一遍,免梯子!是我最近发现的最好用,最快的,且不要梯子的,用起来爽,界面也挺好看的,大家快玩儿玩儿。 试了一下写代码,

    2023年04月09日
    浏览(38)
  • 【贪心算法】leetcode刷题

    贪心算法无固定套路。 核心思想:先找局部最优,再扩展到全局最优。 两种思路: 1、从大到小。局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。 先遍历的胃口,在遍历的饼干 2、从小到大。 小饼干先喂饱小胃口 。两个

    2024年02月14日
    浏览(34)
  • 【刷题篇】贪心算法(一)

    假设1元、2元、5元、10元、20元、50元、100元的纸币分别由c0,c1,c2,c3,c4,c5,c6张。现在要用这些钱来支付K元,至少要用多少张纸币?

    2024年02月09日
    浏览(30)
  • 贪心算法OJ刷题(1)

    指所求问题的整体最优解可以通过一系列局部最优的选择来到达。是不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。 贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考

    2023年04月25日
    浏览(26)
  • 算法刷题记录

    cf题目记录 773 A题 给定三个坐标 就是让你求两个y相同时x的差,这个y要是最大的y 收获:ans我本来定义的是double,因为我看题目输出是浮点数,然后wa了,发现大数相减时,会把后面的省略了,比如 然后把double改为int就行了,因为减数和被减数都是int型的 772 B A题 题意 如果两

    2024年02月15日
    浏览(25)
  • 【算法】刷题路线(系统+全面)

    本系列基于当前各大公司对大公司的考察情况,给大家规划一条可行的算法刷题路线,大概会规划 200 道自认为有用的题,并且争取让初学者,能够刷起来更加丝滑,而且每个阶段都会进行相对应的说明。 当然,无论是面试还是笔试,你也完全可以按照这个路线来,应付大公

    2024年02月16日
    浏览(25)
  • 算法图类型刷题

    使用的基础数据结构和方法 第一题: 广度优先算法: 深度优先: 第二题 第三题 广度优先: 深度优先: 第四题: 第五题: 第六题: 第七题: 深度优先: 广度优先:

    2024年02月16日
    浏览(21)
  • 【算法刷题】Day28

    原题链接 第 i 个元素是一支给定的股票在第 i 天的价格 最多可以完成 两笔 交易 注意:你不能同时参与多笔交易 1. 状态表示: dp[i] 表示:第 i 天结束之后,所能获得的最大利润 f[i][j] 表示:第 i 天结束之后,完成了 j 次交易,此时处于“买入”状态下的,最大利润 g[i][j]

    2024年02月02日
    浏览(29)
  • 【刷题篇】贪心算法

    假设1元、2元、5元、10元、20元、50元、100元的纸币分别由c0,c1,c2,c3,c4,c5,c6张。现在要用这些钱来支付K元,至少要用多少张纸币?

    2024年02月09日
    浏览(26)
  • 每日刷题|贪心算法初识

                                            食用指南:本文为作者刷题中认为有必要记录的题目                                         推荐专栏 : 每日刷题                                        ♈️ 今日夜电波 : 悬溺—葛东琪                        

    2024年02月08日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包