LeetCode12. 整数转罗马数字

2021.5.14 每日一题

题目描述

罗马数字包含以下七种字符: 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 到 3999 的范围内。示例 1:输入: 3
输出: "III"
示例 2:输入: 4
输出: "IV"
示例 3:输入: 9
输出: "IX"
示例 4:输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

思路

经典问题了,思路就是模拟吧,从高位到低位依次处理,我也是这样写的:

class Solution {public String intToRoman(int num) {//经典问题了,必须拿下//数字最大3999,因此先除以1000,等于几,添加几个M//再除以100,等于9 加 CM ,等于4 加CD,其余如果大于等于5,先加D,再加几个C;小于5,加C//下面的处理类似,//想想怎么循环起来StringBuilder sb = new StringBuilder();//用一个Map来存储当前位置(个十百千)对应的罗马数字Map<Integer, Character> map = new HashMap<>(){{put(1, 'I');put(5, 'V');put(10, 'X');put(50, 'L');put(100, 'C');put(500, 'D');put(1000, 'M');}};int base = 1000;//统计每位的个数while(num > 0){int count = num / base;num = num - count * base;char ge = map.get(base);char wu = ' ';char shi = ' ';if(map.containsKey(base * 5))wu = map.get(base * 5);if(map.containsKey(base * 10))shi = map.get(base * 10);if(count == 9){sb.append(ge);sb.append(shi);count = 0;}else if(count == 4){sb.append(ge);sb.append(wu);count = 0;}else if(count >= 5){sb.append(wu);count = count - 5;}for(int i = 0; i < count; i++){sb.append(ge);}base /= 10;}return sb.toString();}
}

没啥问题,看了题解吧,发现也是模拟,只不过把数分的更加详细了,贴一个weiwei哥的代码:

public class Solution {public String intToRoman(int num) {// 把阿拉伯数字与罗马数字可能出现的所有情况和对应关系,放在两个数组中,并且按照阿拉伯数字的大小降序排列int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};String[] romans = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};StringBuilder stringBuilder = new StringBuilder();int index = 0;while (index < 13) {// 特别注意:这里是等号while (num >= nums[index]) {stringBuilder.append(romans[index]);num -= nums[index];}index++;}return stringBuilder.toString();}
}

剑指 Offer 40. 最小的k个数

题目描述

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。示例 1:输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:输入:arr = [0,1,2,1], k = 1
输出:[0]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

第一种:堆排序,用最大堆存储数组中最小的k个数,如果堆顶大于新来的数,就弹出,加入当前数,最后堆中就是k个最小的数

class Solution {public int[] getLeastNumbers(int[] arr, int k) {//最小的数可以求,最小的两个数可以求,那么最小的四个数其实也可以写,只不过有点复杂,判断条件多了//或者排序//用最大堆,堆中存储4个最小的数字,如果新的数字小于堆顶,进行替换if(k == 0)return new int[]{};PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){public int compare(Integer a, Integer b){return b - a;}});for(int i = 0; i < arr.length; i++){if(pq.size() < k){pq.offer(arr[i]);}else{if(pq.peek() > arr[i]){pq.poll();pq.offer(arr[i]);}}}int[] res = new int[k];for(int i = 0; i < k; i++){res[i] = pq.poll();}return res;}
}

第二种:快排思想

先来复习一下快排,快排思路就是将数组划分为小于基准数和大于基准数两部分,最重要的是细节
刚开始写了下面的代码:

class Solution {public int[] getLeastNumbers(int[] arr, int k) {//来写一下快排quickSort(arr, 0, arr.length - 1);return Arrays.copyOf(arr, k);}   public void quickSort(int[] arr, int l, int r){if(l >= r)return;//基准值取最左边的值int base = arr[l];int i = l; int j = r;while(i < j){while(i < j && arr[i] <= base)i++;while(i < j && arr[j] >= base)j--;swap(arr, i, j);}swap(arr, i, l);quickSort(arr, l, i - 1);quickSort(arr, i + 1, r);}public void swap(int[] arr, int i, int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}

这个报错了,[0,1,2,1] 1 这个例子倒下了,我输出的是1,答案应该是0
然后我把下面两个循环的顺序颠倒了,就过了

         while(i < j && arr[j] >= base)j--;while(i < j && arr[i] <= base)i++;

这说明第一种写法,排序结果不对,为什么呢,看一下错误的例子,
第一轮排序,基准是0,第一轮两个循环结束,i == 1,j == 1,交换不变,最后交换基准,把0和1的位置交换了,所以发生错误
第二种写法,先移动j,第一轮两个循环结束,j = 0,i = 0,交换不变,最后交换基准,也不变,即0排在最前面,正确
因此,第一种快排模板,很简洁明了,背过

第二种模板,也还行吧,也顺带记一下

