【LeetCode-面试经典150题-day5】

380.O(1)时间插入、删除和获取随机元素

题意:

实现RandomizedSet 类:

  • RandomizedSet() 初始化 RandomizedSet 对象
  • bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
  • bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
  • int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。

你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。

【输入样例】

["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]

【输出样例】

[null, true, false, true, 2, true, false, 2]

解题思路:

数组+哈希表

1.随机获取一个元素平均时间复杂度为O(1),最好的就是数组;

2.插入和删除的平均时间复杂度为O(1),比较好的就是哈希表;

3.两者结合,因为哈希表中无法再O(1)时间内完成获取随机元素操作,所以数组中存储元素,哈希表中存储每个元素在数组中的下标;

4.因为元素个数随着样例不同而不同,所以使用变长数组。

5.因为元素val是唯一存储的是,所以哈希表中的key是val。

class RandomizedSet {
    List nums;
    Map indices;
    Random random;
    public RandomizedSet() {
        nums = new ArrayList();
        indices = new HashMap();
        random = new Random();
    }
    
    public boolean insert(int val) {
        if(indices.containsKey(val)){
            return false;
        }
        int index = nums.size();
        nums.add(val);
        indices.put(val,index);
        return true;
    }
    
    public boolean remove(int val) {
        if(!indices.containsKey(val)){
            return false;
        }
        int index = indices.get(val);//获取值val在数组中的下标
        int last = nums.get(nums.size()-1);//获取数组最后一位的值
        nums.set(index,last);//nums数组中index位置,值从val变成last
        indices.put(last,index);//哈希表中,将指定的值index与此键last关联
        nums.remove(nums.size()-1);//删掉最后一位
        indices.remove(val);
        return true;
    }
    
    public int getRandom() {
        int randomIndex = random.nextInt(nums.size());
        return nums.get(randomIndex);
    }
}

时间: 击败了85.78%

内存: 击败了39.05%

 134.加油站

题意:

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

【输入样例】

["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]

【输出样例】

[null, true, false, true, 2, true, false, 2]

解题思路:

1.定义两个指针,start指针指向从哪一加油站开始,count指针用于遍历数组

2. 定义变量resOil表示剩余的油量,通过判断来决定能否继续往下走

3.思路简单清晰,但是超时了哈哈哈哈哈

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int start = 0;
        int resOil = 0;
        int len = gas.length;
        int count = 0;
        while(start < len){//从哪个加油站开始
            if((resOil += gas[count]) >= cost[count]){
                resOil -= cost[count];
                ++count;
                count %= len;
                if(count%len == start){
                    return start;
                }
            }else{
                ++start;
                count = start;
                resOil = 0;
            }
        }
        return -1;
    }
}

 勉强修改了一下代码,终于能通过了

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int start = 0;
        int len = gas.length;
       while(start < len){//从哪个加油站开始
            int resOil = 0;
            int count = 0;//总共走了几个站点
            while(count < len){
                int j = (start + count) % len;//环形加油站
                resOil += gas[j];
                resOil -= cost[j];
                if(resOil<0){
                    //哦偶,油不够
                    break;
                }
                count++;//这个站点ok ok
            }
            if(count == len){
                return start;
            }else{
                start = start + count + 1;//从不满足的站点开始检查
            }
        }
        return -1;
    }
}

时间: 击败了39.88%

内存: 击败了38.13%

 13.罗马数字转整型

题意:

罗马数字包含以下七种字符: I, V, X, LCD 和 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

解题思路:

1用map实现了

class Solution {
    public int romanToInt(String s) {
        Map map = new HashMap<>();
        map.put('I',1);
        map.put('V',5);
        map.put('X',10);
        map.put('L',50);
        map.put('C',100);
        map.put('D',500);
        map.put('M',1000);
        //小的数字在大的数字右边,直接加,如果小的数字在大的数字后面,需要减
        int count = 0;
        for(int i =0;i

时间: 击败了58.46%

内存: 击败了26.48%

 12.整数转罗马数字

题意:

与上面一题一样,但是是将整数转为罗马数字。

【输入样例】3

【输出样例】"III"

解题思路:

暴力匹配

class Solution {
    public String intToRoman(int num) {
        StringBuffer s = new StringBuffer();
        int[] listNum = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
        String[] listString = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        int i = 0;
        while(num > 0){
            while(num - listNum[i] >=0){
                s.append(listString[i]);
                num -= listNum[i];
            }
            ++i;
        }
        return s.toString();
    }
}

 时间: 击败了97.00%

内存: 击败了45.15%

58. 最后一个单词的长度

题意:

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

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

【输入样例】“hello world’

【输出样例】5

解题思路:

不用多说吧,从后开始遍历,找到第一个不是空格的字符,统计其到下一个空格的长度,因为题目说是单词组成,所以不用额外判断是否是字母。

class Solution {
    public int lengthOfLastWord(String s) {
        int total=0;
        int i=s.length()-1;
        while(s.charAt(i)==' '){
            i--;
        }
        while(i >= 0 && s.charAt(i)!=' '){
            total++;
            i--;
        }
        return total;
    }
}

 时间: 击败了100.00%

内存: 击败了39.8%

bye~~ 吃饭去了

 

你可能感兴趣的:(LeetCode,算法,leetcode,面试)