带你刷算法——数组/字符串的完全掌握(一)(中)

简介: 带你刷算法——数组/字符串的完全掌握(一)

4.多数元素


给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。


你可以假设数组是非空的,并且给定的数组总是存在多数元素。


示例 1:


输入:nums = [3,2,3] 输出:3


示例 2:


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


提示:



/

n == nums.length
1 <= n <= 5 * 104
-109 <= nums[i] <= 109


题解


思路一:暴力解法


暴力解法是通过遍历数组并统计每个元素的出现次数,然后判断是否满足大于 n/2 的条件。以下是一个使用暴力解法的示例代码:

public class Solution {
    public int majorityElement(int[] nums) {
        int n = nums.length;
        for (int num : nums) {
            int count = 0;
            for (int i = 0; i < n; i++) {
                if (nums[i] == num) {
                    count++;
                }
            }
            if (count > n / 2) {
                return num;
            }
        }
        return -1; // 如果输入的数组不符合题目要求,没有多数元素,则返回一个特定的值(例如 -1)
    }
}


上述代码中,我们使用两层循环来遍历整个数组。外层循环用于遍历每个元素,内层循环用于统计该元素在数组中出现的次数。如果某个元素的出现次数大于 n/2,则返回该元素作为多数元素。


请注意,上述代码并没有对输入数组进行非空判断或检查是否存在多数元素。根据题目描述,假设输入数组是非空的,并且总是存在多数元素。但在实际应用中,需要增加相应的逻辑来处理这些边界情况。


思路二:复杂度为O(1)随机化法


class Solution {
    private int randRange(Random rand, int min, int max) {
        return rand.nextInt(max - min) + min;
    }
    private int countOccurences(int[] nums, int num) {
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == num) {
                count++;
            }
        }
        return count;
    }
    public int majorityElement(int[] nums) {
        Random rand = new Random();
        int majorityCount = nums.length / 2;
        while (true) {
            int candidate = nums[randRange(rand, 0, nums.length)];
            if (countOccurences(nums, candidate) > majorityCount) {
                return candidate;
            }
        }
    }
}


这段代码的目标是找出给定整数数组中的主要元素(即出现次数超过一半的元素)。下面是该代码的思路:


  1. 在 randRange 方法中,使用 Random 类生成一个介于 min 和 max 之间的随机整数。
  2. countOccurences 方法用于计算在给定数组 nums 中等于 num 的元素个数。
  3. majorityElement 方法是主要的方法,它首先创建了一个 Random 对象 rand。
  4. 然后根据数组长度计算出了主要元素的最小出现次数 majorityCount,即数组长度的一半。
  5. 使用一个无限循环,在每次迭代中选择一个随机索引,并将对应的元素赋值给 candidate。
  6. 然后通过调用 countOccurences 方法来计算 candidate 在数组中的出现次数。
  7. 如果 candidate 的出现次数超过了 majorityCount,则返回 candidate,因为它是主要元素。
  8. 如果没有找到主要元素,则会继续循环直到找到为止。

需要注意的是,由于数组中一定存在一个主要元素,所以这个循环一定会终止并返回结果。但是,这种方法的时间复杂度可能很高,因为在每次迭代中需要遍历整个数组来计算出现次数。在某些情况下,这可能导致性能问题。


5.买卖股票的最佳时机


给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。


你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。


返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。


示例 1:


输入:[7,1,5,3,6,4] 输出:5

解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 =6)的时候卖出,最大利润 = 6-1 = 5 。

注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。


示例 2:


输入:prices = [7,6,4,3,1] 输出:0

解释:在这种情况下, 没有交易完成, 所以最大利润为 0。


提示:


1 <= prices.length <= 105

0 <= prices[i] <= 104



题解


思路一:暴力法


暴力解法可以通过考虑每一天作为买入点,然后在之后的每一天作为卖出点,计算利润并找到最大值。以下是使用暴力解法的Java代码示例:

class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        for (int i = 0; i < prices.length - 1; i++) {
            for (int j = i + 1; j < prices.length; j++) {
                int profit = prices[j] - prices[i];
                if (profit > maxProfit) {
                    maxProfit = profit;
                }
            }
        }
        return maxProfit;
    }
}


上述代码中,我们使用两个循环嵌套来遍历所有可能的买入和卖出点组合。对于每个组合,我们计算卖出价格减去买入价格得到的利润,并将其与当前的最大利润进行比较。如果新计算的利润更大,则更新最大利润。


然后返回最大利润作为结果。这种暴力解法的时间复杂度为 O(n^2),其中 n 是数组的长度。


思路二:一次遍历


