LC-初级算法-后续章节

初级算法

二叉树

LC合并俩个有序数组

给你俩个按非递减顺序排列的整数数组nums1和nums2,另有俩个整数m和n,分别表示nums1和nums2中的元素数目。
请你合并nums2nums1中,使合并后的数组同样按非递减顺序排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组nums1中。为了应对这种情况,nums1的初始长度为m+n,其中前m个元素表示应合并的元素,后n个元素为0,应忽略。nums2的长度为n。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109

进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?

题解:
参照归并排序

可以参照归并排序,具体可以看下: 排序-归并排序
代码如下:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //左指针
        int i = 0;
        //右指针
        int j = 0;
        //新数组的指针
        int index = 0;
        //新数组
        int temp[] = new int[m + n];

        while(i < m && j < n){
            //如果nums1小于nums2就放到新数组中
            if(nums1[i] < nums2[j]){
                temp[index++] = nums1[i++];
            }else{
                //否则就把nums2放到新数组中
                temp[index++] = nums2[j++];
            }
        }
        //如果第二个数组还有就继续添加
        while(j < n){
            temp[index++] = nums2[j++];
        }
        //如果第一个数组还有一样继续添加
        while(i < m){
            temp[index++] = nums1[i++];
        }
        //最后将新数组里的值赋值给第一个数组
        for(int k = 0;k < n + m;k++){
            nums1[k] = temp[k];
        }
    }
}

在这里插入图片描述

从大到小归并

题目nums1有足够的空间容纳nums2,所以我们可以认为nums1的长度肯定大于nums2.正常的归并都是从小往大开始,这里Wimbledon可以换种思路,从大往小开始。
代码如下:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //i指向第一个数组的最后一个
        int i = m - 1;
        //j指向第二个数组的最后一个
        int j = n - 1;
        //index指向nums1的最后一个
        int index = m + n -1;
        while(i >= 0 && j >= 0){
            //如果nums1大于nums2就将
            //nums1的值放到最后
            //否则把nums2的值放到最后
            if(nums1[i] > nums2[j]){
                nums1[index--] = nums1[i--];
            }else{
                nums1[index--] = nums2[j--];
            }
        }
        //最后如果第二个数组还有值则继续遍历
        //因为就是在nums1中放,所以不用管nums1的值
        while(j >= 0){
            nums1[index--] = nums2[j--];
        }
    }
}

在这里插入图片描述

LC第一个错误的版本

你是产品经理,目前正在带领一个团队开发心得产品,不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有n个版本【1,2,…n】,你想找出导致之后所有版本出错的第一个版本。
你可以通过Boolean(version)接口来判断版本号version是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用api的次数。

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。

示例 2:

输入:n = 1, bad = 1
输出:1

提示:

1 <= bad <= n <= 231 - 1

题解:
二分查找

如果我们直接从后往前找也是可以实现的,但效率比较差。这题主要考察的是二分法查找,具体可以看下二分查找
代码如下:

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        //二分查找
        //start
        int start = 1,end = n;
        while(start < end){
            int mid = start + (end - start) / 2;
            if(!isBadVersion(mid)){
                start = mid + 1;
            }else{
                end = mid;
            }
        }
        return start;
    }
}

在这里插入图片描述
上面的另一种写法:

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1;
        int right = n;
        while(left < right){
            int mid = (left + right) >>> 1;
            if(!isBadVersion(mid)){
                left = mid + 1;
            }else{
                right = mid;
            }
        }
        return left;
    }
}

在这里插入图片描述

动态规划

LC 爬楼梯

假设你正在爬楼梯。需要n阶你才能到达楼顶。
每次你可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

提示:

1 <= n <= 45

题解:
递归

我们可以使用递归,f(n)=f(n - 1) + f(n - 2)

public int climbStairs(int n) {
	if(n <= 2)
		return n;
	return climbStairs(n - 1) + climbStairs(n - 2);
}

但是这样写的话会产生很多重复计算,就会超时,其实这个问题就是一个斐波那契数列:
在这里插入图片描述

我们把代码修改一下:

class Solution {
    public int climbStairs(int n) {
        
        return helper(n,1,1);
    }
    private int helper(int n,int a,int b){
        if(n <= 1){
            return b;
        }
        return helper(n - 1,b,a + b);
    }
}

在这里插入图片描述

斐波那契数

就把他当做斐波那契数解:

class Solution {
    public int climbStairs(int n) {
        if(n <= 3)
        return n;

        int first = 1;
        int second = 2;
        int res = 0;
        while(n-- > 2){
            res = first + second;
            first = second;
            second = res;
        }
        return res;
    }
}

