LeetCode 簡單難度_第一週總結

1.兩數之和

給定一個整數數組和一個目標值,找出數組中和爲目標值的兩個數
你可以假設每個輸入只對應一種答案,且同樣的元素不能被重複利用

示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解決方案

方法一:暴力法

private static int[] twoSum(int[] nums, int target) {
    if(nums == null || nums.length == 1) {
        return new int[]{0, 0};
    }
    for (int i = 0; i < nums.length - 1; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (target == nums[i] + nums[j]) {
                return new int[] {i, j};
            }
        }
    }
    throw new IllegalArgumentException("No two sum solution");
}

複雜度分析
時間複雜度:O(n^2)
空間複雜度:O(1)

方法二:哈希表
把數組中的元素作爲 key 插入到 hashmap 中,值存元素下標

private static int[] twoSum(int[] nums, int target) {
    if (nums == null || nums.length == 1) {
        return new int[]{0, 0};
    }
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] {map.get(complement), i};
        }
        map.put(nums[i], i);
    }
    throw new IllegalArgumentException("No two sum solution");
}

複雜度分析:
時間複雜度:O(n)
空間複雜度:O(n)

7.反轉整數

給定一個 32 位有符號整數,將整數中的數字進行反轉

示例 1:
輸入: 123
輸出: 321

示例 2:
輸入: -123
輸出: -321

示例 3:
輸入: 120
輸出: 21

注意:假設我們的環境只能存儲 32 位有符號整數,其數值範圍是 [−2^31, 2^31 − 1]。根據這個假設,如果反轉後的整數溢出,則返回 0

方法:每次取一個數進行保存,保存之前判斷是否有可能會溢出
溢出有四種情況:
a. 上次保存的數 > Integer.MAX_VALUE / 10,則肯定溢出
b. 上次保存的數 == Integer.MAX_VALUE / 10,且當前即將要保存的數大於 7,則會溢出
c. 上次保存的數 < Integer.MIN_VALUE / 10,則肯定溢出
d. 上次保存的數 == Integer.MIN_VALUE / 10,且當前即將要保存的數小於 -8,則會溢出

