1.兩數之和(簡單題)
題目描述:
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
代碼:
class Solution {
public static int[] twoSum(int[] nums, int target) {
Map<Integer , Integer> map = new HashMap<>();
for(int i = 0 ; i < nums.length ;i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int j = target - nums[i];
if(map.containsKey(j) && map.get(j) != i) {
return new int[] {i, map.get(j)};
}
}
return new int[2];
}
}
方法解析:
爲了對運行時間複雜度進行優化,我們需要一種更有效的方法來檢查數組中是否存在目標元素。如果存在,我們需要找出它的索引。保持數組中的每個元素與其索引相互對應的最好方法是什麼?哈希表。
通過以空間換取速度的方式,我們可以將查找時間從 O(n)O(n) 降低到 O(1)O(1)。哈希表正是爲此目的而構建的,它支持以 近似 恆定的時間進行快速查找。我用“近似”來描述,是因爲一旦出現衝突,查找用時可能會退化到 O(n)O(n)。但只要你仔細地挑選哈希函數,在哈希表中進行查找的用時應當被攤銷爲 O(1)O(1)。
一個簡單的實現使用了兩次迭代。在第一次迭代中,我們將每個元素的值和它的索引添加到表中。然後,在第二次迭代中,我們將檢查每個元素所對應的目標元素(target - nums[i]target−nums[i])是否存在於表中。注意,該目標元素不能是 nums[i]nums[i] 本身!
優化:其實這個方法可以更加優化,我們可以一次完成。在進行迭代並將元素插入到表中的同時,我們還會回過頭來檢查表中是否已經存在當前元素所對應的目標元素。如果它存在,那我們已經找到了對應解,並立即將其返回。也就是邊加入map元素邊查詢。
- 兩數相加(中等題)
題目描述:
給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
示例:
輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
原因:342 + 465 = 807
代碼:
/**
* 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) {
ListNode d = new ListNode(0);
ListNode p = l1, q = l2, c = d;
int sum = 0;
while(p != null || q != null){
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
sum = sum + x + y;
c.next = new ListNode(sum%10);
sum = sum/10;
c = c.next;
if(p != null){
p = p.next;
}
if(q != null){
q = q.next;
}
}
if(sum > 0)
c.next = new ListNode(sum);
return d.next;
}
}
方法解析:
首先從最低有效位也就是列表 l1和 l2的表頭開始相加。由於每位數字都應當處於 0~9 的範圍內,我們計算兩個數字的和時可能會出現 “溢出”。例如,5 + 7 =12。在這種情況下,我們會將當前位的數值設置爲 2,並將進位 sum = 1 帶入下一次迭代。進位 sum 必定是 0或1,這是因爲兩個數字相加(考慮到進位)可能出現的最大和爲 9 + 9 + 1 = 19。
3.無重複字符的最長子串(中等題)
代碼
public class Solution {
public int lengthOfLongestSubstring(String s) {
char[] a = s.toCharArray();
int sum = 0;
for(int i = 0; i < a.length ; i++){
ArrayList<Character> b = new ArrayList<>();
b.add(a[i]);
//當有元素時,將sum值設爲1
sum = sum < b.size() ? b.size() :sum;
for(int j = i + 1; j < a.length; j++){
if(b.contains(a[j]) ){
sum = sum < b.size() ? b.size() :sum;
b.clear();
break;
}
else{
b.add(a[j]);
sum = sum < b.size() ? b.size() :sum;
}
}
}
return sum;
}
}
方法解析:
我的這種方法屬於暴力法,檢查每一個子字符串,將不含重複的字母的字符串經過比較長度大小,篩選出最長的記錄在sum中
優化:在暴力法中,反覆檢查一個子字符串是否含有有重複的字符,但這是沒有必要的。這道題主要用到思路是:滑動窗口
什麼是滑動窗口?
其實就是一個隊列,比如例題中的 abcabcbb,進入這個隊列(窗口)爲 abc 滿足題目要求,當再進入 a,隊列變成了 abca,這時候不滿足要求。所以,我們要移
動這個隊列!
如何移動?
我們只要把隊列的左邊的元素移出就行了,直到滿足題目要求!
一直維持這樣的隊列,找出隊列出現最長的長度時候,求出解!
代碼:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
代碼二:
以前的我們都沒有對字符串 s 所使用的字符集進行假設。=
當我們知道該字符集比較小的時侯,我們可以用一個整數數組作爲直接訪問表來替換 Map。
常用的表如下所示:
int [26] 用於字母 ‘a’ - ‘z’ 或 ‘A’ - ‘Z’
int [128] 用於ASCII碼
int [256] 用於擴展ASCII碼
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}
}
4.最長迴文子串(中等題)
題目描述:
給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。
示例 1:
輸入: “babad”
輸出: “bab”
注意: “aba” 也是一個有效答案。
示例 2:
輸入: “cbbd”
輸出: “bb”
方法代碼:
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0 , end = 0;
for(int i = 0; i < s.length() ;i++){
int len1 = around(s,i,i);
int len2 = around(s,i,i+1);
int len = Math.max(len1, len2);
if(len > end - start +1){
start = i - (len-1)/2;
end = i + len/2;
}
}
return s.substring(start , end + 1);
}
public static int around(String s ,int left ,int right){
int i = left, j = right ;
while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
i--;
j++;
}
return j - i -1;
}
}
方法解析:
事實上,只需使用恆定的空間,我們就可以決這個問題。
我們觀察到迴文中心的兩側互爲鏡像。因此,迴文可以從它的中心展開,並且只有 2n - 1個這樣的中心。
你可能會問,爲什麼會是 2n - 12n−1 個,而不是 nn 箇中心?原因在於所含字母數爲偶數的迴文的中心可以處於兩字母之間(例如 “abba” 的中心在兩個‘b’ 之間)。
- 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
方法一代碼:
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1) return s;
List<StringBuilder> rows = new ArrayList<>();
for (int i = 0; i < Math.min(numRows, s.length()); i++)
rows.add(new StringBuilder());
int curRow = 0;
boolean goingDown = false;
for (char c : s.toCharArray()) {
rows.get(curRow).append(c);
if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
curRow += goingDown ? 1 : -1;
}
StringBuilder ret = new StringBuilder();
for (StringBuilder row : rows) ret.append(row);
return ret.toString();
}
}
方法一解析:
這種方法是將字符串按各個字符的順序儲存到新字符串的相應的位置,先創建StringBuilder的集合,一共有numRows組,每一組代表每一行,然後按各個字符的順序儲存到新字符串的相應的行的StringBuilder中,最後再將各個行的字符串拼接起來。
注:
不要用String進行拼接,因爲如果輸入的規模大,會造成超時,不通過。
方法二代碼:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
string ret;
int n = s.size();
int cycleLen = 2 * numRows - 2;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j + i < n; j += cycleLen) {
ret += s[j + i];
if (i != 0 && i != numRows - 1 && j + cycleLen - i < n)
ret += s[j + cycleLen - i];
}
}
return ret;
}
};
方法二解析:
這種方法是按照逐行訪問的方式進行儲存的,將重新分好的字符按照行進行逐個儲存,
首先訪問行0中的所有字符,接着訪問行1,然後行2,依在這裏插入代碼片此類推…
- 整數反轉(簡單題)
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
示例 1:
輸入: 123
輸出: 321
示例 2:
輸入: -123
輸出: -321
示例 3:
輸入: 120
輸出: 21
方法代碼:
class Solution {
public int reverse(int x) {
long result = 0L;
int yu = 0;
for(int i = 0; x != 0; i++) {
yu = x % 10;
x /= 10;
result = result*10 + yu;
}
if(result >= -2147483648 &&result <= 2147483647)
return (int)result;
else
return 0;
}
}
這個方法沒什麼好說的,比較好理解,唯一要注意的是,要注意當反轉過來的數組超過int類型的最大範圍時的處理。