在这里插入图片描述

LC买卖股票的最佳时机

给定一个数组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

题解:
双指针解决

我们可以使用双指针,一个指针记录访问过得最小值(注意这里是访问过得最小值),一个指针一直往后走,任何计算他们的差值,保存最大的即可,这里就以示例1位例来画个图看下:
在这里插入图片描述
在这里插入图片描述
代码如下:

class Solution {
    public int maxProfit(int[] prices) {
    	//记录最小值
        int min = prices[0];
        //记录最后返回的值
        int res = 0;
        //从第二个开始循环
        int index = 1;
        while(index < prices.length){
        //如果当前值小于最小值就保存在min里
            if(prices[index] < min){
                min = prices[index++];
            }else{
            //如果当前值比min大就用当前值减去最小值,和当前最大利润做比较,取最大值
                res = Math.max(res,prices[index++] - min);
            }
        }
        return res;
    }
}

在这里插入图片描述

动态规划

这题是一道经典的动态规划,让求完成一笔交易所获得的最大利润,首先我们来看一下使用动态规划该怎么解决,动态规划还是常见的几个步骤:

  • 确定状态
  • 找到转移公式
  • 确定初始条件以及边界条件
  • 计算结果

我们来定义一个二维数组 dp[length][2] ,其中 dp[i][0] 来表示第i + 1天(i 是从0开始的)结束的时候没持有股票的最大利润, dp[i][1] 表示第i + 1天结束的时候持有股票的最大利润。

如果我们要求第i + 1天结束的时候没有持有股票的最大利润 dp[i][0] ,那么会有俩种情况。
第一种情况就是第 i + 1天我们既没有买也没有卖,那么最大利润就是第 i 天没有持有股票的最大利润 dp[i - 1][0]。
第二种情况就是第 i + 1天我们卖了一只股票,那么最大利润就是第i天持有股票的最大利润(这个是负的,并且也不一定是第 i 天开始持有的,有可能在第 i 天之前就已经持有了)加上第 i + 1 天卖出股票的最大利润,dp[i - 1][1] + prices[i]。

很明显我们可以得出:
dp[i][0] = max(dp[i - 1][0],dp[i - 1][1] + prices[i]);

同理我们可以得出第 i + 1天结束的时候我们持有股票的最大利润
dp[i][1] = max(dp[i - 1][1], -prices[i]);

边界条件就是第 1 天的时候,如果我们不持有股票,那么:
dp[0][0] = 0;
如果持有股票,那么
dp[0][1] = -prices[0];

有了边界条件和递推公式,代码就很容易写出来了,来看一下代码:

class Solution {
    public int maxProfit(int[] prices) {
        int dp[][] = new int[prices.length][2];
        //边界条件
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        int i = 1;
        while(i < prices.length){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],-prices[i]);
            i++;
        }
        //最后一天肯定是没有股票
        return dp[prices.length - 1][0];
    }
}

在这里插入图片描述
代码优化:
我们看到上面二维数组中计算当天的最大利润之和前一天的利润有关,所以没必要使用二维数组,把所有天的最大利润都记录了,我们只需要使用俩个变量即可,一个表示当天持有股票的最大利润,一个表示当天没有持有股票的最大利润,代码如下:

class Solution {
    public int maxProfit(int[] prices) {
        //边界条件
        int noHold= 0;
        int hold = -prices[0];
        int i = 1;
        while(i < prices.length){
            noHold = Math.max(noHold,hold + prices[i]);
            hold = Math.max(hold,-prices[i]);
            i++;
        }
        //最后一天肯定是没有股票
        return noHold;
    }
}

在这里插入图片描述

LC最大子序和

给你一个整数数组nums,请你找出一个具有最大和连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

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

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

题解:
动态规划

这题是让求最大的连续子序和,如果不是连续的非常简单,只需要把所有的正数相加即可。但这里说的是连续的,中间可能掺杂负数,如果求出一个最大子序和在加上负数肯定要比原来小了,解这题最简单的一种方式就是动态规划。

我们先来了解一下动态规划的几个步骤:
1,确定状态

2,找到转移公式

3,确定初始条件以及边界条件

4,计算结果。

最后一个不用看,只看前3个就行,因为前3个一旦确定,最后一个结果也就出来了。我们试着找一下

1,定义dp[i]表示数组中下标i为右端点的连续子数组的最大和。

2,如果要计算下标i为右端点的连续子数组的最大和,也就是计算dp[i],只需要判断dp[i-1]是大于0还是小于0。如果dp[i-1]大于0,就继续累加,dp[i]=dp[i-1]+num[i]。如果dp[i-1]小于0,我们直接把前面的舍弃,也就是说重新开始计算,否则会越加越小的,直接让dp[i]=num[i]。所以转移公式如下