     private int partition(int[] nums, int lo, int hi) {int v = nums[lo];int i = lo, j = hi + 1;while (true) {while (++i <= hi && nums[i] < v);while (--j >= lo && nums[j] > v);if (i >= j) {break;}int t = nums[j];nums[j] = nums[i];nums[i] = t;}nums[lo] = nums[j];nums[j] = v;return j;}

然后针对这个问题,对快排进行改进,因为要返回的是最小的k个数,因此不需要对数组进行排序,只需要将数组划分为长度为k和剩余部分两个部分就可以了。
因此只需要找到基准位置的下标为k就可以了,此时,左边的数正好是k个

class Solution {public int[] getLeastNumbers(int[] arr, int k) {//来写一下快排——> 快排思想//如果k比数组长度还大,就直接返回数组if(k >= arr.length)return arr;return quickSort(arr, 0, arr.length - 1, k);}   public int[] quickSort(int[] arr, int l, int r, int k){//基准值取最左边的值int base = arr[l];int i = l; int j = r;while(i < j){while(i < j && arr[j] >= base)j--;while(i < j && arr[i] <= base)i++;swap(arr, i, j);}swap(arr, i, l);//如果当前基准数的位置大于k,说明在左边if(i > k) return quickSort(arr, l, i - 1, k);if(i < k) return quickSort(arr, i + 1, r, k);return Arrays.copyOf(arr, k);}public void swap(int[] arr, int i, int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

剑指 Offer 41. 数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。例如,[2,3,4] 的中位数是 3[2,3] 的中位数是 (2 + 3) / 2 = 2.5设计一个支持以下两种操作的数据结构:void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

还是想办法优化时间复杂度,因为如果用数组每次插入找到位置,然后移动的话,时间复杂度为O(n)
然后就想起之前做过的用两个对顶堆求中位数的方法;中位数把一组数分成了大数部分和小数部分,用一个小顶堆存放大数部分,堆顶就是大数部分最小数;大顶堆存放小数部分,堆顶就是小数部分最大数
主要是怎么维护这两个堆,首先插入元素,如果大于小顶堆堆顶,放入小顶堆;小于大顶堆堆顶,放在大顶堆;介于中间,也放在小顶堆
然后调整两个堆的大小,使得大小之差小于等于1
仔细想了一下后,写了下面代码:

class MedianFinder {//怎么快速计算中位数,//如果之前是偶数个数,中位数是m,m = (l + r)/ 2 //现在添加了一个t,如果t大于r,中位数变成r;小于l,中位数变成l;l和r之间,中位数为t//现在是奇数,中位数是m,记录左边的数l和右边的数r,添加一个数,如果大于r,更新mid = (r + mid) / 2,l = mid,r = r;//同理处理小于r和在l和r之间的情况,不行。。。。。。。。。。。//那么怎么办呢,突然想起来之前做过的一道题,两个堆找中位数,就是一个大根堆,一个小跟堆,两个顶部的数就是中位数//想想逻辑怎么实现,小根堆里存大数,大根堆里存小数//先把第一个数放进任意一个堆中,例如小根堆中,//然后放第二个数,如果第二个数大于第一个数,那么就把第一个数弹出,放入大根堆中,再把第二个数放在小根堆中//放第三个数,和两个堆顶的数进行比较,如果大于小根堆堆顶,放在小根堆中;小于大根堆堆顶,放在大根堆里;介于之间,放在小根堆//放入以后,调整两个堆的数个数,使得两个堆个数之差不大于1//这样,如果奇数,就取小根堆堆顶的数;如果偶数,就两个堆堆顶的数平均//放一个数的时间复杂度为O(logn),计算中位数的时间复杂度为O(1)/** initialize your data structure here. */PriorityQueue<Integer> small;PriorityQueue<Integer> big;public MedianFinder() {small = new PriorityQueue<>();       //小根堆,放大数big = new PriorityQueue<>(new Comparator<Integer>(){     //大根堆,放小数public int compare(Integer a, Integer b){return b - a;}   });}public void addNum(int num) {//刚开始放在小根堆里if(small.isEmpty() && big.isEmpty()){small.add(num);return; }//如果大于小根堆堆顶,说明是大数部分的,放在小根堆里面if(!small.isEmpty() && num > small.peek())small.offer(num);//如果小于大根堆堆顶,说明是小数部分的,放在大根堆里面else if(!big.isEmpty() && num < big.peek())big.offer(num);//介于中间的,也放在小根堆堆顶elsesmall.offer(num);//调整两个堆元素的数量while(small.size() - big.size() > 1){big.offer(small.poll());}while(big.size() - small.size() > 1){small.offer(big.poll());}}public double findMedian() {if(small.size() > big.size())return small.peek();else if(small.size() < big.size())return big.peek();elsereturn (double)((small.peek() + big.peek()) / 2.0);}
}

这样写代码有点复杂,不过感觉思路还是很清晰的。
然后去看了题解,其实直接根据两个堆的大小,直接进行两个堆的更新就可以了
首先规定,如果是奇数,那么小顶堆的个数要比大顶堆多1个
那么如果此时两个堆大小相同,就可以先将要加入的数num放在大顶堆中,然后把大顶堆堆顶的数弹出,放在小顶堆中
如果大小不同,由于小顶堆个数多一个,那么就放在小顶堆里,然后弹出一个放进大顶堆中

class MedianFinder {/** initialize your data structure here. */PriorityQueue<Integer> small;PriorityQueue<Integer> big;public MedianFinder() {small = new PriorityQueue<>();       //小根堆,放大数big = new PriorityQueue<>(new Comparator<Integer>(){     //大根堆,放小数public int compare(Integer a, Integer b){return b - a;}   });}public void addNum(int num) {if(small.size() == big.size()){big.offer(num);small.offer(big.poll());}else{small.offer(num);big.offer(small.poll());}}public double findMedian() {if(small.size() > big.size())return small.peek();elsereturn (small.peek() + big.peek()) / 2.0;}
}

LeetCode12. 整数转罗马数字 / 剑指 Offer 40. 最小的k个数 / 剑指 Offer 41. 数据流中的中位数相关推荐

