目錄
1. 兩數之和
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1]
思路:將每個元素對應target的差值存入map中,若當前元素存在map中,將找到結果
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; ++i){
int num = target - nums[i];
if(map.containsKey(num)){
return new int[] {map.get(num), i};
}
map.put(nums[i], i);
}
return null;
}
}
2. 兩數相加
給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。
如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。
示例:
輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
原因:342 + 465 = 807
思路:根據加法的步驟,記錄進位標誌,浪費一個節點來提高代碼可讀性也未嘗不可
加完後還有最後的進位也需要處理(carryFlag != 0)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int carryFlag = 0;
ListNode result = new ListNode(0);
ListNode curr = result;
while (l1 != null || l2 != null || carryFlag != 0) {
int x = (l1 != null) ? l1.val : 0;
int y = (l2 != null) ? l2.val : 0;
int tmp = x + y + carryFlag;
curr.next = new ListNode(tmp % 10);
curr = curr.next;
carryFlag = tmp / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
return result.next;
}
}
3. 無重複字符的最長字串
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因爲無重複字符的最長子串是 "abc",所以其長度爲 3。
思路:滑動窗口,i是窗口左邊界,j是窗口右邊界,將字符一個一個放入hashMap,如果當前字符在map中,則更新i爲當前字符下標+1
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> map = new HashMap();
int res = 0;
for (int i = 0, j = 0; j < s.length(); ++j) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)) + 1, i);
}
res = Math.max(res, j - i + 1);
map.put(s.charAt(j), j);
}
return res;
}
}
4. 尋找兩個數組的中位數
給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。
請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時爲空。
示例 1:nums1 = [1, 3],nums2 = [2],則中位數是 2.0
示例 2:nums1 = [1, 2],nums2 = [3, 4],則中位數是 (2 + 3)/2 = 2.5
思路:先排序然後取中位數,排序類似合併兩個有序鏈表(自己的初始想法,以下是初始代碼)
但時間複雜度是爲O(m+n),空間複雜度爲O(m+n)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//先排序然後取中位數,排序類似合併兩個有序鏈表
int length1 = nums1.length;
int length2 = nums2.length;
int[] resArr = new int[length1 + length2];
int i = 0;
int j = 0;
int n = 0;
while (i < length1 || j < length2) {
if (i >= length1) {
resArr[n] = nums2[j++];
} else if (j >= length2) {
resArr[n] = nums1[i++];
} else if (nums1[i] > nums2[j]) {
resArr[n] = nums2[j++];
} else {
resArr[n] = nums1[i++];
}
++n;
}
int resLen = resArr.length;
return resLen % 2 == 0 ? (double) (resArr[resLen / 2] + resArr[resLen / 2 - 1]) / 2 : resArr[resLen / 2];
}
}
算法的時間複雜度爲 O(log(min(m + n)))的解法
思路:首先,中位數:將一個集合劃分爲兩個長度相等的子集,其中一個子集中的元素總是大於另一個子集中的元素。
將A,B兩個數組分別切割,切割點分別爲i、j,分爲左AB和右AB,保證切割點滿足A[i-1] <= B[j] && B[j-1] <= A[i],從而保證左AB均小於等於右AB,並且,len(左AB)=len(總AB)-len(右AB),從而i+j = m-i + n-j,此時,
當A、B兩個數組的總長度爲偶數時,中位數=(max(A[i-1], B[j-1]) + min(A[i] + B[j]))/2,左AB的最大值和右AB的最小值的均值;
當A、B兩個數組的總長度爲奇數時,中位數=max(A[i-1], B[j-1]),左AB的最大值
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) {
// 保證 m <= n
return findMedianSortedArrays(B,A);
}
int iMin = 0;
int iMax = m;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
if (j != 0 && i != m && B[j - 1] > A[i]) {
iMin = i + 1;
} else if (i != 0 && j != n && A[i - 1] > B[j]) {
iMax = i - 1;
} else {
//滿足以上兩個切割點條件
int maxLeft = 0;
if (i == 0) {
maxLeft = B[j - 1];
} else if (j == 0) {
maxLeft = A[i - 1];
} else {
maxLeft = Math.max(B[j - 1], A[i - 1]);
}
//奇數直接返回
if ((m + n) % 2 == 1) {
return maxLeft;
}
int minRight = 0;
if (i == m) {
minRight = B[j];
} else if (j == n) {
minRight = A[i];
} else {
minRight = Math.min(B[j], A[i]);
}
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
Q1:爲什麼m <= n?
由於 0 <= i <= m 且 j = (m+n+1)/2−i,必須確保 j 不是負數。如果 n < m,那麼 j 將可能是負數,而這會造成錯誤的答案。
Q2:j = (m+n+1)/2−i,爲什麼要+1?
因爲當 A 數組和 B 數組的總長度是奇數時,要保證左半部分的長度比右半部分大1,這樣中位數纔是左AB的最大值
所以i + j = m - i + n - j + 1也就是 j = ( m + n + 1) / 2 - i
5. Z字形變換
將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。比如輸入字符串爲 "LEETCODEISHIRING" 行數爲 3 時,排列如下:
L C I R
E T O E S I I G
E D H N
之後,你的輸出需要從左往右逐行讀取,產生出一個新的字符串,比如:"LCIRETOESIIGEDHN"。請你實現這個將字符串進行指定行數變換的函數:string convert(string s, int numRows);
示例 1:輸入: s = "LEETCODEISHIRING", numRows = 3,輸出: "LCIRETOESIIGEDHN"
示例 2:輸入: s = "LEETCODEISHIRING", numRows = 4,輸出: "LDREOEIIECIHNTSG"
L D R
E O E I I
E C I H N
T S G
思路:1.每一行都創建一個StringBuilder;2.遍歷字符串,將每個字符放到對應行,遇到第一行和最後一行,行走的方向變化;3.將每一行的StringBuilder拼接
class Solution {
public String convert(String s, int numRows) {
if (s.length() < numRows || numRows == 1) {
return s;
}
//每一行都創建一個StringBuilder
List<StringBuilder> rowlist = new ArrayList<>(numRows);
for (int i = 0; i < numRows; ++i) {
rowlist.add(new StringBuilder());
}
//遍歷字符串,將每個字符放到對應行
int curRow = 0;
boolean growRow = false;
for (char ch : s.toCharArray()) {
rowlist.get(curRow).append(ch);
//方向變化
if (curRow == 0 || curRow == numRows - 1) {
growRow = !growRow;
}
curRow += growRow ? 1 : -1;
}
//將每一行的StringBuilder拼接
StringBuilder res = new StringBuilder();
for (StringBuilder sb : rowlist) {
res.append(sb);
}
return res.toString();
}
}
6. 整數反轉
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
示例 1:輸入: 123輸出: 321
示例 2:輸入: -123輸出: -321
示例 3:輸入: 120輸出: 21
注意:假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍爲 [−231, 231 − 1]。請根據這個假設,如果反轉後整數溢出那麼就返回 0。
思路:通過取餘(%)獲取每位數,通過res = res * 10 + pop一位位遞進,中間需判斷是否有溢出情況
class Solution {
public int reverse(int x) {
int res = 0;
while (x != 0) {
//每位數
int pop = x % 10;
//Integer範圍:-2147483648~2147483647
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && pop > 7)) {
return 0;
}
if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && pop < -8)) {
return 0;
}
res = res * 10 + pop;
x /= 10;
}
return res;
}
}
7. 字符串轉換整數(atoi)
首先,該函數會根據需要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符爲止。當我們尋找到的第一個非空字符爲正或者負號時,則將該符號與之後面儘可能多的連續數字組合起來,作爲該整數的正負號;假如第一個非空字符是數字,則直接將其與之後連續的數字字符組合起來,形成整數。該字符串除了有效的整數部分之後也可能會存在多餘的字符,這些字符可以被忽略,它們對於函數不應該造成影響。注意:假如該字符串中的第一個非空格字符不是一個有效整數字符、字符串爲空或字符串僅包含空白字符時,則你的函數不需要進行轉換。在任何情況下,若函數不能進行有效的轉換時,請返回 0。
說明:假設我們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍爲 [−231, 231 − 1]。如果數值超過這個範圍,請返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:輸入: "42"輸出: 42
示例 2:輸入: " -42"輸出: -42,解釋: 第一個非空白字符爲 '-', 它是一個負號。我們儘可能將負號與後面所有連續出現的數字組合起來,最後得到 -42 。
示例 3:輸入: "4193 with words"輸出: 4193,解釋: 轉換截止於數字 '3' ,因爲它的下一個字符不爲數字。
示例 4:輸入: "words and 987"輸出: 0,解釋: 第一個非空字符是 'w', 但它不是數字或正、負號。
因此無法執行有效的轉換。
示例 5:輸入: "-91283472332"輸出: -2147483648解釋: 數字 "-91283472332" 超過 32 位有符號整數範圍,因此返回 INT_MIN (−231) 。
思路:1. 去掉字符串前後空格;2. 處理第一個字符爲正負號的情況;3. 一個一個取數判斷通過res * 10 + tmp湊數;4. 判斷是否溢出同7整數反轉(初始想法)
class Solution {
public int myAtoi(String str) {
str = str.trim();
int res = 0;
int i = 0;
boolean negativeFlag = false;
if (str.startsWith("-")) {
i++;
negativeFlag = true;
}
if (str.startsWith("+")) {
i++;
}
char[] strArray = str.toCharArray();
for (; i < strArray.length; ++i) {
if (strArray[i] < '0' || strArray[i] > '9') {
return negativeFlag ? -res : res;
}
int tmp = strArray[i] - '0';
if (!negativeFlag && (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && tmp > 7))) {
return Integer.MAX_VALUE;
}
if (negativeFlag && (-res < Integer.MIN_VALUE / 10 || (-res == Integer.MIN_VALUE / 10 && tmp > 8))) {
return Integer.MIN_VALUE;
}
res = res * 10 + tmp;
}
return negativeFlag ? -res : res;
}
}