[TOC]java
題目描述:github
給定兩個有序整數數組 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成爲一個有序數組。數組
輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
輸出: [1,2,2,3,5,6]
複製代碼
解題思路:bash
跟堆排序中的合併兩個有序數組的方法相似。函數
從尾部進行遍歷,比較兩個數組的最大值,放入到第一個數組中的末尾。ui
public void merge(int[] nums1, int m, int[] nums2, int n) {
if (nums1 == null || nums2 == null) {
return;
}
int p1 = m - 1;
int p2 = n - 1;
while (p1 >= 0 && p2 >= 0) {
nums1[p1 + p2 + 1] = nums1[p1] > nums2[p2] ? nums1[p1--] : nums2[p2--];
}
while (p2 >= 0) {
nums1[p2] = nums2[p2];
p2--;
}
}
複製代碼
27. 移除元素 簡單spa
題目描述: 給定一個數組 nums 和一個值 val,你須要原地移除全部數值等於 val 的元素,返回移除後數組的新長度。.net
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。指針
元素的順序能夠改變。你不須要考慮數組中超出新長度後面的元素。
給定 nums = [3,2,2,3], val = 3,
函數應該返回新的長度 2, 而且 nums 中的前兩個元素均爲 2。
你不須要考慮數組中超出新長度後面的元素。
複製代碼
解題思路: 使用雙指針, i 是慢指針, j 是快指針, 原地置換元素, 最後返回新數組的長度.(因爲 nums 數組是傳參引用, 因此在方法內部修改後, 外部調用時是修改後的數組內容)
public int removeElement(int[] nums, int val) {
int i = 0;
for (int j = 0; j < nums.length; j ++) {
if (nums[j] != val) {
nums[i++] = nums[j];
}
}
return i;
}
複製代碼
題目描述:給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
輸入: "abcabcbb"
輸出: 3
解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。
複製代碼
解答思路: 使用滑動窗口,雙指針進行左閉右開[i, j),每次遍歷刷新下標,保證set中不重複,取最長的ans,
代碼:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
// 有重複的狀況下,去掉最左邊
set.remove(s.charAt(i++));
}
}
return ans;
}
複製代碼
進階版
解答思路: 遇到重複的字符,將滑動窗口的左邊直接滑到第一個重複的字符下標。若是 s[j] 在 [i, j) 範圍內有與 j' 相同的字符, 能夠直接跳過 [i, j']範圍內的全部元素, 將 i 下標置爲 j' + 1. 使用 HashMap 來存儲字符對應的下標.
public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int length = s.length(), ans = 0;
for (int i = 0, j = 0; j < length; j++) {
char temp = s.charAt(j);
if (map.containsKey(temp)) {
i = Math.max(map.get(temp), i);
}
ans = Math.max(ans, j - i + 1);
map.put(temp, j + 1);
}
return ans;
}
複製代碼
題目描述: 給定一個字符串 s 和一些長度相同的單詞 words。找出 s 中剛好能夠由 words 中全部單詞串聯造成的子串的起始位置。
注意子串要與 words 中的單詞徹底匹配,中間不能有其餘字符,但不須要考慮 words 中單詞串聯的順序。
輸入:
s = "barfoothefoobarman",
words = ["foo","bar"]
輸出:[0,9]
解釋:
從索引 0 和 9 開始的子串分別是 "barfoor" 和 "foobar" 。
輸出的順序不重要, [9,0] 也是有效答案。
複製代碼
解題思路:
使用兩個 HashMap 保存出現的字符和次數
遍歷給定字符(直到總長度 totalLen減去單個待查找字符的長度wordLen), 在內部循環中, 以 wordLen 做爲長度切分給定的字符。若是連續子字符中包含查詢字符,繼續找;若是不包含查詢字符,調成內層循環,從下一個字符開始查詢。最後內層循環結束後,判斷查找次數是否一致,若是是,保存起始下標到結果集中。
代碼:
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
int wordSize = words.length;
if (wordSize == 0) {
return result;
}
// words 數組中每一個字符的長度都一致, 取第一個
int wordLength = words[0].length();
// 字符數組有可能有重複的, 使用 allWordMap 存放每一個 word 出現的次數
HashMap<String, Integer> allWordMap = new HashMap<>();
for (String word : words) {
allWordMap.put(word, allWordMap.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < s.length() - wordSize * wordLength + 1; i++) {
HashMap<String, Integer> hashMap = new HashMap<>();
// 單趟循環中, 成功匹配的次數
int num = 0;
while (num < wordSize) {
// 根據查找字符的長度進行截斷
String word = s.substring(i + num * wordLength, i + (num + 1) * wordLength);
if (allWordMap.containsKey(word)) {
hashMap.put(word, hashMap.getOrDefault(word, 0) + 1);
// 還有可能超過 words 中原來的數量
if (hashMap.get(word) > allWordMap.get(word)) {
break;
}
} else {
// 若是沒有查詢到, 跳過此次循環, 查找下一個字符
break;
}
num++;
}
if (num == wordSize) {
result.add(i);
}
}
return result;
}
複製代碼
題目描述:
給定一個鏈表,旋轉鏈表,將鏈表每一個節點向右移動 k 個位置,其中 k 是非負數。
輸入: 1->2->3->4->5->NULL, k = 2
輸出: 4->5->1->2->3->NULL
解釋:
向右旋轉 1 步: 5->1->2->3->4->NULL
向右旋轉 2 步: 4->5->1->2->3->NULL
複製代碼
解題思路
①:遍歷鏈表,得到鏈表長度和最後一個節點的地址。
②:計算獲得須要移動的次數(k % length)
③:移動,以示例做爲例子,k = 2,表示右移兩次,能夠認爲是作了如下三步操做:
代碼:
public ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
int length = 1;
ListNode tempNode = head;
ListNode endNode = null;
while (tempNode.next != null) {
length++;
tempNode = tempNode.next;
}
endNode = tempNode;
// 獲得須要移動的次數
int moveNum = k % length;
if (moveNum == 0) {
return head;
}
// 找到待替換的下標
int findIndex = 1, index = length - moveNum + 1;
tempNode = head;
while (tempNode.next != null) {
findIndex++;
if (findIndex == index) {
endNode.next = head;
head = tempNode.next;
tempNode.next = null;
break;
}
tempNode = tempNode.next;
}
return head;
}
複製代碼
題目描述:(原題描述不完整,還有不少隱含條件,在評論區找出來的)
給定一個字符串 S 和一個字符串 T,請在 S 中找出包含 T 全部字母的最小子串。
示例:
輸入: S = "ADOBECODEBANC", T = "ABC"
輸出: "BANC"
說明:
若是 S 中不存這樣的子串,則返回空字符串 ""。
若是 S 中存在這樣的子串,咱們保證它是惟一的答案。
複製代碼
隱含條件:
①
輸入:
s='a'
t='aa'
輸出:
''
②
參數的全集是大寫字母和小寫字母
複製代碼
解題思路,參考這篇文章
1. 注意到題目的關鍵:"全部字母的最小子串",也就是說兩個串都只能是字母。
2. 因而,能夠開闢一個大小爲64的數組,來存放數組中字母的頻率(Frequency)。準確的說,
經過字母的ASCII碼做爲數組的索引,開闢空間的大小爲26+6+26=58:26個大寫字母,26個小寫字母,
還有中間的6個非字母 A~Z[65~90] 非字母[91~96] a~z[97~122]
3. 滑動窗口的使用:分三種狀況來移動窗口:(這裏令當前窗口的左右邊界分別爲l,r,窗口的大小爲winSize=r-l+1)
1) 當winSize < t.size() r++; 也就是窗口右邊界向右移動
2) 當winSize == t.size() :
2.1) 當窗口中的字符已經符合要求了,直接返回return,已經找到了
2.2) 不然r++,窗口右邊界向右移動
3) 當winSize > t.size()
2.1) 當窗口中的字符已經符合要求了,l++,窗口左邊界向右移動
2.2) 不然r++,窗口右邊界向右移動
4. 上面是滑動窗口的使用思路,具體實現上有必定的不一樣,下面是須要考慮到的要點:
1) 啥叫做窗口中的字符已經符合要求了?
1) 窗口滑動時的操做是關鍵
2) 要考慮到數組越界的問題
複製代碼
代碼實現(解答答案中耗時最少的回答):
public String minWindow(String s, String t) {
int lenS = s.length(), lenT = t.length();
if(lenS < lenT){
return "";
}
int[] tCount = new int[256];
for(int i=0;i<lenT;i++){
tCount[t.charAt(i)] ++;
}
int[] sCount = new int[256];
int left = 0, right = 0;
//保存窗口中等於字符串t的字符的數量,當count等於lenT時,說明找到一個子串
int count = 0;
int minLen = lenS + 1, start = -1;
while(left < lenS){
if(right < lenS && count < lenT){
//right右滑一格
char charRight = s.charAt(right);
sCount[charRight] ++;
if(sCount[charRight] <= tCount[charRight]){
count ++;
}
right ++;
}else{
char charLeft = s.charAt(left);
//更新最小字串的長度和起始索引
if(count == lenT && right - left < minLen){
minLen = right - left;
start = left;
}
//left右滑一格
sCount[charLeft] --;
// 這裏只會在 sCount 中,被去掉要查詢的字符,纔會減小窗口中的總數;
// 若是被去掉是其它無關字符,只須要 left 移動,總量 count 不須要改變
if(sCount[charLeft] < tCount[charLeft]){
count --;
}
left ++;
}
}
if(start != -1){
return s.substring(start, start + minLen);
}
return "";
}
複製代碼
題目描述: 給定一個整數數組和一個整數 k, 你須要在數組裏找到不一樣的 k-diff 數對。這裏將 k-diff 數對定義爲一個整數對 (i, j), 其中 i 和 j 都是數組中的數字,且兩數之差的絕對值是 k.
示例 1:
輸入: [3, 1, 4, 1, 5], k = 2
輸出: 2
解釋: 數組中有兩個 2-diff 數對, (1, 3) 和 (3, 5)。
儘管數組中有兩個1,但咱們只應返回不一樣的數對的數量。
複製代碼
解題思路:
使用 map 保存給定的數組,每一個數字出現的次數。根據 k 值判斷兩個數字間的差值是否符合條件。
代碼:
public int findPairs(int[] nums, int k) {
int ans = 0;
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
if (k == 0) {
for (int key : map.keySet()) {
if (map.get(key) > 1) {
ans++;
}
}
} else if (k > 0) {
for (int key : map.keySet()) {
if (map.containsKey(key + k)) {
ans++;
}
}
} else {
for (int key : map.keySet()) {
if (key >= 0) {
continue;
}
if (map.containsKey(key - k)) {
ans++;
}
}
}
return ans;
}
複製代碼
進階版
將數組排好序以後進行比較:
public int findPairs(int[] nums, int k) {
if (k < 0) {
return 0;
}
Arrays.sort(nums);
int length = nums.length;
int ans = 0, i = 0, j = 1;
if (k == 0) {
while (j < length) {
if (nums[j] == nums[i]) {
ans++;
}
int a = i++;
while (nums[i] == nums[a]) {
i++;
if (i == length) {
return ans;
}
}
j = i + 1;
}
} else {
while (j < length) {
while (nums[j] - nums[i] < k) {
j++;
if (j == length) {
return ans;
}
}
if (nums[j] - nums[i] == k) {
ans++;
}
int a = i++;
while (nums[i] == nums[a]) {
i++;
}
}
}
return ans;
}
複製代碼
題目描述:
給定兩個字符串 s1 和 s2,寫一個函數來判斷 s2 是否包含 s1 的排列。
換句話說,第一個字符串的排列之一是第二個字符串的子串。
輸入: s1 = "ab" s2 = "eidbaooo"
輸出: True
解釋: s2 包含 s1 的排列之一 ("ba").
注意:
輸入的字符串只包含小寫字母
兩個字符串的長度都在 [1, 10,000] 之間
複製代碼
解題思路:
因爲只包含小寫字母,可使用數組保存待匹配的字符出現的次數。而後經過兩個指針的移動,匹配到 s1 長度的 s2 子串。
代碼:
public boolean checkInclusion(String s1, String s2) {
int[] count = new int[26];
char[] pattern = s1.toCharArray();
char[] str = s2.toCharArray();
for (int i = 0; i < pattern.length; i++) {
count[pattern[i] - 'a']++;
}
int left = 0, right = 0;
while (right < str.length){
if (count[str[right] - 'a'] != 0) {
count[str[right] - 'a']--;
right++;
if (right - left == pattern.length) {
return true;
}
} else if (left == right) {
left++;
right++;
} else {
count[str[left] - 'a']++;
left++;
}
}
return false;
}
複製代碼
但願各位幫忙點個star,給我加個小星星✨