private static int reverse(int x) {
    intrev = 0;
    while (x != 0) {
        int pop = x % 10;
        x /= 10;
        if (rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
        if (rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
        rev = rev * 10 + pop;
    }
    return rev;
}

複雜度分析
時間複雜度:O(log(n))
空間複雜度:O(1)

9.迴文數

判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數

示例 1:
輸入: 121
輸出: true

示例 2:
輸入: -121
輸出: false
解釋: 從左向右讀, 爲 -121 。 從右向左讀, 爲 121- 。因此它不是一個迴文數

示例 3:
輸入: 10
輸出: false
解釋: 從右向左讀, 爲 01 。因此它不是一個迴文數

進階:
你能不將整數轉爲字符串來解決這個問題嗎?

方法:反轉後半部分,再和前半部分比較,相等則爲迴文數
怎麼確定一半?
將原始數字除以 10,然後給反轉後的數字乘上 10,所以,當原始數字小於反轉後的數字時,就意味着我們已經處理了一半位數的數字

private static boolean isPalindrome(int x) {
    if (x <0 || (x % 10 == 0 && x != 0)) {
        return false;
    }
    int revertedNumber = 0;
    while (x > revertedNumber) {
        revertedNumber = revertedNumber * 10 + x % 10;
        x /= 10;
    }
    return x == revertedNumber || x == revertedNumber / 10;
}

複雜度分析
時間複雜度:參考給的是 O(log(n))
空間複雜度:O(1)

13.羅馬數字轉整數

羅馬數字包含以下七種字符: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 的範圍內

自己寫的

private static int romanToInt(String s) {
    // 輸入的字符串爲 null 或長度爲 0
    if (s == null || s.length() == 0) {
        return 0;
    }
    String roman = "IVXLCDM";
    char[] cs = s.toCharArray();
    // 遍歷輸入的字符串
    for (int i = 0; i < cs.length; i++) {
        // 當前字符不在羅馬數字對應的字符範圍內
        if (!roman.contains(cs[i] + "")) {
            return 0;
        }
    }
    // 若連續輸入 4 個羅馬字符,拋異常,提示羅馬數字一般最多爲連續三個字符
    if (s.contains("IIII") || s.contains("VVVV") || s.contains("XXXX") || s.contains("LLLL") || s.contains("CCCC")
            || s.contains("DDDD") || s.contains("MMMM")) {
        throw new IllegalArgumentException("連續字母不能超過三個");
    }

    Map<Character, Integer> 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 len = s.length();
    // 初始化 sum 的值,賦值默認取最後一個羅馬字符的值
    int sum = map.get(s.charAt(len - 1));
    for (int i = 0; i < len - 1; i++) {
        if (map.get(s.charAt(i)) < map.get(s.charAt(i + 1))) {
            sum -= map.get(s.charAt(i));
        } else {
            sum += map.get(s.charAt(i));
        }
    }
    return sum;
}

複雜度分析
時間複雜度:O(n)
空間複雜度:O(n)

14.最長公共前綴

編寫一個函數來查找字符串數組中的最長公共前綴
如果不存在公共前綴,返回空字符串 “”

示例 1:
輸入: [“flower”,”flow”,”flight”]
輸出: “fl”

示例 2:
輸入: [“dog”,”racecar”,”car”]
輸出: “”

說明:
所有輸入只包含小寫字母 a-z

自己寫的

private static String longestCommonPrefix(String[] strs) {
    // 輸入的數組爲 null,或者數組所含元素個數爲 0,即 strs = new String[]{}
    if (strs == null) {
        // 直接返回空字符串
        return "";
    } else if (strs.length == 1) {// 輸入的數組所含元素個數爲 1
        // 直接返回該字符串
        return strs[0];
    }
    // 獲取輸入的數組所含元素個數
    int len = strs.length;
    // 初始化數組元素最短長度爲輸入的數組中第一個元素的長度
    int lessStrLen = strs[0].length();
    // 數組第一個元素長度爲 0
    if (lessStrLen == 0) {
        // 直接返回空字符串
        return "";
    }
    // 從數組下標爲 1 處遍歷數組
    for (int i = 1; i < len; i++) {
        // 獲取當前位置元素的長度
        int currStrLen = strs[i].length();
        // 元素長度爲 0
        if (currStrLen == 0) {
            // 返回空字符串
            return "";
        }
        // 如果當前元素長度小於記錄的最小元素長度
        if (currStrLen < lessStrLen) {
            // 更新最小元素長度爲當前元素長度
            lessStrLen = currStrLen;
        }
    }
    // 創建 map 對象
    Map<Character, Integer> map = new HashMap<>(lessStrLen);
    // 當前遍歷的次數
    int index = 0;
    // 每輪遍歷的個數
    int count = 0;
    // 當前遍歷的字符
    char c1 = 0;
    // 輪數,即最短元素長度
    for (int i = 0; i < lessStrLen; i++) {
        // 當前遍歷的次數
        index = i + 1;
        // 開始新的一輪,已遍歷的個數清零
        count = 0;
        // 每一輪的次數,即數組長度
        for (int j = 0; j < len; j++) {
            // 每次獲取數組第 j 個元素的第 i 個字符
            c1 = strs[j].charAt(i);
            // 從第二個元素開始,map 中若不存在 c1,表示數組下標爲 j 的元素,第 i 個字符在 map 中不存在
            if (j != 0 && map.get(c1) == null) {
                // 返回第 i 個字符之前的字符串
                return strs[0].substring(0, i);
            }
            // 當前第 i 輪遍歷的個數加一
            count += 1;
            // 把 c1 放到數組中,值隨意,判斷的時候關鍵是判斷 c1 這個 key 對應的 value 是否存在
            map.put(c1, j + 1);
        }
        // 當前第 i 輪遍歷結束,把 c1 置空,方便下一輪對字符進行存儲、判斷
        map.put(c1, null);
    }
    // 最後一輪遍歷的個數不等於數組元素的個數,即表示最後一輪沒有遍歷完就出現了不同的字符
    if (count != len) {
        // 最後一輪之前的輪數對應的字符串都是相同的,相同的輪數爲當前遍歷的次數 index - 1
        index--;
    }
    // 返回 index 之前的字符串
    return strs[0].substring(0, index);
}

複雜度分析
時間複雜度:O(n^2)
空間複雜度:O(n)

20.有效的括號

給定一個只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串,判斷字符串是否有效。

有效字符串需滿足:

左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。

示例 1:
輸入: “()”
輸出: true

示例 2:
輸入: “()[]{}”
輸出: true

示例 3:
輸入: “(]”
輸出: false

示例 4:
輸入: “([)]”
輸出: false

示例 5:
輸入: “{[]}”
輸出: true

自己寫的

private static boolean isValid(String s) {
    if (s == null) {
        return false;
    }
    if (s.length() == 0) {
        return true;
    }
    boolean flag = false;

    String[] strs = { "()", "{}", "[]" };
    while (s.length() != 0) {
        flag = false;
        for (int i = 0; i < strs.length; i++) {
            // 當前字符串包含有效括號
            if (s.contains(strs[i])) {
                flag = true;
                // 剔除當前字符串中的有效括號
                s = s.replace(strs[i], "");
                // 跳出循環
                break;
            }
            // 此時爲遍歷有效括號數組 strs 最後一個元素,即有效括號數組在遍歷完之前還沒有跳出循環
            // 則證明當前字符串中含有非有效括號
            if (i == strs.length - 1) {
                // 返回 falg,此時 flag 爲 false
                return flag;
            }
        }
    }
    // 全部爲有效括號,此時 flag 爲 true
    return flag;
}

複雜度分析
時間複雜度:
a. 最好情況,返回 true,即每次都找到有效括號,O(log2(n))
b. 最差情況,返回 false,數組遍歷完也沒找到有效括號,O(n)
空間複雜度:O(1)

点赞