算法笔记:乱七八糟的题目汇总

一、《剑指offer》面试题三中的题目二:不修改数组找出数组中重复的数字

在一个长度为n+1的数组 nums 里的所有数字都在0~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。
 例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或3。

作者介绍了二分法来解决这个问题,简单说,就是把1~n从中间的数字m分成两部分,如果前半部分包含的数字超过m,说明该区间内一定有至少一个重复数字,则继续对这个区间进行二分,后面的区间就不用管了。否则,后半部分区间一定包含重复数字,对它继续进行二分和判断。

如此循环,直到区间范围只能容忍一个数字,且该数字的出现次数大于1,则它一定是重复的。

代码如下。

class Solution {
public:
    int findRepeatNumber(vector& nums) {
        int length = nums.size();
        int begin = 0;
        int end = length - 1;
        while (end >= begin) {
            int middle = ((begin + end) >> 2) + begin;
            int count = countnum(nums, begin, middle, length); //搜索在这个范围内的数的个数
            if (begin == middle) { //如果区间最小的元素与区间最大的元素相等,说明该区间只有一个元素
                if (count > 1) //如果这个元素出现次数超过1次说明它重复
                    return begin;
                else //否则,说明这个数组中没有重复元素
                    return -1;
            }

            if (count > middle - begin + 1) //如果这个区间的数出现的次数超过了区间范围,说明区间里肯定有某个数重复
                end = middle;
            else //否则,要遍历紧邻着该区间之后的下一个区间,区间元素的范围是相同的
                begin = middle + 1;           
        }
        return -1;
    }
    int countnum(vector nums, int begin, int end, int length) {
        int count = 0;
        for(auto a : nums)
            if (a <= end && a >= begin)
                count++;
        return count;
    }
};

二、《剑指offer》面试题四:*二维数组中的查找

在一个n*m的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:有矩阵如下。
1 2 8  9
2 4 9  12
4 7 10 13
6 8 11 15
给定查找数字7,则返回true;给定查找数字5,则返回false。

从最右上角的数字开始。如果目标数字小于右上角的数字,则它肯定比这一列的所有数字都小,则不再考虑这一列,查找的范围排除掉这一列,否则不做改变。再查看第一行,如果第一行的倒数第二个数字(倒数第一个数字已经被排除掉了,不再考虑它)小于目标数字,说明这一行所有数字都比目标数字小,则不再考虑第一行,否则不做改变。

第二次循环也同理,让当前的范围内右上角的数字与目标数字比较,缩小行和列的范围。如此循环直到找到数组或找不到数字,返回true或false。

class Solution {
public:
    bool findNumberIn2DArray(vector>& matrix, int target) {
        int rows = matrix.size();   //数组的行数
        if (rows == 0)  //说明数组为空
            return false;

        int cols = matrix[0].size();    //数组的列数
        
        //从右上角开始遍历
        int col = cols - 1; 
        int row = 0;
        while (row < rows && col >= 0) {
            if (target == matrix[row][col])
                return true;
            else if (target < matrix[row][col])
                col--;
            else if (target > matrix[row][col])
                row++;
        }
        return false;
    }
};

三、《剑指offer》面试题63:股票的最大利润

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例1:
 输入: [7,1,5,3,6,4]
 输出: 5
 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例2:
 输入: [7,6,4,3,1]
 输出: 0
 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

面试的时候真的被问到这个题目了,特此记录一下。首先新建一个数组min。遍历给定的prices数组。

如果数组min为空或prices[i]小于数组的最后一个数字,就把prices[i]也加入到数组min中,因此就得到了一个递减的数列。否则,就说明prices[i]比当前的min最后的数字要大,用prices[i]减去数组min的最后一个数字,也就是在prices[i]之前出现过的最小的数字。

代码如下。

#include 

class Solution {
public:
    int maxProfit(vector& prices) {
        int min[prices.size()];
        int max_profit = 0;
        int j = 0;
        for (int i = 0; i < prices.size(); i++) {
            if (j == 0 || prices[i] < min[j - 1]) {
                min[j] = prices[i];
                j++;
            }
            else {
                int profit = prices[i] - min[j - 1];
                max_profit = max_profit > profit ? max_profit : profit;
            }
        }
        return max_profit;
    }
};