  1. 【LeetCode】剑指 Offer 40. 最小的k个数

    [LeetCode]剑指 Offer 40. 最小的k个数 文章目录 [LeetCode]剑指 Offer 40. 最小的k个数 一.笨比解法 二.堆排序 三.快速选择 总结 一.笨比解法 选择排序变 ...

  2. 【力扣刷题】剑指 Offer 40. 最小的k个数(大顶堆)

    题目: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例: 输入:arr = [3,2,1], k = 2 ...

  3. 剑指 Offer 40. 最小的k个数

    题目 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 输入:arr = [3,2,1], k = 2 输 ...

  4. LeetCode_Heap_剑指 Offer 40. 最小的k个数 【堆,泛型实现,自定义比较器】【C++/java】【简单】

    目录 一,题目描述 英文描述 中文描述 示例与说明 二,解题思路 1,手动实现堆--C++泛型实现 2,手动实现堆--java泛型实现 3,快速使用堆--C++ 优先队列 pop_heap().pus ...

  5. Leetcode 剑指 Offer 40. 最小的k个数 (每日一题 20210825)

    输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4.示例 1:输入:arr = [3,2,1], k = 2 输出: ...

  6. 【算法】剑指 Offer 40. 最小的k个数 【重刷】

    1.概述 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k ...

  7. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素

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

  8. 剑指Offer 40—最小的k个数

    力扣 题意 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 法1-对数组进行排序 这个很容易想到,直接对原数组 ...

  9. 【Java】 剑指offer(40) 最小的k个数

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7 ...

  10. 最小的k个数 java_【Java】 剑指offer(40) 最小的k个数

    本文参考自<剑指offer>一书,代码采用Java语言. 题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 思 ...

最新文章

  1. python发送json数据_在Websockets上发送JSON
  2. 起点海外版 Hybrid App-内嵌页优化实践
  3. 手机突然电量消耗很快_手机掉电突然变快?这5点操作你肯定至少做了其中一个!...
  4. 一步一步写算法(之 回数)
  5. string中内容的访问
  6. 哪几种人会被房价拐点忽悠
  7. 乱谈B2C系统-算是今年的总结吧
  8. linux没有.brashrc文件,Linux 安装 Redis4.0.6
  9. java 十进制与二进制,八进制,十六进制之间的转换
  10. 谐振放大器工作点和阻抗
  11. 串联稳压电源与多谐振荡器
  12. 18 在springboot整合thymeleaf模板引擎中@Controller和@RestController不同注解的跳转页面方法
  13. 椭圆抛物面matlab程序,椭圆抛物面画法.pdf
  14. C# 图片位深度转至8位灰度图像,8位灰度图像转为1位灰度图像
  15. Android 鼠标样式修改
  16. 矩阵/向量/标量间相互求导
  17. java语言简介论文,基于Java的Web论坛开发.doc
  18. 进入Recovery模式方法
  19. 大数据告诉你梅西如何制霸足坛!
  20. 二本学计算机专业好吗,二本大学学计算机专业好的学政

热门文章

  1. 高维空间中点到超平面的距离
  2. install pecl php_pecl安装以前的php版本
  3. 2017《Java预备作业2》计科1502杨雪莹
  4. 白山搜索引擎优化收费_百度搜索引擎优化收费标准
  5. 蒙特卡洛方法求定积分
  6. android 4.1.2大小,4.1.2 Activity初窥门径
  7. 简单python3D绘图---Axes3D
  8. 使用python获取日历信息并制作订阅文件
  9. 不卷了!技术团队成员集体辞职
  10. 两篇关于区块链的最新综述论文被 IEEE ACCESS 接收