dp[i]=num[i]+max(dp[i-1],0);

3,边界条件判断,当i等于0的时候,也就是前1个元素,他能构成的最大和也就是他自己,所以

dp[0]=num[0];

代码如下:

class Solution {
    public int maxSubArray(int[] nums) {
        int dp[] = new int [nums.length];
        dp[0] = nums[0];
        int res = dp[0];
        int i = 1;
        while(i < nums.length){
            dp[i] = Math.max(dp[i - 1],0) + nums[i];
            res = Math.max(res,dp[i]);
            i++;
        }
        return res;
    }
}

在这里插入图片描述
代码优化
仔细看下上面的代码会发现,我们申请了一个长度为length的数组,但在转移公式计算的时候,每次计算当前值的时候只会用到前面的那个值,再往前面就用不到了,这样实际上是造成了空间的浪费。这里不需要一个数组,只需要一个临时变量即可,看下代码

class Solution {
    public int maxSubArray(int[] nums) {
        int max = nums[0];
        int res = max;
        int i = 1;
        while(i < nums.length){
            max = Math.max(max,0) + nums[i];
            res = Math.max(res,max);
            i++;
        }
        return res;
    }
}

在这里插入图片描述

设计问题

LC打乱数组

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。

实现 Solution class:

Solution(int[] nums) 使用整数数组 nums 初始化对象
int[] reset() 重设数组到它的初始状态并返回
int[] shuffle() 返回数组随机打乱后的结果

示例 1:

输入
[“Solution”, “shuffle”, “reset”, “shuffle”]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:

1 <= nums.length <= 50
-106 <= nums[i] <= 106
nums 中的所有元素都是 唯一的
最多可以调用 104 次 reset 和 shuffle

题解:

代码如下:

class Solution {
    private int [] num;
    private Random random;
    public Solution(int[] nums) {
        num = nums;
         random = new Random();
    }
    
    public int[] reset() {
        return num;
    }
    
