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)