public class Solution {
    public int maxProfit(int prices[]) {
        int minprice = Integer.MAX_VALUE;
        int maxprofit = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minprice) {
                minprice = prices[i];
            } else if (prices[i] - minprice > maxprofit) {
                maxprofit = prices[i] - minprice;
            }
        }
        return maxprofit;
    }
}


  1. 初始化minprice为整数的最大值,maxprofit为0。minprice用于记录迄今为止最低的股票价格,maxprofit用于记录迄今为止最大的利润。
  2. 遍历给定的股票价格数组prices,从索引0开始到数组末尾。
  3. 对于每个价格prices[i],首先检查它是否比当前的minprice更低。如果是,则更新minprice为prices[i],因为我们找到了一个更低的购买价格。
  4. 如果prices[i]不是比当前的minprice更低,那么我们计算以当前价格卖出时的利润。将prices[i] - minprice与当前的maxprofit进行比较,如果大于maxprofit,则更新maxprofit为prices[i] - minprice,因为我们找到了一个更大的利润。
  5. 完成遍历后,maxprofit中存储的就是股票买卖的最大利润。


该算法的时间复杂度为O(n),其中n是股票价格数组的长度。


6.罗马数字转整数


罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。



/

字符 数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000


例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。


通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:


I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。

X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。

C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。


示例


输入: s = “III” 输出: 3


输入: s = “IV” 输出: 4


输入: s = “IX” 输出: 9 :


输入: s = “LVIII” 输出: 58 解释: L = 50, V= 5, III = 3.


输入: s = “MCMXCIV” 输出: 1994 解释: M = 1000, CM = 900, XC = 90, IV = 4.


提示:


1 <= s.length <= 15

s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’)

题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内

题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。

IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。

关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。



题解


思路一:暴力法


class Solution {
    Map<Character, Integer> symbolValues = new HashMap<Character, Integer>() {{
        put('I', 1);
        put('V', 5);
        put('X', 10);
        put('L', 50);
        put('C', 100);
        put('D', 500);
        put('M', 1000);
    }};
    public int romanToInt(String s) {
        int ans = 0;
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            int value = symbolValues.get(s.charAt(i));
            if (i < n - 1 && value < symbolValues.get(s.charAt(i + 1))) {
                ans -= value;
            } else {
                ans += value;
            }
        }
        return ans;
    }
}


通常情况下,罗马数字中小的数字在大的数字的右边。若输入的字符串满足该情况,那么可以将每个字符视作一个单独的值,累加每个字符对应的数值即可。


例如 XXVII\texttt{XXVII}XXVII 可视作 X+X+V+I+I=10+10+5+1+1=27\texttt{X}+\texttt{X}+\texttt{V}+\texttt{I}+\texttt{I}=10+10+5+1+1=27X+X+V+I+I=10+10+5+1+1=27。


若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反。


例如 XIV\texttt{XIV}XIV 可视作 X−I+V=10−1+5=14\texttt{X}-\texttt{I}+\texttt{V}=10-1+5=14X−I+V=10−1+5=14。


7.最后一个单词长度



给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。


单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。


示例


输入:s = “Hello World” 输出:5 解释:最后一个单词是“World”,长度为5。


输入:s = " fly me to the moon " 输出:4 解释:最后一个单词是“moon”,长度为4。


输入:s = “luffy is still joyboy” 输出:6 解释:最后一个单词是长度为6的“joyboy”。


提示:


1 <= s.length <= 104

s 仅有英文字母和空格 ’ ’ 组成

s 中至少存在一个单词



相关文章
|
3天前
|
算法 搜索推荐 Java
滚雪球学Java(33):数组算法大揭秘:应用案例实战分享
【5月更文挑战第8天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
31 8
滚雪球学Java(33):数组算法大揭秘:应用案例实战分享
|
6天前
|
算法 搜索推荐 程序员
第六十五练 字符串匹配 - Rabin-Karp算法
第六十五练 字符串匹配 - Rabin-Karp算法
8 1
|
6天前
|
算法 搜索推荐 程序员
第六十四练 字符串匹配 - Boyer-Moore算法
第六十四练 字符串匹配 - Boyer-Moore算法
9 0
|
6天前
|
算法 搜索推荐 程序员
第六十三练 字符串匹配 - KMP算法
第六十三练 字符串匹配 - KMP算法
8 2
|
6天前
|
搜索推荐 算法 Java
滚雪球学Java(29):数组长度和排序算法:让你的程序更高效
【5月更文挑战第4天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
13 0
滚雪球学Java(29):数组长度和排序算法:让你的程序更高效
|
6天前
|
算法 C语言 人工智能
|
6天前
|
存储 算法 Java
数据结构与算法 数组和链表
数据结构与算法 数组和链表
12 0
|
6天前
|
存储 算法
Leetcode 30天高效刷数据结构和算法 Day1 两数之和 —— 无序数组
给定一个无序整数数组和目标值,找出数组中和为目标值的两个数的下标。要求不重复且可按任意顺序返回。示例:输入nums = [2,7,11,15], target = 9,输出[0,1]。暴力解法时间复杂度O(n²),优化解法利用哈希表实现,时间复杂度O(n)。
22 0
|
6天前
|
算法
代码随想录算法训练营第五十五天 | LeetCode 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结
代码随想录算法训练营第五十五天 | LeetCode 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结
25 1
|
6天前
|
算法 JavaScript 前端开发
贪心算法】按要求补齐数组
贪心算法】按要求补齐数组
10 0