leetcode刷题记录24(2023-10-05)【直线上最多的点数(枚举、哈希、保存分子分母解决精度问题) | 寻找峰值(二分) | 分数到小数(余数相同即循环节、哈希) | xls序列(模拟)】

149. 直线上最多的点数

给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。

示例 1:

在这里插入图片描述

输入:points = [[1,1],[2,2],[3,3]]
输出:3
示例 2:

在这里插入图片描述

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

提示:

1 < = p o i n t s . l e n g t h < = 300 1 <= points.length <= 300 1<=points.length<=300
p o i n t s [ i ] . l e n g t h = = 2 points[i].length == 2 points[i].length==2
− 1 0 4 < = x i , y i < = 1 0 4 -10^4 <= x_i, y_i <= 10^4 104<=xi,yi<=104
points 中的所有点 互不相同

主要思路就是二重循环进行枚举,枚举从一个点开始,到其它点上的斜率,如果一致,就保存在map里,同时记录他出现的次数。这里需要涉及到一个的hash过程,参考了题解:https://leetcode.cn/problems/max-points-on-a-line/solutions/842114/zhi-xian-shang-zui-duo-de-dian-shu-by-le-tq8f/

同时,需要利用 δ x \delta x δx δ y \delta y δy来表示 k = δ x δ y k=\frac{\delta x}{\delta y} k=δyδx,防止出现精度误差。因此,需要进行一个最小公约数的化简。

#include <vector>
#include <unordered_map>
#include <cmath>
using namespace std;

class Solution
{
    int gcd(int x, int y)
    {
        return y > 0 ? gcd(y, x % y) : x;
    }

public:
    int maxPoints(vector<vector<int>> &points)
    {
        int n = points.size();
        if (n <= 2)
            return n;
        int res = 0;
        for (int i = 0; i < n; i++)
        {
            if (res > n - i || res > n / 2)
            {
                break;
            }
            unordered_map<int, int> mp;
            for (int j = i + 1; j < points.size(); j++)
            {
                int x = points[j][0] - points[i][0];
                int y = points[j][1] - points[i][1];
                if (x == 0)
                {
                    y = 1;
                }
                else if (y == 0)
                {
                    x = 1;
                }
                else
                {
                    if (y < 0)
                    {
                        y = -y;
                        x = -x;
                    }
                    int gcdXY = gcd(abs(x), abs(y));
                    x /= gcdXY;
                    y /= gcdXY;
                }
                mp[y + x * 20001]++;
            }
            for (auto &[_, num] : mp)
            {
                res = max(res, num + 1);
            }
        }
        return res;
    }
};

162. 寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 n u m s [ − 1 ] = n u m s [ n ] = − ∞ nums[-1] = nums[n] = -∞ nums[1]=nums[n]=

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

示例 1:

输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。

提示:

1 <= nums.length <= 1000
− 2 31 < = n u m s [ i ] < = 2 31 − 1 -2^{31} <= nums[i] <= 2^{31} - 1 231<=nums[i]<=2311
对于所有有效的 i 都有 nums[i] != nums[i + 1]

主要思路就是二分查找,具体的是,沿着数组,往大的方向走。

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

class Solution
{
public:
    int findPeakElement(vector<int> &nums)
    {
        int n = nums.size();
        int left = 0;
        int right = n - 1;
        int mid = -1;
        while (left <= right)
        {
            mid = (left + right) / 2;
            if ((mid + 1 == n || nums[mid + 1] < nums[mid]) &&
                (mid - 1 == -1 || nums[mid - 1] < nums[mid]))
            {
                break;
            }
            if (mid + 1 == n || nums[mid + 1] > nums[mid])
            {
                left = mid + 1;
            }
            else if (mid - 1 == -1 || nums[mid - 1] > nums[mid])
            {
                right = mid - 1;
            }
        }
        return mid;
    }
};

int main()
{
    vector<int> vec = {1, 2, 3, 9, 5};
    Solution sol;
    int res = sol.findPeakElement(vec);
    cout << res;
}