    public int[] shuffle() {
        if(num == null){
            return null;
        }
        int [] a = num.clone();
        for(int j = 0;j < num.length ; j++){
            int i = random.nextInt(j + 1);
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return a;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(nums);
 * int[] param_1 = obj.reset();
 * int[] param_2 = obj.shuffle();
 */

在这里插入图片描述

LC最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

提示:

-231 <= val <= 231 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次

题解:
使用辅助类解决

这道题让我们自定义一个栈,有push,pop,top,min四个函数。这题和官方的Stack相比就多了一个min函数。栈的实现我们可以使用链表,先来定义一个链表类
代码如下:

class MinStack {

    class ListNode{
        private int val;
        private int min;
        private ListNode next;

        public ListNode(int val,int min,ListNode next){
            this.val = val;
            this.min = min;
            this.next = next;
        }


    }
    ListNode head;
    
    public void push(int val) {
        if(head == null){
            head = new ListNode(val,val,null);
        }else{
            head = new ListNode(val,Math.min(head.min,val),head);
        }
    }
    
    public void pop() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        head = head.next;
    }
    
    public int top() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return head.val;
    }
    
    public int getMin() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return head.min;
    }
        //判断栈是否为空
    private boolean empty() {
        return head == null;
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

在这里插入图片描述

使用单个栈解决

也可以使用官方提供的栈,当压栈的值小于栈中最小值时,先把最小值入栈,然后再把需要压栈的值入栈,最后再更新栈中最小值。如果压栈的值大于栈中最小值的时候,直接压栈,这里就以[6,2,1,4]分别入栈来看一下

class MinStack {
    int min = Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<>();

    public void push(int val) {
        if(val <= min){
            stack.push(min);
            min = val;
        }
        stack.push(val);
    }
    
    public void pop() {
        if(stack.pop() == min){
            min = stack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return min;
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

在这里插入图片描述

数字

LC Fizz Buzz

给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:

answer[i] == “FizzBuzz” 如果 i 同时是 3 和 5 的倍数。
answer[i] == “Fizz” 如果 i 是 3 的倍数。
answer[i] == “Buzz” 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。

示例 1:

输入:n = 3
输出:[“1”,“2”,“Fizz”]

示例 2:

输入:n = 5
输出:[“1”,“2”,“Fizz”,“4”,“Buzz”]
示例 3:

输入:n = 15
输出:[“1”,“2”,“Fizz”,“4”,“Buzz”,“Fizz”,“7”,“8”,“Fizz”,“Buzz”,“11”,“Fizz”,“13”,“14”,“FizzBuzz”]

提示:

1 <= n <= 104

题解:

代码如下:

class Solution {
    public List<String> fizzBuzz(int n) {
        List<String> list = new ArrayList(n);
        //全部初始化为数字
        for(int i = 1;i <= n;i++){
            list.add(i + "");
        }
        //如果是3的倍数,就把他改为“Fizz”
        for(int i = 2;i < n;i+=3){
            list.set(i,"Fizz");
        }
        //如果是5的倍数,就把他改为“Buzz”
        for(int i = 4;i < n;i += 5){
            list.set(i,"Buzz");
        }
        //如果是3和5的倍数,也就是15的倍数,把它改为“FizzBuzz”
        for(int i = 14; i < n;i += 15){
            list.set(i,"FizzBuzz");
        }
        return list;

    }
}

在这里插入图片描述
优化代码之后:

class Solution {
    public List<String> fizzBuzz(int n) {
        List<String> list = new ArrayList();
        for(int i = 1;i <= n;i++){
            if(i % 15 ==0){
                list.add("FizzBuzz");
                continue;
            }
            if(i % 5 == 0){
                list.add("Buzz");
                continue;
            }else if(i % 3 == 0){
                list.add("Fizz");
                continue;
            }else{
                list.add(i+"");
            }
        }
        return list;
    }
}

在这里插入图片描述

LC 3的幂数

给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x

示例 1:

输入:n = 27
输出:true
示例 2:

输入:n = 0
输出:false
示例 3:

输入:n = 9
输出:true
示例 4:

输入:n = 45
输出:false

提示:

-231 <= n <= 231 - 1

进阶:你能不使用循环或者递归来完成本题吗?

题解:
一直除3

代码如下:

class Solution {
    public boolean isPowerOfThree(int n) {
        if(n > 1){
            while(n % 3 ==0){
                n /= 3;
            }
        }
        return n==1;
    }
}

在这里插入图片描述

LC 罗马数字转整数

罗马数字包含以下七种字符: 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。
给定一个罗马数字,将其转换成整数。

示例 1:

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

示例 2:

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

示例 3:

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

示例 4:

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

示例 5:

输入: 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 。

题解:
列举全部

把所有的可能组合都列出来,如果两个字符能组成一个有效数字,就不要拆开。比如"IV",因为它能组成有效的数字,不能把它拆成I和V。

然后截取字符串s,每两位截取,看看能不能构成有效的数字,如果能构成有效的数字,就把他们看做一个整体,如果不能构成有效的数字,就截取一位。代码如下

class Solution {
    public int romanToInt(String s) {
 Map<String, Integer> map = new HashMap<>();
        //所有可能的都列出来
        map.put("I", 1);
        map.put("IV", 4);
        map.put("V", 5);
        map.put("IX", 9);
        map.put("X", 10);
        map.put("XL", 40);
        map.put("L", 50);
        map.put("XC", 90);
        map.put("C", 100);
        map.put("CD", 400);
        map.put("D", 500);
        map.put("CM", 900);
        map.put("M", 1000);
        int i = 0;
        int res = 0;
        while(i < s.length()){
            if(i + 1 < s.length() && map.containsKey(s.substring(i,i + 2))){
                res += map.get(s.substring(i,i + 2));
                i += 2;
            }else{
                res += map.get(s.substring(i,i + 1));
                i++;
            }
        }
        return res;
    }

}

在这里插入图片描述

前后比较

题中说了通常情况下,罗马数字中小的数字在大的数字的右边。也就是说如果小写的在大写的右边,每个字符都是一个有效的数字,他表示的数字就是所有字符相加,比如VI就是5+1=6。

如果小写的在大写的左边,就是无效的,只有一种情况,就是这个小写的和大写的组成一个数字,比如IV表示的是5-1=4,

搞懂了上面的原理,代码就简单多了,

class Solution {
    public int romanToInt(String s) {
        int res = 0;
        //获取前一个字符的值
        int w = getValue(s.charAt(0));
        for(int i = 1;i < s.length() ; i++){
            int c = getValue(s.charAt(i));
            if(w < c){
                res -= w; 
            }else{
                res += w;
            }
            w = c;
        }
        return res + w;
    }
    public int getValue(char a){
       switch (a) {
            case 'I':
                return 1;
            case 'V':
                return 5;
            case 'X':
                return 10;
            case 'L':
                return 50;
            case 'C':
                return 100;
            case 'D':
                return 500;
            case 'M':
                return 1000;
            default:
                return 0;
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值