四、Leetcode332:零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
 输入:coins = [1, 2, 5], amount = 11
 输出:3
 解释:11 = 5 + 5 + 1
示例 2:
 输入:coins = [2], amount = 3
 输出:-1
示例 3:
 输入:coins = [1], amount = 0
 输出:0
示例 4:
 输入:coins = [1], amount = 1
 输出:1
示例 5:
 输入:coins = [1], amount = 2
 输出:2

经典的动态规划题目,关于动态规划的详细解读,可以参考这篇博客。假设输入coins是{1, 2, 5},金额amount是27。

用表示金额为时所需的最少金币数,则一定有如下的递推关系:。的初始值为,。

最后的代码如下。

#include 
#include 

class Solution {
public:
    int coinChange(vector& coins, int amount) {
        int f[amount + 1];
        f[0] = 0;
        for (int i = 1; i<= amount; i++)
            f[i] = INT_MAX;
        for (int i = 1; i <= amount; i++)
            for (int j = 0; j < coins.size(); j++)
                if (coins[j] <= i && f[i - coins[j]] != INT_MAX)
                    f[i] = min(f[i], f[i - coins[j]] + 1);
        if (f[amount] == INT_MAX)
            return -1;
        return f[amount];
    }
};

五、Leetcode62:不同路径问题

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?
示例 1:


 输入:m = 3, n = 7
 输出:28
示例 2:
 输入:m = 3, n = 2
 输出:3
解释:
 从左上角开始,总共有 3 条路径可以到达右下角。
 1. 向右 -> 向下 -> 向下
 2. 向下 -> 向下 -> 向右
 3. 向下 -> 向右 -> 向下
示例 3:
 输入:m = 7, n = 3
 输出:28
示例 4:
 输入:m = 3, n = 3
 输出:6
提示:
 1. 1 <= m, n <= 100
 2. 题目数据保证答案小于等于 2 * 109

动态规划问题。假设用表示在第i+1行j+1列处(因为是从0开始算的)的不同路径数。

因为走到某个格子要么是从上面的格子向下走到的,要么是从左面的格子向右走到的,所以递推关系式。

初始状态,。

代码如下。

class Solution {
public:
    int uniquePaths(int m, int n) {
        if (m == 1 || n == 1)
            return 1;

        int f[m][n];
        f[0][0] = 0;
        for (int i = 1; i < m; i++)
            f[i][0] = 1;
        for (int j = 1; j < n; j++)
            f[0][j] = 1;

        for (int i = 1; i < m; i++) 
            for (int j = 1; j < n; j++) 
                f[i][j] = f[i - 1][j] + f[i][j - 1];
        return f[m - 1][n - 1];
    }
};

六、Leetcode1:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
 输入:nums = [2,7,11,15], target = 9
 输出:[0,1]
 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
 输入:nums = [3,2,4], target = 6
 输出:[1,2]
示例 3:
 输入:nums = [3,3], target = 6
 输出:[0,1]

第一种方法是暴力求解,使用两层循环,将数组中的每两个数都加一遍直至找到结果。这种方法时间复杂度为O(n^2)。

第二种解法使用哈希表,哈希表中存放已经遍历过的所有元素的值和位置,以值为哈希表的key、位置为value。只需要遍历一次数组,每遍历到一个元素,先查看target减去该元素得到的值是否在哈希表里,如果在就取出它的value,返回结果。否则,就将当前元素加入哈希表中。这种方法以空间换时间,时间复杂度为O(n)。

具体代码如下。

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        vector result;
        map my_map;
        map::iterator iter;
        for (int i = 0; i < nums.size(); i++) {
            iter = my_map.find(target - nums[i]);
            if (iter != my_map.end()) {
                result.insert(result.end(), iter->second);
                result.insert(result.end(), i);
                return result;
            }
            my_map.insert(map::value_type(nums[i], i));
        }
        return result;
    }
};

结果如下。


这篇文章写起来不易,如果它对你有帮助的话,麻烦给个赞吧!!

你可能感兴趣的:(算法笔记:乱七八糟的题目汇总)