【leetcode】算法题记录(171-180)

Excel表列序号

给定一个Excel表格中的列名称,返回其相应的列序号。

例如,

    A -> 1
    B -> 2
    C -> 3
    ...
    Z -> 26
    AA -> 27
    AB -> 28 
    ...

示例 1:

输入: "A"
输出: 1

示例 2:

输入: "AB"
输出: 28

示例 3:

输入: "ZY"
输出: 701

解题思路:之前做将数字转为列表号的时候就已经说明这道题实质上是进制转换的问题,所以这样的话思路也是很明确。

class Solution {
public:
    int titleToNumber(string columnTitle) {
        int res = 0;
        int n = columnTitle.size();
        for(int i = 0; i < n; ++i) {
            res = res + (columnTitle[i] - 'A' + 1) * pow(26, (n-i-1));
            // res = res * 26 + (columnTitle[i] - 'A' + 1);
        }
        return res;
    }
};

阶乘后的零

给定一个整数 n,返回 n! 结果尾数中零的数量。

示例 1:

输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。

示例 2:

输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.

说明: 你算法的时间复杂度应为 O(log n) 。

解题思路:看到这个时间复杂度,自然而然的想到二分法。然后解析这道题,发现尾数中零的个数,可以转化包含多少个 10 相乘,而阶乘的运算就是相乘,乘法是具有交换律的。

所以这道题可以等价于寻找阶乘中 10 的个数,因为 10 不是质数,所以也就是 2 * 5 的个数。而明显可以发现的是 2 的次数明显是多余 5 的次数的。

最终这道题变成了寻找这个整数阶乘中 5 的次数,或者就是所谓的五分法,时间复杂度也是 O(log n)。

class Solution {
public:
    int trailingZeroes(int n) {
        int res = 0;
        while (n > 0) {
            n /= 5;
            res += n;
        }
        return res;
    }
};

二叉搜索树迭代器

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

示例:

输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]

解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next();    // 返回 3
bSTIterator.next();    // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 20
bSTIterator.hasNext(); // 返回 False

提示:

    树中节点的数目在范围 [1, 10^5] 内
    0 <= Node.val <= 10^6
    最多调用 10^5 次 hasNext 和 next 操作


进阶:

    你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。

解题思路:冷知识,中序遍历搜索二叉树的中序遍历就是一个有序数组。首先想到的就是利用数组把前序遍历存下来然后遍历数组即可,这样的话能够满足查询和迭代时间复杂度满足条件,但是空间复杂度不满足要求。

如果需要满足题目的要求,则需要考虑到给的二叉树,因为用上面的方法,在后面调用方法的时候完全没有用到二叉树,而且这道题其实不用关系上一个节点的,所以联想到遍历的第二种方式,也就是利用迭代去寻找 next。这样可以达到平均复杂度达到要求。

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator* obj = new BSTIterator(root);
 * int param_1 = obj->next();
 * bool param_2 = obj->hasNext();
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

// 利用 数组+递归 保存有序数组
class BSTIterator {
public:

    vector<int> mRoot;
    vector<int>::iterator mIter;

    void PreVisit(TreeNode* root) {
        if(root) {
            PreVisit(root->left);
            mRoot.push_back(root->val);
            PreVisit(root->right);
        }
    }

    BSTIterator(TreeNode* root) {
        PreVisit(root);
        mIter = mRoot.begin();
    }
    
    int next() {
        return *mIter++;
    }
    
    bool hasNext() {
        return mIter != mRoot.end();
    }
};

//利用 栈+迭代 依次遍历
class BSTIterator {
public:
    TreeNode* cur;
    stack<TreeNode*> stk;

    BSTIterator(TreeNode* root) {
        cur = root;
    }
    
    int next() {
        while (cur != nullptr) {
            stk.push(cur);
            cur = cur->left;
        }
        cur = stk.top();
        stk.pop();
        int ret = cur->val;
        cur = cur->right;
        return ret;
    }
    
    bool hasNext() {
        return cur != nullptr || !stk.empty();
    }
};

地下城游戏

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快到达公主,骑士决定每次只向右或向下移动一步。

编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。

例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
-2(K)   -3      3
-5      -10 	1
10      30      -5 (P)

说明:
    骑士的健康点数没有上限。
    任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

解题思路:看到这个道题,想到的方法有回溯 + 动态规划。发现动态规划更适合去解答这道题。

动态规划步骤:

  • 利用 dp0[i][j] 表示在地图 (i,j) 处当前的生命值,dp1[i][j] 为所需要的生命上限。
  • 初始化为 dp0[0][0] = dp1[i][j] = dungeon[0][0]
  • 生命值状态转移 dp0[i][j] = min(dp0[i-1][j], dp0[i][j-1]) + dp0[i][j];
  • 生命上限状态转移 dp1[i][j] = min(dp0[i-1][j] < dp0[i][j-1] ? dp1[i-1][j] : dp1[i][j-1], dp0[i][j]);

此路不通,因为最小值和当前生命值无法权衡。尝试回溯,结果超时。

然后优化的方案有记忆化回溯,重新思考动态规划。

其实动态规划是有方向的,可以从起点开始,也可以从终点开始,我们最开始是从起点出发,发现需要考虑两个方面,走到当前需要的最少生命值和当前的生命值,而且这两方面是无法达到同步的。

所以可以从终点出发,这样可以只用考虑

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int n = dungeon.size(), m = dungeon[0].size();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1, INT_MAX));
        dp[n][m - 1] = dp[n - 1][m] = 1;
        for (int i = n - 1; i >= 0; --i) {
            for (int j = m - 1; j >= 0; --j) {
                int minn = min(dp[i + 1][j], dp[i][j + 1]);
                dp[i][j] = max(minn - dungeon[i][j], 1);
            }
        }
        return dp[0][0];
    }
};

最大数

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

示例 1:

输入:nums = [10,2]
输出:"210"

示例 2:

输入:nums = [3,30,34,5,9]
输出:"9534330"

示例 3:

输入:nums = [1]
输出:"1"

示例 4:

输入:nums = [10]
输出:"10"

提示:

    1 <= nums.length <= 100
    0 <= nums[i] <= 10^9

解题思路:利用排序的思想,先将数组排序。而且 sort 函数是运行自己添加排序规则的。

class Solution {
public:
    bool bigger(int f, int s) {
		string fir = to_string(f);
		string sec = to_string(s);
		long fAdds = f * pow(10, sec.size()) + s;
		long sAddf = s * pow(10, fir.size()) + f;
        return fAdds > sAddf;
    }

    string largestNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end(), bind(&Solution::bigger, this, placeholders::_1, placeholders::_2));
        string res;
        if(nums[0] == 0) return "0";
        for(auto i : nums) {
            res += to_string(i);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值