166. 分数到小数

给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。

如果小数部分为循环小数,则将循环的部分括在括号内。

如果存在多个答案,只需返回 任意一个 。

对于所有给定的输入,保证 答案字符串的长度小于 104 。

示例 1:

输入:numerator = 1, denominator = 2
输出:“0.5”
示例 2:

输入:numerator = 2, denominator = 1
输出:“2”
示例 3:

输入:numerator = 4, denominator = 333
输出:“0.(012)”

提示:

− 2 31 < = n u m e r a t o r , d e n o m i n a t o r < = 2 31 − 1 -2^{31} <= numerator, denominator <= 2^{31} - 1 231<=numerator,denominator<=2311
denominator != 0

这道题目的关键就是如何找到循环的部分,很巧妙:

计算小数部分时,每次将余数乘以 10,然后计算小数的下一位数字,并得到新的余数。重复上述操作直到余数变成 0 或者找到循环节。

如果余数变成 0,则结果是有限小数,将小数部分拼接到结果中。

如果找到循环节,则找到循环节的开始位置和结束位置并加上括号,然后将小数部分拼接到结果中。

如何判断是否找到循环节?注意到对于相同的余数,计算得到的小数的下一位数字一定是相同的,因此如果计算过程中发现某一位的余数在之前已经出现过,则为找到循环节。为了记录每个余数是否已经出现过,需要使用哈希表存储每个余数在小数部分第一次出现的下标。1

#include <string>
#include <map>
using namespace std;

class Solution
{
public:
    string fractionToDecimal(int numerator, int denominator)
    {
        long long numeratorLong = numerator;
        long long denominatorLong = denominator;
        if (denominatorLong == 0)
        {
            return "";
        }
        string res = "";
        // 获取符号部分
        if ((double)numeratorLong / denominatorLong < 0)
        {
            res += "-";
            numeratorLong = abs(numeratorLong);
            denominatorLong = abs(denominatorLong);
        }
        // 计算整数部分
        int intPart = numeratorLong / denominatorLong;
        res += to_string(intPart);
        // 小数部分
        int remainder = numeratorLong % denominatorLong;
        if (remainder == 0)
        {
            return res;
        }
        // 拼接小数点
        res += ".";
        // 计算小数部分
        string fractionPart = "";
        // 建立map来记录之前出现过的数字(数字-index)
        map<int, int> mp;
        int idx = 0;
        while (remainder != 0)
        {
            // 计算当前位的小数
            fractionPart += to_string(remainder * 10 / denominatorLong);
            // 说明还没有存在重复的余数
            if (mp.find(remainder) == mp.end())
            {
                mp[remainder] = idx;
            }
            else
            {
                // 获取循环小数部分
                string repeatPart = fractionPart.substr(mp[remainder], idx - mp[remainder]);
                // 拼接小数部分整体
                fractionPart = fractionPart.substr(0, mp[remainder]) + "(" + repeatPart + ")";
                break;
            }
            remainder = remainder * 10 % denominatorLong;
            idx++;
        }
        res += fractionPart;
        return res;
    }
};

171. Excel 表列序号

给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。

例如:

A -> 1
B -> 2
C -> 3

Z -> 26
AA -> 27
AB -> 28

示例 1:

输入: columnTitle = “A”
输出: 1

示例 2:

输入: columnTitle = “AB”
输出: 28

示例 3:

输入: columnTitle = “ZY”
输出: 701

提示:

1 <= columnTitle.length <= 7
columnTitle 仅由大写英文组成
columnTitle 在范围 [“A”, “FXSHRXW”] 内

就是字符串转化为int,相当于是26进制而已,代码如下:

#include <string>

using namespace std;

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

  1. https://leetcode.cn/problems/fraction-to-recurring-decimal/solutions/1028368/fen-shu-dao-xiao-shu-by-leetcode-solutio-tqdw/ ↩︎

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherries Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值