日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LeetCode整理----合集一

發(fā)布時(shí)間:2023/12/29 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LeetCode整理----合集一 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一:LeetCode[2]? 兩個(gè)數(shù)相加

? ? ? ? ? 1:需要考慮進(jìn)位的問題,需要使用一個(gè)變量存儲(chǔ)進(jìn)位標(biāo)識(shí),每一次都去判斷,而且在一個(gè)鏈表判斷完成之后,另外一個(gè)鏈表,也是需要單獨(dú)考慮進(jìn)位問題的

? ? ? ? ? 2:在計(jì)算結(jié)束后,需要再次判斷進(jìn)位問題,如果有進(jìn)位,則需要進(jìn)行處理。

? ? ? ? ? 3:需要維護(hù)返回的指針和移動(dòng)的當(dāng)前指針,返回的指針確定之后就不再變化,而移動(dòng)的當(dāng)前指針會(huì)隨著操作而變動(dòng),最好每次判斷返回指針是否為null,這樣防止空指針異常。

class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//進(jìn)位標(biāo)識(shí)int flag = 0;ListNode res = null;ListNode next = null;int val1 = 0;int val2 = 0;while(l1 != null || l2 != null){val1 = l1 == null ? 0 : l1.val;val2 = l2 == null ? 0 : l2.val;int i = val1 + val2 + flag;if(l1 != null){l1 = l1.next;}if(l2 != null){l2 = l2.next;}if(i > 9){flag = i/10;i = i%10;}else {flag = 0;}if(res == null){res = new ListNode(i);next = res;}else {next.next = new ListNode(i);next = next.next;}}if(flag == 1){next.next = new ListNode(1);}return res;} }

二:LeetCode[19]? ?刪除鏈表的倒數(shù)第 n 個(gè)節(jié)點(diǎn)

? ? ? ? 1.使用雙指針方進(jìn)行解決,先將一個(gè)指針向前移動(dòng)n位,此時(shí)再另兩個(gè)指針同時(shí)移動(dòng),當(dāng)快的那個(gè)指針移動(dòng)到尾節(jié)點(diǎn),那么慢的指針會(huì)移動(dòng)到倒數(shù)第n個(gè)節(jié)點(diǎn),此時(shí)再進(jìn)行刪除

? ? ? ? 2.編程時(shí),需要注意的是,當(dāng)快指針移動(dòng)到尾部,慢指針?biāo)诘奈恢?#xff0c;要保證慢指針再倒數(shù)第n + 1個(gè),這樣才能使用slow.next ?= ?slow.next.next,這一點(diǎn)要特別注意。

class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode fast = head;ListNode slow = head;for (int i = 0; i < n; i++) {fast = fast.next;}if (fast == null){return slow.next;}while(fast.next != null){fast = fast.next;slow = slow.next;}slow.next = slow.next.next;return head;} }

三:LeetCode[21]?合并兩個(gè)有序鏈表

? ? ? ? ? ?1.需要維護(hù)返回的指針和移動(dòng)的當(dāng)前指針,返回的指針確定之后就不再變化,而移動(dòng)的當(dāng)前指針會(huì)隨著操作而變動(dòng),最好每次判斷返回指針是否為null,這樣防止空指針異常。

? ? ? ? ? ?2.在新鏈表的下一個(gè)節(jié)點(diǎn)確定后,他的下下一個(gè)節(jié)點(diǎn)要置為null

? ? ? ? ? ?2.清楚指針的維護(hù)關(guān)系

class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {if(l1 == null) return l2;if(l2 == null) return l1;ListNode res = null;ListNode current = null;while(l1 != null && l2 != null){if(l1.val > l2.val){if(res == null){res = l2;current = res;}else{current.next = l2;current = current.next;}l2 = l2.next;current.next = null;}else {if(res == null){res = l1;current = res;}else{current.next = l1;current = current.next;}l1 = l1.next;current.next = null;}}if(l1 != null) current.next = l1;if(l2 != null) current.next = l2;return res;} }

?

四:LeetCode[206]? 反轉(zhuǎn)鏈表

? ? ? ? ? 1.需要使用3個(gè)指針進(jìn)行維護(hù),因?yàn)樵诜崔D(zhuǎn)的過程中,是有兩條鏈的

class Solution {public ListNode reverseList(ListNode head) {if(head == null || head.next == null) return head;ListNode current = head;ListNode next = null;ListNode before = null;while(current != null){next = current.next;current.next = before;before = current;current = next;}return before;} }

?

五:LeetCode[24]? 兩兩交換鏈表中的節(jié)點(diǎn)

? ? ? ? ? ? ? ?1.需要維護(hù)返回的節(jié)點(diǎn),每次判斷返回的節(jié)點(diǎn)是否為空,為空則進(jìn)行賦值

? ? ? ? ? ? ? ?2.兩兩交換,這樣的話,指針每次會(huì)移動(dòng)兩位,所以要記錄?next?和?third指針

? ? ? ? ? ? ? ?3.維護(hù)一個(gè)current節(jié)點(diǎn)的前指針,因?yàn)樵谛枰诮粨Q之后,將前指針的下一個(gè)指向進(jìn)行修改

class Solution {public ListNode swapPairs(ListNode head) {if(head == null || head.next == null) return head;ListNode res = null;ListNode current = head;ListNode before = null;ListNode next = null;ListNode third = null;//c n t//1 - 2 - 3 - 4 - 5 - 6while(current != null && current.next != null){next = current.next;third = current.next.next;current.next = third;next.next = current;if(res == null){res = next;}else {before.next = next;}before = current;current = third;}return res;} }

二刷兩兩交換鏈表

class Solution {public ListNode swapPairs(ListNode head) {if(head == null || head.next == null){return head;}//結(jié)果返回的節(jié)點(diǎn)ListNode res = null;//前一個(gè)節(jié)點(diǎn)ListNode per = null;//當(dāng)前節(jié)點(diǎn)ListNode current = null;ListNode next = null;ListNode third = null;// n c t//每次移動(dòng)兩位 2 -> 1 3 -> 4while(head != null && head.next != null){current = head;next = head.next;third = head.next.next;next.next = current;current.next = null;//確定返回的最終值if(res == null){res = next;}//連接到之前的鏈表中,并維護(hù)per指針if(per != null){per.next = next;per = current;}else{per = current;}head = third;}if(head != null){per.next = head;}return res;} }

?

六:劍指Offer[06]? 從尾到頭打印鏈表

? ? ? ?1.使用遞歸來解決問題,需要記錄遞歸的深度,用于創(chuàng)建數(shù)組

? ? ? ?2.遞歸的結(jié)束條件為當(dāng)前所查節(jié)點(diǎn)為null,此時(shí)需要?jiǎng)?chuàng)建數(shù)組,然后在遞歸返回時(shí),進(jìn)行賦值

class Solution {public int[] reversePrint(ListNode head) {return rec(head, 0);}//1 -> 3 -> 2public int[] rec(ListNode current, int num){if(current == null){return new int[num];}int[] ints = rec(current.next, ++num);int length = ints.length;//num在rec(current.next, ++num)遞歸時(shí),已經(jīng)加1了,此時(shí)不用再加1了ints[length - num] = current.val;return ints;} }

七: LeetCode[328]? 奇偶鏈表

? ? ? ? ? ?1.使用兩個(gè)鏈表來進(jìn)行保存奇偶鏈表,每次循環(huán)向前移動(dòng)兩位指針指向

? ? ? ? ? ?2.需要記錄奇偶鏈表的頭結(jié)點(diǎn)和當(dāng)前訪問的節(jié)點(diǎn),當(dāng)前節(jié)點(diǎn)用于拼接奇偶鏈表的下一個(gè)節(jié)點(diǎn),會(huì)進(jìn)行移動(dòng)

? ? ? ? ? ?3.在最后,奇鏈表的尾部鏈接偶鏈表的頭部即可

class Solution {public ListNode oddEvenList(ListNode head) {if(head == null || head.next == null) return head;ListNode even = null;ListNode odd = null;ListNode evenHead = null;ListNode oddHead = null;while (head != null){if(odd == null){odd = head;oddHead = head;}else {odd.next = head;odd = odd.next;}head = head.next;//需要將下一節(jié)點(diǎn)置為null,防止最后一個(gè)不設(shè)置而出現(xiàn)循環(huán)的情況,而且//需要在head = head.next之后,不然節(jié)點(diǎn)置為null時(shí),下個(gè)節(jié)點(diǎn)找不到odd.next = null;if(head == null){break ;}if(even == null){even = head;evenHead = head;}else {even.next = head;even = even.next;}head = head.next;//需要將下一節(jié)點(diǎn)置為null,防止最后一個(gè)不設(shè)置而出現(xiàn)循環(huán)的情況,而且//需要在head = head.next之后,不然節(jié)點(diǎn)置為null時(shí),下個(gè)節(jié)點(diǎn)找不到even.next = null;}odd.next = evenHead;return oddHead;} }

八: 劍指Offer[22]? 鏈表中倒數(shù)第K個(gè)節(jié)點(diǎn)

? ? ? ? ? 1.雙指針法,先讓快的節(jié)點(diǎn)移動(dòng)k位,再讓兩個(gè)指針同時(shí)移動(dòng),當(dāng)快的到尾部時(shí),慢的就是倒數(shù)第k為

class Solution { // 1->2->3->4->5, 和 k = 2.public ListNode getKthFromEnd(ListNode head, int k) {ListNode slow = head;ListNode fast = head;for (int i = 0; i < k; i++) {fast = fast.next;}while(fast != null){fast = fast.next;slow = slow.next;}return slow;} }

九:Leetcode[300]? 最長遞增子序列

給你一個(gè)整數(shù)數(shù)組 nums ,找到其中最長嚴(yán)格遞增子序列的長度。?
子序列是由數(shù)組派生而來的序列,刪除(或不刪除)數(shù)組中的元素而不改變其余元素的順序。例如,[3,6,2,7] 是數(shù)組 [0,3,1,6,2,2,7] 的子序列。?

示例 1:?
輸入:nums = [10,9,2,5,3,7,101,18]
輸出:4
解釋:最長遞增子序列是 [2,3,7,101],因此長度為 4 。

題解:?

使用動(dòng)態(tài)規(guī)劃進(jìn)行處理,最長遞增子序列的長度是和前面的子集有關(guān)系的,
如求第n個(gè)數(shù)的最長子序列,那么你就需要求出在 0 - n-1 之間的最長子序列,然后再 + 1,即可求出當(dāng)前第n個(gè)數(shù)的最長子序列

在求第n個(gè)數(shù)的最長子序列, 依次遍歷0 - n-1 之間的最長子序列,而且需要是數(shù)字小于第n位時(shí),才能生效

public int lengthOfLIS(int[] nums) {int length = nums.length;//新建數(shù)組,保存第n位他的最長子序列int[] dp = new int[length];//存放最終返回的值int res = 1;for (int i = 0; i < length; i++) {if(i == 0){dp[i] = 1;continue;}//依次從0 - n-1 找到比n小的數(shù)的最大的最長子序列int max = 0;for(int j = 0; j < i; j++){if(nums[j] < nums[i] && dp[j] > max){max = dp[j];}}dp[i] = max + 1;//進(jìn)行返回值的更新if(dp[i] > res){res = dp[i];}}return res;}

十:劍指Offer[38]? 字符串的排列

輸入一個(gè)字符串,打印出該字符串中字符的所有排列。?
你可以以任意順序返回這個(gè)字符串?dāng)?shù)組,但里面不能有重復(fù)元素。?

示例:?
輸入:s = "abc"
輸出:["abc","acb","bac","bca","cab","cba"]

題解:

把字符串分為兩部分:一部分是字符串的第一個(gè)字符,另一部分是第一個(gè)字符以后的所有字符。

第一步是求所有可能出現(xiàn)在第一個(gè)位置的字符,即把第一個(gè)字符和后面所有字符交換。(for循環(huán)、交換操作)
第二步是固定住第一個(gè)字符,求后面所有字符的排列。(遞歸)如何求剩余字符的排列:將剩余字符的第一個(gè)字符依次和后面的字符進(jìn)行交換,
而“求后面所有字符的排列”即可按照上面的思路遞歸進(jìn)行。
遞歸出口:求剩余字符的排列時(shí)只剩下一個(gè)字符
實(shí)現(xiàn)借助一個(gè)char[],通過交換元素得到不同的排列,在遞歸返回時(shí)將其裝入ArrayList。

如下圖所示,有兩點(diǎn)需要注意:

在每個(gè)分支進(jìn)行完后,要將交換過的元素復(fù)位,從而不會(huì)影響其他分支。
因?yàn)橛小癝wap A with A”這樣的操作,并且題目指出可能有字符重復(fù),所以分支末尾可能有重復(fù)的序列,在加入ArrayList要進(jìn)行去重判斷,不重復(fù)再加入。
以 abc為例,圖解過程如下:

class Solution {public String[] permutation(String s) {//1.先轉(zhuǎn)換為數(shù)組,然后再使用遞歸思想,進(jìn)行求解,每次固定第一個(gè)數(shù)if(s == null || "".equals(s)){return new String[]{};}//使用hashset進(jìn)行存儲(chǔ),判斷是否重復(fù),如使用list,則會(huì)超時(shí)HashSet<String> set = new HashSet<>();//當(dāng)前已遍歷的點(diǎn)addString(s.toCharArray(), 0, set);return set.toArray(new String[]{});}private void addString(char[] chars, int index, HashSet<String> set) {//如判斷到最后,則添加并返回if(index == chars.length - 1){String value = String.valueOf(chars);if(! set.contains(value)){set.add(value);}return;}for (int i = index; i < chars.length; i++) {//交換數(shù)組第i個(gè)字符和第index個(gè)字符swap(i, index, chars);addString(chars, index + 1, set);//再次交換數(shù)組第i個(gè)字符和第index個(gè)字符,保證回到此次for循環(huán)前字符數(shù)組的狀態(tài),不影響字符數(shù)組進(jìn)行下一次for循環(huán)swap(index, i, chars);}}private void swap(int i, int index, char[] chars) {char temp = chars[i];chars[i] = chars[index];chars[index] = temp;} }

十一:[LeetCode] 165. 比較版本號

題目描述:

比較兩個(gè)版本號 version1 和 version2。 如果 version1 > version2 返回 1,如果 version1 < version2 返回 -1, 除此之外返回 0。

你可以假設(shè)版本字符串非空,并且只包含數(shù)字和 . 字符。

. 字符不代表小數(shù)點(diǎn),而是用于分隔數(shù)字序列。

例如,2.5 不是“兩個(gè)半”,也不是“差一半到三”,而是第二版中的第五個(gè)小版本。

你可以假設(shè)版本號的每一級的默認(rèn)修訂版號為 0。例如,版本號 3.4 的第一級(大版本)和第二級(小版本)修訂號分別為 3 和 4。其第三級和第四級修訂號均為 0。

示例:

示例 1:

輸入: version1 = "0.1", version2 = "1.1" 輸出: -1

示例 2:

輸入: version1 = "1.0.1", version2 = "1" 輸出: 1

題解:

? ? ? ?先使用逗號進(jìn)行分割,然后將字符串轉(zhuǎn)為int型進(jìn)行比較,需要注意一點(diǎn)的是,兩個(gè)字符串使用逗號分割之后的長度可能不同,這個(gè)需要進(jìn)行一下特殊處理。

class Solution {public int compareVersion(String version1, String version2) {String[] split1 = version1.split("\\.");String[] split2 = version2.split("\\.");int length1 = split1.length;int length2 = split2.length;//找到最大的長度int maxLength = length1 > length2 ? length1 : length2;for (int i = 0; i < maxLength; i++) {String splitSub1 = null;String splitSub2 = null;//判斷是否達(dá)到最大,防止數(shù)組越界if(i < length1){splitSub1 = split1[i];}if(i < length2){splitSub2 = split2[i];}//都在的情況if(splitSub1 != null && splitSub2 != null){//都轉(zhuǎn)為int然后再進(jìn)行比較Integer integer1 = Integer.valueOf(splitSub1);Integer integer2 = Integer.valueOf(splitSub2);if(integer1 > integer2){return 1;}else if(integer1 < integer2){return -1;}}else if(splitSub1 == null){Integer integer2 = Integer.valueOf(splitSub2);if(integer2 != 0){return -1;}}else {Integer integer1 = Integer.valueOf(splitSub1);if(integer1 != 0){return 1;}}}return 0;} }

十二:[LeetCode] 11. 盛最多水的容器

題目描述:

給定?n?個(gè)非負(fù)整數(shù)?a1,a2,...,an,每個(gè)數(shù)代表坐標(biāo)中的一個(gè)點(diǎn) (i,?ai) 。在坐標(biāo)內(nèi)畫?n?條垂直線,垂直線?i?的兩個(gè)端點(diǎn)分別為 (i,?ai) 和 (i, 0)。找出其中的兩條線,使得它們與?x?軸共同構(gòu)成的容器可以容納最多的水。

說明:你不能傾斜容器,且 n 的值至少為 2。

圖中垂直線代表輸入數(shù)組 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍(lán)色部分)的最大值為 49。

示例:

輸入: [1,8,6,2,5,4,8,3,7] 輸出: 49

題解:

? ?設(shè)立兩個(gè)指針,一個(gè)從頭一個(gè)從尾,相向而行遍歷數(shù)組,每次舍棄較短邊

(1)計(jì)算面積最大值的初值,該初值以數(shù)組中的第一個(gè)元素和最后一個(gè)元素構(gòu)成兩邊。

(2)設(shè)置首尾兩個(gè)指針,首指針i指向數(shù)組中的第一個(gè)元素,尾指針j指向數(shù)組中的最后一個(gè)元素。

(3)當(dāng)首指針i小于尾指針j時(shí),一直循環(huán)計(jì)算其面積。若計(jì)算所得的當(dāng)前面積大于(1)步驟中所計(jì)算得的面積最大值,則更新最大值。每一次循環(huán)都舍棄索引i和索引j中較短的那一條邊。

為什么每一次循環(huán)舍棄索引i和索引j中較短的那一條邊,我們最終得到的結(jié)果就會(huì)是最大的面積值呢?

反證法:

假設(shè)我們現(xiàn)在遍歷到了height數(shù)組中第i和第j個(gè)元素,且height[i] < height[j],如果我們的面積最大值中取了第i個(gè)元素,那么構(gòu)成我們的面積最大值的兩個(gè)元素一定是i和j,因?yàn)?strong>j繼續(xù)減小的話長方形的寬肯定一直在減小,而其高最多只能是height[i],不可能比height[i]更大,因此我們在繼續(xù)遍歷的過程中,繼續(xù)保持i不變而減小j是沒有意義的。我們可以直接舍棄i,從i + 1開始去繼續(xù)遍歷。

由于整個(gè)過程只遍歷了一次數(shù)組,因此時(shí)間復(fù)雜度為O(n),其中n為數(shù)組height的長度。而使用空間就是幾個(gè)變量,故空間復(fù)雜度是O(1)。

class Solution {public int maxArea(int[] height) {if(height == null || height.length == 1){return 0;}int length = height.length;int right = length-1;int left = 0;int with = length-1;int res = 0;while (left < right){int minHeight = height[left] > height[right] ? height[right] : height[left];int temp = minHeight * with;if(temp > res){res = temp;}if(height[left] > height[right]){right--;}else {left++;}with--;}return res;} }

十三:[LeetCode] 26. 刪除排序數(shù)組中重復(fù)項(xiàng)

給定一個(gè)排序數(shù)組,你需要在原地刪除重復(fù)出現(xiàn)的元素,使得每個(gè)元素只出現(xiàn)一次,返回移除后數(shù)組的新長度。

不要使用額外的數(shù)組空間,你必須在原地修改輸入數(shù)組并在使用 O(1) 額外空間的條件下完成。

示例?1:

給定數(shù)組 nums = [1,1,2],

函數(shù)應(yīng)該返回新的長度 2, 并且原數(shù)組 nums 的前兩個(gè)元素被修改為 1, 2。

你不需要考慮數(shù)組中超出新長度后面的元素。
示例?2:

給定 nums = [0,0,1,1,1,2,2,3,3,4],

函數(shù)應(yīng)該返回新的長度 5, 并且原數(shù)組 nums 的前五個(gè)元素被修改為 0, 1, 2, 3, 4。

你不需要考慮數(shù)組中超出新長度后面的元素。
說明:

為什么返回?cái)?shù)值是整數(shù),但輸出的答案是數(shù)組呢?

請注意,輸入數(shù)組是以“引用”方式傳遞的,這意味著在函數(shù)里修改輸入數(shù)組對于調(diào)用者是可見的。

你可以想象內(nèi)部操作如下:

// nums 是以“引用”方式傳遞的。也就是說,不對實(shí)參做任何拷貝
int len = removeDuplicates(nums);

// 在函數(shù)里修改輸入數(shù)組對于調(diào)用者是可見的。
// 根據(jù)你的函數(shù)返回的長度, 它會(huì)打印出數(shù)組中該長度范圍內(nèi)的所有元素。
for (int i = 0; i < len; i++) {
? ? print(nums[i]);
}

題解:

官方題解:雙指針法

數(shù)組完成排序后,我們可以放置兩個(gè)指針i 和j,其中i 是慢指針,而j 是快指針。只要 nums[i] = nums[j],我們就增加 j 以跳過重復(fù)項(xiàng)。
當(dāng)我們遇到 nums[j] != nums[i],跳過重復(fù)項(xiàng)的運(yùn)行已經(jīng)結(jié)束,因此我們必須把它(nums[j])的值復(fù)制到 nums[i + 1]。然后遞增 i,接著我們將再次重復(fù)相同的過程,直到 j 到達(dá)數(shù)組的末尾為止。

復(fù)雜度分析
時(shí)間復(fù)雜度:O(n),假設(shè)數(shù)組的長度是 n,那么 i 和 j 分別最多遍歷 n 步。
空間復(fù)雜度:O(1)

class Solution {public int removeDuplicates(int[] nums) {if(nums == null) return 0;if(nums.length == 1) return 1;int res = 0;for(int i = 1; i<nums.length; i++){//不想等時(shí)進(jìn)行處理,先使res + 1,然后再進(jìn)行數(shù)據(jù)的替換,不需要考慮數(shù)組中超出新長度后面的元素if(nums[res] != nums[i]){res++;nums[res] = nums[i];}}return res + 1;} }

十四:[LeetCode] 104. 二叉樹最大深度

給定一個(gè)二叉樹,找出其最大深度。?

二叉樹的深度為根節(jié)點(diǎn)到最遠(yuǎn)葉子節(jié)點(diǎn)的最長路徑上的節(jié)點(diǎn)數(shù)。?

說明: 葉子節(jié)點(diǎn)是指沒有子節(jié)點(diǎn)的節(jié)點(diǎn)。?

示例:?
給定二叉樹 [3,9,20,null,null,15,7],?
? ? 3
? / \
?9 ?20
? ?/ ?\
? 15 ? 7?
返回它的最大深度 3 。?
Related Topics 樹 深度優(yōu)先搜索 遞歸

題解:

? ? ? ?直接使用遞歸判斷就行,每次判斷一個(gè)子樹的左節(jié)點(diǎn)和右節(jié)點(diǎn)哪個(gè)深度最大,并再次基礎(chǔ)上加1即可。

class Solution {public int maxDepth(TreeNode root) {if(root == null) return 0;//進(jìn)行左右節(jié)點(diǎn)的遞歸工作int left = maxDepth(root.left, 1);int right = maxDepth(root.right, 1);return Math.max(left, right);}private int maxDepth(TreeNode root, int depth){if(root == null){return depth;}return Math.max( maxDepth(root.left, depth + 1), maxDepth(root.right, depth + 1));} }

十五:[LeetCode] 104. 重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建該二叉樹。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字。?

例如,給出?

前序遍歷 preorder =?[3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]?

?返回如下的二叉樹:?
? ? ?3
? ?/ \
? 9 ?20
? ? / ?\
? ?15 ? 7?

?限制:?
?0 <= 節(jié)點(diǎn)個(gè)數(shù) <= 5000?

題解:

遞歸思想!
? ? ?使用前序和中序遍歷可以確認(rèn)唯一的二叉樹,通過前序可以得知根節(jié)點(diǎn)為1,然后通過中序可以得知左子樹為{4, 7, 2},右子樹為{5, 3, 8, 6}。接下來通過遞歸思想,{4, 7, 2}中4為根節(jié)點(diǎn),再繼續(xù)分左右子樹,對{5, 3, 8, 6}也做同樣操作,直至遍歷結(jié)束。

在二叉樹的前序遍歷序列中,第一個(gè)數(shù)字總是樹的根結(jié)點(diǎn)的值。但在中序遍歷序列中,根結(jié)點(diǎn)的值在序列的中間,左子樹的結(jié)點(diǎn)的值位于根結(jié)點(diǎn)的值的左邊,而右子樹的結(jié)點(diǎn)的值位于根結(jié)點(diǎn)的值的右邊。因此我們需要掃描中序遍歷序列,才能找到根結(jié)點(diǎn)的值。

前序遍歷序列的第一個(gè)數(shù)字1就是根結(jié)點(diǎn)的值。掃描中序遍歷序列,就能確定根結(jié)點(diǎn)的值的位置。根據(jù)中序遍歷特點(diǎn),在根結(jié)點(diǎn)的值1前面的3個(gè)數(shù)字都是左子樹結(jié)點(diǎn)的值,位于1后面的數(shù)字都是右子樹結(jié)點(diǎn)的值。

在二叉樹的前序遍歷和中序遍歷的序列中確定根結(jié)點(diǎn)的值、左子樹結(jié)點(diǎn)的值和右子樹結(jié)點(diǎn)的值的步驟如下圖所示:

自己的理解:

? ? ? ? ?1.遞歸時(shí),要注意,是先構(gòu)建好子樹,然后再構(gòu)建好外層的樹,我開始就是從外部開始構(gòu)建的,也就是說是先構(gòu)建好根節(jié)點(diǎn),然后將跟根節(jié)點(diǎn)傳遞給子節(jié)點(diǎn),并且判斷當(dāng)前是左字樹,還是右子樹,然后再進(jìn)行其他操作,這樣的話,違背了規(guī)則的原則,也是一個(gè)錯(cuò)誤的思想,子樹的構(gòu)建,不應(yīng)該牽連到父類是什么樣子的。

? ? ? ? 2.在子樹遞歸時(shí),需要時(shí)刻注意前根遍歷的開始下標(biāo)和結(jié)束下標(biāo),這個(gè)一定要判斷好,而對于中根遍歷的的話,因?yàn)楦谥虚g,正好可以平均分配,而前根就不一樣了,這是能做出提的關(guān)鍵。

class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {if(preorder == null || inorder == null){return null;}return recurseBuildTree(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);}public TreeNode recurseBuildTree(int[] preorder, int perLeft, int perRight, int[] inorder, int inLeft, int inRight){if(perLeft > perRight || inLeft > inRight){return null;}TreeNode root = new TreeNode(preorder[perLeft]);for(int i=inLeft; i<=inRight; i++){if(inorder[i] == root.val){//(i-inLeft)為左子樹的個(gè)數(shù),在加上perLeft,那么就是左子樹的結(jié)束root.left = recurseBuildTree(preorder, perLeft+1, i-inLeft+perLeft, inorder, inLeft, i-1);//(i-inLeft)為左子樹的個(gè)數(shù),在加上perLeft,那么就是左子樹的結(jié)束,再加1,那么就是右子樹的開始root.right = recurseBuildTree(preorder, i-inLeft+perLeft+1, perRight, inorder, i+1, inRight);}}return root;} }

十六:[LeetCode] 70. 爬樓梯

假設(shè)你正在爬樓梯。需要 n 階你才能到達(dá)樓頂。?
?每次你可以爬 1 或 2 個(gè)臺(tái)階。你有多少種不同的方法可以爬到樓頂呢??

?注意:給定 n 是一個(gè)正整數(shù)。?
?
示例 1:?
輸入: 2
輸出: 2

解釋: 有兩種方法可以爬到樓頂。
1. ?1 階 + 1 階
2. ?2 階?

示例 2:?
輸入: 3
輸出: 3
?
Related Topics 動(dòng)態(tài)規(guī)劃

題解:

使用動(dòng)態(tài)規(guī)劃,你爬第n階樓梯時(shí),可以從n-1上來,也可以從n-2階樓梯上來,所以你上樓梯的方法是在n-1和n-2的總和

class Solution {public int climbStairs(int n) {if(n==1) return 1;if(n==2) return 2;int[] dp = new int[n];dp[0] = 1;dp[1] = 2;for(int i=2; i<n;i++){dp[i] = dp[i-1] + dp[i-2];}return dp[n-1];} }

十七:[LeetCode] 189. 旋轉(zhuǎn)數(shù)組

給定一個(gè)數(shù)組,將數(shù)組中的元素向右移動(dòng) k 個(gè)位置,其中 k 是非負(fù)數(shù)。?

?進(jìn)階:?
?盡可能想出更多的解決方案,至少有三種不同的方法可以解決這個(gè)問題。?
?你可以使用空間復(fù)雜度為 O(1) 的 原地 算法解決這個(gè)問題嗎??

示例 1:
輸入: nums = [1,2,3,4,5,6,7], k = 3
輸出: [5,6,7,1,2,3,4]

解釋:
向右旋轉(zhuǎn) 1 步: [7,1,2,3,4,5,6]
向右旋轉(zhuǎn) 2 步: [6,7,1,2,3,4,5]
向右旋轉(zhuǎn) 3 步: [5,6,7,1,2,3,4]
?

?示例 2:?
?
輸入:nums = [-1,-100,3,99], k = 2
輸出:[3,99,-1,-100]

解釋:?
向右旋轉(zhuǎn) 1 步: [99,-1,-100,3]
向右旋轉(zhuǎn) 2 步: [3,99,-1,-100]?

題解:

? ? ? 解法一:可以增加一個(gè)數(shù)組保存新移動(dòng)的數(shù)據(jù),這個(gè)簡單

? ? ? 解法二:先進(jìn)行整體的反轉(zhuǎn),然后根據(jù)右移數(shù),進(jìn)行兩次局部的反轉(zhuǎn)

?

class Solution {public void rotate(int[] nums, int k) {//先進(jìn)行整體的反轉(zhuǎn),再進(jìn)行局部的反轉(zhuǎn),int length = nums.length;k %= length;//整體反轉(zhuǎn) 如nums = [1,2,3,4,5,6,7], k = 3recurseArray(nums, 0, length-1); //[7, 6, 5, 4, 3, 2, 1]//局部的反轉(zhuǎn)recurseArray(nums, 0, k-1); //[5, 6, 7, 4, 3, 2, 1]recurseArray(nums, k, length-1); //[5, 6, 7, 1, 2, 3, 4]}private void recurseArray(int[] nums, int i, int j){//兩兩比較if(i>j){return;}while(i<j){swap(nums, i, j);i++;j--;}}private void swap(int[] nums, int i, int j){int temp = nums[i];nums[i] = nums[j];nums[j] = temp;} }

十八:[LeetCode] 15. 三數(shù)之和

給你一個(gè)包含 n 個(gè)整數(shù)的數(shù)組?nums,判斷?nums?中是否存在三個(gè)元素 a,b,c ,使得?a + b + c = 0 ?請你找出所有和為 0 且不重復(fù)的三元組。

注意:答案中不可以包含重復(fù)的三元組。

示例 1:

輸入:nums = [-1,0,1,2,-1,-4]
輸出:[[-1,-1,2],[-1,0,1]]
示例 2:

輸入:nums = []
輸出:[]
示例 3:

輸入:nums = [0]
輸出:[]
?
提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

題解:

? ? ? 可以先對數(shù)組進(jìn)行排序,使用Arrays.sort進(jìn)行排序,然后數(shù)組是從到大進(jìn)行排序的了,然后使用?a+b = -c,先固定一個(gè)數(shù),然后使用雙指針法進(jìn)行數(shù)組的查找,寫代碼時(shí),要注意不能出現(xiàn)重復(fù)的數(shù)。

需要注意的點(diǎn):

? ? ? 1.每次都需要進(jìn)行判斷,當(dāng)前下標(biāo)值和上一個(gè)下標(biāo)值是否一樣,一樣則跳過

? ? ? 2.數(shù)組只需要遍歷到當(dāng)前下標(biāo)值>=0就行,如果都大于1,顯然不滿足條件

class Solution {public List<List<Integer>> threeSum(int[] nums) {if(nums == null){return null;}Arrays.sort(nums);List<List<Integer>> list = new ArrayList<>();for(int i=0; i<nums.length-2 && nums[i]<=0; i++){//去除重復(fù)的元素if(i!=0 && nums[i]==nums[i-1]){continue;}int left = i+1;int right = nums.length-1;twoSum(list, nums[i], left, right, nums);}return list;}private void twoSum(List<List<Integer>> list, int target, int left, int right, int[] nums){int pre = nums[left] - 1; //保證第一次pre不能和nums[left]相同,之后會(huì)用這個(gè)判斷是否發(fā)生了重復(fù)while(left < right){if(pre == nums[left]){left++;continue;}int temp = 0 - (nums[left] + nums[right]);if(target == temp){pre = nums[left]; //這個(gè)要相同之后,再進(jìn)行一個(gè)判斷,防止重復(fù)List<Integer> subList = new ArrayList<>();subList.add(nums[left]);subList.add(nums[right]);subList.add(target);list.add(subList);left++;right--; //這步不能忘}else if(target > temp){right--;}else{left++;}}} }

十九:[LeetCode] 3. 無重復(fù)字符的最長子串

給定一個(gè)字符串,請你找出其中不含有重復(fù)字符的?最長子串?的長度。

示例?1:
輸入: s = "abcabcbb"
輸出: 3?
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 "abc",所以其長度為 3。

示例 2:
輸入: s = "bbbbb"
輸出: 1
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 "b",所以其長度為 1。

示例 3:
輸入: s = "pwwkew"
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是?"wke",所以其長度為 3。
?? ? 請注意,你的答案必須是 子串 的長度,"pwke"?是一個(gè)子序列,不是子串。

示例 4:
輸入: s = ""
輸出: 0

提示:
0 <= s.length <= 5 * 104
s?由英文字母、數(shù)字、符號和空格組成

題解:

? ? ? 使用滑動(dòng)窗口進(jìn)行求解,使用map進(jìn)行存儲(chǔ)已經(jīng)存在的元素,字符 ->? 數(shù)組下標(biāo) 的形勢進(jìn)行保存,循環(huán)時(shí),每次都判斷,當(dāng)前字符是否出現(xiàn)過,如果出現(xiàn)過,那么就要更新滑動(dòng)窗口的開始位置,開始位置的判斷是本題的解題關(guān)鍵

解題注意點(diǎn):

? ? ? ?1.需要到了HashMap的containsKey方法,這個(gè)要會(huì)寫

? ? ? ?2.在map.get(c)時(shí),可能字符串在map中,但是出現(xiàn)的位置在滑動(dòng)窗口的左邊,此時(shí)這個(gè)值是不能算是在滑動(dòng)窗口出現(xiàn)過的,這個(gè)需要特別注意,比如: kababkc,當(dāng)判斷數(shù)組下標(biāo)為5, 字符'k'時(shí),此時(shí)的滑動(dòng)窗口的開始下標(biāo)為3, 此時(shí)k已經(jīng)在map中,下標(biāo)為1 ,所以對于下標(biāo)開始的判斷很關(guān)鍵

class Solution {public int lengthOfLongestSubstring(String s) {int n = s.length();Map<Character, Integer> map = new HashMap<>(); int res = 0;int start = 0;for (int j = 0; j < n; j++) {//取出該元素值char c = s.charAt(j);if(map.containsKey(c)){//得到字符串的下一個(gè)位置,這個(gè)位置可能會(huì)成功滑動(dòng)窗口的開始位置int start2 = map.get(c)+1;//取出一個(gè)最大的當(dāng)做開始,為什么需要這么做,因?yàn)檫@個(gè)map是沒有刪除的,也就是說map中存放的元素,有可能在//滑動(dòng)窗口的左邊,這時(shí)取得值都已經(jīng)作廢了//比如: kababkc,當(dāng)判斷數(shù)組下標(biāo)為5, 字符'k'時(shí),此時(shí)的滑動(dòng)窗口的開始下標(biāo)為3, 此時(shí)k已經(jīng)在map中,下標(biāo)為1//所以對于下標(biāo)開始的判斷很關(guān)鍵start = Math.max(start, start2);}map.put(c, j);//進(jìn)行值的更新if(res < (j-start+1)){res = j-start+1;}}return res;} }

二十:[LeetCode] 128. 最長連續(xù)序列

給定一個(gè)未排序的整數(shù)數(shù)組?nums?,找出數(shù)字連續(xù)的最長序列(不要求序列元素在原數(shù)組中連續(xù))的長度。

進(jìn)階:你可以設(shè)計(jì)并實(shí)現(xiàn)時(shí)間復(fù)雜度為?O(n)?的解決方案嗎?

示例 1:

輸入:nums = [100,4,200,1,3,2] 輸出:4 解釋:最長數(shù)字連續(xù)序列是 [1, 2, 3, 4]。它的長度為 4。

示例 2:

輸入:nums = [0,3,7,2,5,8,4,6,0,1] 輸出:9

提示:

  • 0 <= nums.length <= 104
  • -109?<= nums[i] <= 109

題解:

? ? ? ?使用哈希表進(jìn)行解決,依次遍歷整個(gè)數(shù)組進(jìn)行查找,而且在查找時(shí),我們只從開始位置進(jìn)行查詢,也就是說,當(dāng)我們從 2查找時(shí),1不能存在,1存在的話,就會(huì)出現(xiàn)重復(fù),導(dǎo)致我們查找的出現(xiàn)重復(fù)。

注意點(diǎn):

? ? ? ?1.使用HashSet將整個(gè)數(shù)組進(jìn)行保存,去重

? ? ? ? 2.依次遍歷數(shù)組的每個(gè)元素,如果該元素減1,不存放于數(shù)組中,那么它就是一個(gè)開始位置,我們從這個(gè)依次+1,去HashSet中進(jìn)行查找。

class Solution {public int longestConsecutive(int[] nums) {//先將數(shù)都放到HashSet中HashSet<Integer> set = new HashSet<>();for(int i=0;i<nums.length;i++){set.add(nums[i]);}int res = 0;for(int i=0; i<nums.length; i++){int num = nums[i];//set中不包含它的一個(gè)數(shù)的前驅(qū),說明它就是開始,那么以它為開始節(jié)點(diǎn),進(jìn)行搜索if(!set.contains(num-1)){//以當(dāng)前節(jié)點(diǎn)為開始,進(jìn)行一個(gè)搜索,如果set中也存放num++,那么長度+1int current = 0;while(set.contains(num+1)){num++;current++;}res = Math.max(res, current+1);}}return res;} }

二十一:[LeetCode] 17. 電話號碼的字母組合

給定一個(gè)僅包含數(shù)字?2-9?的字符串,返回所有它能表示的字母組合。答案可以按 任意順序 返回。

給出數(shù)字到字母的映射如下(與電話按鍵相同)。注意 1 不對應(yīng)任何字母。

示例 1:

輸入:digits = "23"
輸出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:

輸入:digits = ""
輸出:[]
示例 3:

輸入:digits = "2"
輸出:["a","b","c"]
?

提示:

0 <= digits.length <= 4
digits[i] 是范圍 ['2', '9'] 的一個(gè)數(shù)字。

題解:

? ? ? 全排列的一般都是使用遞歸,此時(shí)需要注意的是怎么遞歸,結(jié)束條件是什么

注意:

? ? ? ?1.對于每個(gè)數(shù)字對應(yīng)的字符進(jìn)行映射,使用Map<Character, String>進(jìn)行存儲(chǔ)

? ? ? ?2.獲取到數(shù)組對應(yīng)的String數(shù)組,因?yàn)槊總€(gè)數(shù)字對應(yīng)一個(gè)數(shù)組,所以是String,這里依次從String的第一個(gè)開始,然后進(jìn)行遞歸

? ? ? ?3.使用StringBuilder進(jìn)行保存,可以進(jìn)行恢復(fù)

class Solution {public List<String> letterCombinations(String digits) {if(digits == null || digits.length() == 0){return new ArrayList<>();}Map<Character, String> map = new HashMap<>();map.put('2',"abc");map.put('3',"def");map.put('4',"ghi");map.put('5',"jkl");map.put('6',"mno");map.put('7',"pqrs");map.put('8',"tuv");map.put('9',"wxyz");String[] strArr = new String[digits.length()];for(int i=0; i<digits.length(); i++){char c = digits.charAt(i);strArr[i] = map.get(c);}List<String> list = new ArrayList<>();//從第0個(gè)開始,每次遞歸調(diào)用recurseCombinations(strArr, 0, list, new StringBuilder());return list;}private void recurseCombinations(String[] strArr, int index, List<String> list, StringBuilder sb){//當(dāng)遍歷完成后,轉(zhuǎn)為String,并放到集合中if(strArr.length == index){list.add(sb.toString());return;}char[] chars = strArr[index].toCharArray();//依次遍歷當(dāng)前的每一個(gè)字符for(int i=0; i<chars.length; i++){sb.append(chars[i]);recurseCombinations(strArr, index+1, list, sb);sb.deleteCharAt(index); //進(jìn)行元素的復(fù)原操作}} }

二十二:[LeetCode] 22. 括號生成

數(shù)字 n?代表生成括號的對數(shù),請你設(shè)計(jì)一個(gè)函數(shù),用于能夠生成所有可能的并且 有效的 括號組合。

示例 1:

輸入:n = 3
輸出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:

輸入:n = 1
輸出:["()"]

提示:

1 <= n <= 8

題解:

? ? ? ?使用遞歸解決,并且關(guān)于字符串使用StringBuilder,?使用兩個(gè)變量都為n,?一個(gè)代表左括號的剩余數(shù)量left,一個(gè)代表右括號的剩余數(shù)量right,? 初始都為n,?當(dāng)左括號、右括號剩余數(shù)量都為0

時(shí),進(jìn)行生成字符串。

? ? ? 遞歸的返回條件為left<0? ||??right< 0 ||?left >?right,其中對于left >?right表示的是左括號剩余的大于右括號,如())這樣的就不滿足條件,直接返回。

class Solution {public List<String> generateParenthesis(int n) {List<String> list = new ArrayList<>();StringBuilder sb = new StringBuilder();recurseGenerate(n, n, sb, list);return list;}private void recurseGenerate(int left, int right, StringBuilder sb, List<String> list){if(left > right || left < 0 || right < 0){ //left > right,表示如果左括號剩余的多,說明本次是違規(guī)的操作,比如))()((,這樣的輸出不合法return;}if(left == 0 && right == 0){list.add(sb.toString());}recurseGenerate(left-1, right, sb.append("("), list);sb.deleteCharAt(sb.length()-1);recurseGenerate(left, right-1, sb.append(")"), list);sb.deleteCharAt(sb.length()-1);} }

二十三:[LeetCode] 200. 島嶼數(shù)量

給你一個(gè)由?'1'(陸地)和 '0'(水)組成的的二維網(wǎng)格,請你計(jì)算網(wǎng)格中島嶼的數(shù)量。

島嶼總是被水包圍,并且每座島嶼只能由水平方向和/或豎直方向上相鄰的陸地連接形成。

此外,你可以假設(shè)該網(wǎng)格的四條邊均被水包圍。

示例 1:

輸入:grid = [
? ["1","1","1","1","0"],
? ["1","1","0","1","0"],
? ["1","1","0","0","0"],
? ["0","0","0","0","0"]
]
輸出:1
示例 2:

輸入:grid = [
? ["1","1","0","0","0"],
? ["1","1","0","0","0"],
? ["0","0","1","0","0"],
? ["0","0","0","1","1"]
]
輸出:3
?

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值為 '0' 或 '1'

題解:

? ? ? ?使用DFS進(jìn)行求解,如果當(dāng)前遍歷的坐標(biāo)符合要求,則將坐標(biāo)位置置為0

注意點(diǎn):

? ? ? ?1.遞歸的結(jié)束條件

class Solution {public int numIslands(char[][] grid) {int n = grid.length;int m = grid[0].length;int res = 0;for(int i=0; i<n; i++){for(int j=0; j<m; j++){if(grid[i][j] == '1'){res++;recurseNumIslands(grid, i, j);}}}return res;}private void recurseNumIslands(char[][]grid, int i, int j){//遞歸結(jié)束條件要清楚,特別是grid[i][j] == '0',不然就會(huì)出錯(cuò)if(i<0 || i>= grid.length || j<0 || j>=grid[0].length || grid[i][j] == '0'){return;}//設(shè)置為0grid[i][j] = '0';//進(jìn)行遞歸recurseNumIslands(grid, i+1 ,j);recurseNumIslands(grid, i-1 , j);recurseNumIslands(grid, i , j+1);recurseNumIslands(grid, i , j-1);} }

二十四:[LeetCode] 543. 二叉樹的直徑

給定一棵二叉樹,你需要計(jì)算它的直徑長度。一棵二叉樹的直徑長度是任意兩個(gè)結(jié)點(diǎn)路徑長度中的最大值。這條路徑可能穿過也可能不穿過根結(jié)點(diǎn)。?

示例 :?
給定二叉樹?
? ? ? ? ? ?1
? ? ? ? ?/ \
? ? ? ? 2 ? 3
? ? ? ?/ \ ? ??
? ? ? 4 ? 5 ? ?
?返回 3, 它的長度是路徑 [4,2,1,3] 或者 [5,2,1,3]。?

?注意:兩結(jié)點(diǎn)之間的路徑長度是以它們之間邊的數(shù)目表示。?
?Related Topics 樹

題解:

使用遞歸 + 動(dòng)態(tài)規(guī)劃解決問題

?遞歸調(diào)用每個(gè)節(jié)點(diǎn),而且每個(gè)節(jié)點(diǎn)保存兩個(gè)值,一個(gè)是當(dāng)前節(jié)點(diǎn)的最大深度, 一個(gè)是以當(dāng)前節(jié)點(diǎn)為一個(gè)子樹的內(nèi)部的最大深度,
?使用數(shù)組進(jìn)行保存,int[2]{maxDiameterWithCurrentRootNode, maxDeep }

?當(dāng)前節(jié)點(diǎn)的使用current[],左節(jié)點(diǎn)使用leftTemps[],右節(jié)點(diǎn)使用RightTemps[],求當(dāng)前節(jié)點(diǎn)時(shí),左右節(jié)點(diǎn)的值均已求出

?current[0] = max(leftTemps[0], ?RightTemps[0], ?leftTemps[1] + RightTemps[1] + 1), 其中l(wèi)eftTemps[1] + RightTemps[1] + 1為已當(dāng)前節(jié)點(diǎn)為根的最大直徑
?current[1] = max(leftTemps[1] , ?RightTemps[1]) + 1

class Solution {public int diameterOfBinaryTree(TreeNode root) {//使用遞歸解決問題,每次保存一個(gè)數(shù)組,第一個(gè)下標(biāo)為以當(dāng)前節(jié)點(diǎn)為根的最大直徑,//第二個(gè)就是以當(dāng)前節(jié)點(diǎn)的的最大深度//使用動(dòng)態(tài)規(guī)劃 + 遞歸運(yùn)算if(root == null){return 0;}int[] temps = recurseDiameter(root);return Math.max(temps[0], temps[1]) - 1;}private int[] recurseDiameter(TreeNode root){if(root == null){return new int[]{0,0};}//leftTemps[0] 代表以root.left為根的最大直徑, leftTemps[1]代表以root.left中的最大深度int[] leftTemps = recurseDiameter(root.left);int[] rightTemps = recurseDiameter(root.right);//當(dāng)前節(jié)點(diǎn)的最大深度int maxdeep = Math.max(leftTemps[1], rightTemps[1]) + 1;//以當(dāng)前節(jié)點(diǎn)為根的最大直徑, 它的取值為Math(左節(jié)點(diǎn)為根的最大直徑, 有節(jié)點(diǎn)為根的最大直徑, 當(dāng)前節(jié)點(diǎn)為根的最大直徑),取最大的一個(gè)int max = Math.max(leftTemps[0], rightTemps[0]);max = Math.max(max, leftTemps[1] + rightTemps[1] + 1);return new int[]{max, maxdeep};} }

二十五:[LeetCode] 146. LRU緩存機(jī)制

運(yùn)用你所掌握的數(shù)據(jù)結(jié)構(gòu),設(shè)計(jì)和實(shí)現(xiàn)一個(gè) LRU (最近最少使用) 緩存機(jī)制 。?
?
實(shí)現(xiàn) LRUCache 類:?
?
LRUCache(int capacity) 以正整數(shù)作為容量 capacity 初始化 LRU 緩存?
int get(int key) 如果關(guān)鍵字 key 存在于緩存中,則返回關(guān)鍵字的值,否則返回 -1 。?
void put(int key, int value) 如果關(guān)鍵字已經(jīng)存在,則變更其數(shù)據(jù)值;如果關(guān)鍵字不存在,則插入該組「關(guān)鍵字-值」。當(dāng)緩存容量達(dá)到上
限時(shí),它應(yīng)該在寫入新數(shù)據(jù)之前刪除最久未使用的數(shù)據(jù)值,從而為新的數(shù)據(jù)值留出空間。?

進(jìn)階:你是否可以在 O(1) 時(shí)間復(fù)雜度內(nèi)完成這兩種操作??

示例:?
輸入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
輸出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解釋
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); ?緩存是 {1=1}
lRUCache.put(2, 2); ?緩存是 {1=1, 2=2}
lRUCache.get(1); ? ? 返回 1
lRUCache.put(3, 3); ?該操作會(huì)使得關(guān)鍵字 2 作廢,緩存是 {1=1, 3=3}
lRUCache.get(2); ? ? 返回 -1 (未找到)
lRUCache.put(4, 4); ?該操作會(huì)使得關(guān)鍵字 1 作廢,緩存是 {4=4, 3=3}
lRUCache.get(1); ? ? 返回 -1 (未找到)
lRUCache.get(3); ? ? 返回 3
lRUCache.get(4); ? ? 返回 4

?提示:?
?1 <= capacity <= 3000?
?0 <= key <= 3000?
?0 <= value <= 104?
?最多調(diào)用 3 * 104 次 get 和 put?

題解:

? ? ? 使用LinkedHashMap可以很輕松的實(shí)現(xiàn),但是你這樣的話,沒有起到自己設(shè)計(jì)的目的,只是自己調(diào)用了API,所以,這里使用HashMap進(jìn)行設(shè)計(jì)。

注意點(diǎn):

? ? ? 1.LRU需要使用鏈表結(jié)構(gòu)進(jìn)行保存,就像LinkedHashMap一樣,構(gòu)造虛擬節(jié)點(diǎn)內(nèi)部類,保存到HashMap的value中

? ? ? 2.HashMap中value保存的是虛擬的鏈表節(jié)點(diǎn),如果value直接存放值的話,工作量太大

? ? ? 3.構(gòu)建出虛擬的頭結(jié)點(diǎn)和尾節(jié)點(diǎn),只是用來指明頭尾的位置,這樣的不用考慮空指針異常的問題,這個(gè)能想到的話,做題很快的

? ? ? 4.明白LUR的含義,在get時(shí),將訪問的當(dāng)前節(jié)點(diǎn)移動(dòng)到頭部去,當(dāng)put時(shí),先查看是否存在,如不存在,則創(chuàng)建虛擬節(jié)點(diǎn)進(jìn)行包裝后,放入,再判斷是否超過最大容量,超過時(shí),則需要?jiǎng)h除。

class LRUCache {class CacheLinked{int key;int val;CacheLinked next;CacheLinked pre;public CacheLinked(){}public CacheLinked(int key, int val){this.key = key;this.val = val;}}//size存放當(dāng)前元素?cái)?shù), capacity存放最大元素?cái)?shù)private int size;private int capacity;private CacheLinked head;private CacheLinked tail;Map<Integer, CacheLinked> map = new HashMap<>();public LRUCache(int capacity) {this.capacity = capacity;//構(gòu)建頭尾的虛擬節(jié)點(diǎn),不存放任何元素,只是為了操作簡單,不用進(jìn)行空指針的判斷,如果不構(gòu)建//虛擬節(jié)點(diǎn)有你受的了,光空指針的判斷就很麻煩head = new CacheLinked();tail = new CacheLinked();head.next = tail;tail.pre = head;}public int get(int key) {CacheLinked cache = map.get(key);if(cache == null){return -1;}//更新lru,移除元素,并添加到首部moveCache(cache);addHead(cache);return cache.val;}public void put(int key, int value) {CacheLinked cache = map.get(key);//分兩種情況,緩存已存在,和緩存不存在,if(cache == null){CacheLinked newCache = new CacheLinked(key, value);addHead(newCache);//判斷容量大小,如果超過了,那么就將最就的那個(gè)從hashmap中刪除size++;if(size > capacity){CacheLinked oldTail = moveTail();//移除元素map.remove(oldTail.key);}map.put(key, newCache);}else{//更新值cache.val = value;//更新lru,移除元素,并添加到首部moveCache(cache);addHead(cache);}}private void addHead(CacheLinked cache){CacheLinked oldHead = head.next;head.next = cache;cache.next = oldHead;oldHead.pre = cache;cache.pre = head;}private void moveCache(CacheLinked cache){//因?yàn)橛刑摂M節(jié)點(diǎn)的存在,根本不用判斷空指針的問題cache.pre.next = cache.next;cache.next.pre = cache.pre;//釋放元素,防止內(nèi)存遺漏cache.next = null;cache.pre = null;}//移除尾部元素,并將尾部元素進(jìn)行返回private CacheLinked moveTail(){CacheLinked oldTail = tail.pre;CacheLinked newTail = oldTail.pre;newTail.next = tail;tail.pre = newTail;oldTail.next = null;oldTail.pre = null;return oldTail;} }

二十六:[LeetCode] 337. 打家劫舍

在上次打劫完一條街道之后和一圈房屋后,小偷又發(fā)現(xiàn)了一個(gè)新的可行竊的地區(qū)。這個(gè)地區(qū)只有一個(gè)入口,我們稱之為“根”。 除了“根”之外,每棟房子有且只有一個(gè)“父“
房子與之相連。一番偵察之后,聰明的小偷意識(shí)到“這個(gè)地方的所有房屋的排列類似于一棵二叉樹”。 如果兩個(gè)直接相連的房子在同一天晚上被打劫,房屋將自動(dòng)報(bào)警。?

計(jì)算在不觸動(dòng)警報(bào)的情況下,小偷一晚能夠盜取的最高金額。?

示例 1:?
輸入: [3,2,3,null,3,null,1]

? ? ?3
? ? / \
? ?2 ? 3
? ? \ ? \?
? ? ?3 ? 1
輸出: 7?
解釋:?小偷一晚能夠盜取的最高金額 = 3 + 3 + 1 = 7.?

示例 2:?
輸入: [3,4,5,1,3,null,1]

?? ? 3
? ? / \
? ?4 ? 5
? / \ ? \?
?1 ? 3 ? 1
輸出: 9
解釋:?小偷一晚能夠盜取的最高金額?= 4 + 5 = 9.
?
Related Topics 樹 深度優(yōu)先搜索

題解:

????? 使用遞歸 + 動(dòng)態(tài)規(guī)劃進(jìn)行求解,因?yàn)槊總€(gè)節(jié)點(diǎn)都可以選也可以不選,如果當(dāng)前節(jié)點(diǎn)選擇的話,他的兩個(gè)子節(jié)點(diǎn)都不能選擇了,所以,我們可以使用數(shù)組保存當(dāng)前節(jié)點(diǎn)的兩個(gè)狀態(tài),即[0]不選當(dāng)前節(jié)點(diǎn)的最大值, [1]選擇了當(dāng)前節(jié)點(diǎn)的最大值

注意:
? ? ? 1.對于選擇當(dāng)前節(jié)點(diǎn)還好,直接左右節(jié)點(diǎn)不選 + 當(dāng)前節(jié)點(diǎn)的value
? ? ? ?而對于不選當(dāng)前節(jié)點(diǎn),那么左節(jié)點(diǎn)和右節(jié)點(diǎn)都可以選或者是不選,所以,我們要查出最大的左節(jié)點(diǎn)和最大的右節(jié)點(diǎn),如下面這樣情況

? ? ? ? ?3
? ? ? /? ? ?\
? ? ?4? ? ? ?5
? ?/ ? \? ? ? ? \
1 ?10000 ? ?1
? ?當(dāng)遍歷到根節(jié)點(diǎn)3時(shí),我們看根據(jù)是不選4這個(gè)節(jié)點(diǎn),他的整體會(huì)更大一點(diǎn)

class Solution {public int rob(TreeNode root) {if(root == null){return 0;}int[] res = recurseReachMaxRob(root);return Math.max(res[0], res[1]);}public int[] recurseReachMaxRob(TreeNode root) {if(root == null){return new int[]{0, 0};}//[0]不選當(dāng)前節(jié)點(diǎn)的最大值, [1]選擇了當(dāng)前節(jié)點(diǎn)的最大值int[] leftMaxJob = recurseReachMaxRob(root.left);int[] rightMaxJob = recurseReachMaxRob(root.right);//選擇了當(dāng)前節(jié)點(diǎn),那么它的左右孩子都不能選int selectCurrent = leftMaxJob[0] + rightMaxJob[0] + root.val;//不選擇當(dāng)前節(jié)點(diǎn)的話,那么左右節(jié)點(diǎn)都可以選,也都可以不選,這樣要看做大值//這個(gè)是解題的關(guān)鍵,當(dāng)前節(jié)點(diǎn)不選的,他的子節(jié)點(diǎn)可以選,也可以不選,這是,就要取最大的一個(gè),//? 3// / \// 4 5// / \ \// 1 10000 1//當(dāng)遍歷到根節(jié)點(diǎn)3時(shí),我們看根據(jù)是不選4這個(gè)節(jié)點(diǎn),他的整體會(huì)更大一點(diǎn)int noSelectCurrent = Math.max(leftMaxJob[0], leftMaxJob[1]) + Math.max(rightMaxJob[0], rightMaxJob[1]);return new int[]{noSelectCurrent, selectCurrent};} }

二十七:[LeetCode] 101. 對稱二叉樹

給定一個(gè)二叉樹,檢查它是否是鏡像對稱的。?

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。?
? ? ?1
? ?/ \
? 2 ? 2
?/ \ / \
3 ?4 4 ?3

但是下面這個(gè) [1,2,2,null,3,null,3] 則不是鏡像對稱的:?
? ? ?1
? ?/ \
? 2 ? 2
? ?\ ? \
? ?3 ? ?3

進(jìn)階:?
你可以運(yùn)用遞歸和迭代兩種方法解決這個(gè)問題嗎??
Related Topics 樹 深度優(yōu)先搜索 廣度優(yōu)先搜索

題解:
?? ??? ?首先分析下這個(gè)對稱二叉樹,也就是一個(gè)二叉樹中間對稱。所以我們可以使用遞歸的思想,首先以根節(jié)點(diǎn)以及其左右子樹,左子樹的左子樹和右子樹的右子樹相同,左子樹的右子樹和右子樹的左子樹相同。兩個(gè)條件都要符合,所以我們第一個(gè)傳根節(jié)點(diǎn)的左子樹和右子樹,先判斷左右子樹根結(jié)點(diǎn)的比較。然后分辨對左子樹的左子樹和右子樹的右子樹。左子樹的右子樹和右子樹的左子樹進(jìn)行判斷。只有兩個(gè)條件都滿足則返回的是true,一層一層遞歸進(jìn)入,則可以得到結(jié)果。
注意:
?? ??? ?1.主要遞歸進(jìn)行判斷,如果值不相同的話,那么也不是一個(gè)鏡像二叉樹
?? ??? ?2.只要有一個(gè)返回為false,那么整個(gè)結(jié)果就會(huì)返回false

class Solution {public boolean isSymmetric(TreeNode root) {if(root == null){return false;}return recurseJudgeSymmetric(root.left, root.right);}private boolean recurseJudgeSymmetric(TreeNode left, TreeNode right){if(left == null){//left為null時(shí),判斷right是否為null//right == null,成立返回truereturn right == null;}//此時(shí)走到這,說明left不為空了,if(right == null){return false;}//值不相等,則也返回falseif(left.val != right.val){return false;}boolean symmetric1 = recurseJudgeSymmetric(left.left, right.right);boolean symmetric2 = recurseJudgeSymmetric(left.right, right.left);//返回他們之間的 && 運(yùn)算,只要有一個(gè)為false那么就為falsereturn symmetric1 && symmetric2;} }

二十八:[LeetCode] 98. 驗(yàn)證二叉搜索樹

給定一個(gè)二叉樹,判斷其是否是一個(gè)有效的二叉搜索樹。?

假設(shè)一個(gè)二叉搜索樹具有如下特征:?

節(jié)點(diǎn)的左子樹只包含小于當(dāng)前節(jié)點(diǎn)的數(shù)。?
節(jié)點(diǎn)的右子樹只包含大于當(dāng)前節(jié)點(diǎn)的數(shù)。?
所有左子樹和右子樹自身必須也是二叉搜索樹。?
?

示例 1:?
輸入:
? ? 2
? ?/ \
? 1 ? 3
輸出: true
?

示例 2:?
輸入:
? ? 5
? ?/ \
? 1 ? 4
? ? ? / \
? ? 3 ? 6
輸出: false
解釋: 輸入為: [5,1,4,null,null,3,6]。
根節(jié)點(diǎn)的值為 5 ,但是其右子節(jié)點(diǎn)值為 4 。

題解:
?? ??? ?先使用中序遍歷,二叉搜索樹的話,對于中序遍歷來說,整個(gè)數(shù)組是有序的,所以可以依次判斷數(shù)組的后一個(gè)是否比前一個(gè)數(shù)組大,如果小的話,則不滿足。
?? ??? ?
?

class Solution {public boolean isValidBST(TreeNode root) {if(root == null){return false;}//中序遍歷,肯定是遞增的List<Integer> list = new ArrayList<>();//先進(jìn)行中序遍歷recurseValidBST(root, list);//如果是二叉搜索樹的話,那么數(shù)據(jù)是有序的,進(jìn)行遍歷就行for(int i=1; i<list.size(); i++){if(list.get(i-1) >= list.get(i)){return false;}}return true;}private void recurseValidBST(TreeNode root, List<Integer> list){if(root == null){return;}recurseValidBST(root.left, list);list.add(root.val);recurseValidBST(root.right, list);} }

二十九:[LeetCode] 64. 最小路徑和

給定一個(gè)包含非負(fù)整數(shù)的 m x n 網(wǎng)格 grid ,請找出一條從左上角到右下角的路徑,使得路徑上的數(shù)字總和為最小。?

說明:每次只能向下或者向右移動(dòng)一步。?

示例 1:?
輸入:grid = [[1,3,1]
? ? ? ? ? ? ,[1,5,1],
? ? ? ? ? ? ?[4,2,1]]
輸出:7
解釋:因?yàn)槁窂?1→3→1→1→1 的總和最小。
?
示例 2:?
輸入:grid = [[1,2,3],[4,5,6]]
輸出:12

提示:?
遞歸的話,結(jié)束條件時(shí)什么
到達(dá)右下角,則結(jié)束, ?結(jié)束之后返回值,因?yàn)橹荒芟蜃蠡蛘呤窍蛴?#xff0c;則進(jìn)行比較,左右做小值
?m == grid.length?
?n == grid[i].length?
?1 <= m, n <= 200?
?0 <= grid[i][j] <= 100?
?
?Related Topics 數(shù)組 動(dòng)態(tài)規(guī)劃
?
?題解:
兩種解法: ?
?? ??? ?一種是遞歸,會(huì)超時(shí).....................,每次遞歸判斷向左向右的最小值,并進(jìn)行求值
?? ??? ?二種是動(dòng)態(tài)規(guī)劃,第dp[i][j]代表走到i,j所走的最小路徑,那么這個(gè)最小路徑和和dp[i-1][j],dp[i][j-1]有關(guān)系的,
?? ??? ? ? ? 具體的最小值為dp[i][j] = Math.max(dp[i-1][j], ?dp[i][j-1]) + grid[i][j];

注意:

? ? ? ? 對于臨界點(diǎn)的判斷,因?yàn)閕=0的話,你再i-1就會(huì)出錯(cuò)了,這個(gè)要特別注意

class Solution {public int minPathSum(int[][] grid) {if(grid == null){return 0;}int n = grid.length;int m = grid[0].length;int[][] dp = new int[n][m];for(int i=0; i<n; i++){for(int j=0; j<m; j++){if(i == 0 && j == 0){dp[i][j] = grid[i][j];}else if(i == 0) {//進(jìn)入這里,i==0,那么j肯定不為0dp[i][j] = dp[i][j-1]+grid[i][j];}else if(j == 0){//進(jìn)入這里,j==0,那么i肯定不為0dp[i][j] = dp[i-1][j]+grid[i][j];}else{dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];}}}return dp[n-1][m-1];}// return recurseReachMinPathSum(grid, 0, 0);//這里解法會(huì)超時(shí)的,不用看public int recurseReachMinPathSum(int[][]grid, int i, int j){//結(jié)束條件if(i==grid.length-1 && j==grid[0].length-1){return grid[i][j];}int down = Integer.MAX_VALUE;int right = Integer.MAX_VALUE;//條件判斷,剪紙if(i < grid.length-1){down = recurseReachMinPathSum(grid, i+1, j);}if(j < grid[0].length-1){right = recurseReachMinPathSum(grid, i, j+1);}return Math.min(down, right) + grid[i][j];} }

三十:[LeetCode] 115. 最小棧

設(shè)計(jì)一個(gè)支持 push ,pop ,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。?

push(x) —— 將元素 x 推入棧中。?
pop() —— 刪除棧頂?shù)脑亍?
top() —— 獲取棧頂元素。?
getMin() —— 檢索棧中的最小元素。?
?
示例:?

輸入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[], ? ? ? ?[-2], ? [0], ? [-3], ? ?[], ? ?[], ? [], ? ? []]

輸出:
[null, ? ? ? null, ? null, ?null, ? -3, ? ?null, ?0, ? ? -2]

解釋:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); ? --> 返回 -3.
minStack.pop();
minStack.top(); ? ? ?--> 返回 0.
minStack.getMin(); ? --> 返回 -2.

提示:?
pop、top 和 getMin 操作總是在 非空棧 上調(diào)用。?
Related Topics 棧 設(shè)計(jì)

題解:

? ? ? ?使用額外的一個(gè)棧保存當(dāng)前最小的數(shù),每當(dāng)一個(gè)元素進(jìn)棧時(shí),都會(huì)在兩個(gè)棧中各存放一個(gè)元素,最小棧中存放的是和之前的進(jìn)行比較,看哪個(gè)小,就將小的放入棧中,具體官方題解有動(dòng)態(tài)圖,這個(gè)看一下:https://leetcode-cn.com/problems/min-stack/solution/zui-xiao-zhan-by-leetcode-solution/

?

class MinStack {private LinkedList<Integer> stack;private LinkedList<Integer> minStack;/** initialize your data structure here. */public MinStack() {stack = new LinkedList<>();minStack = new LinkedList<>();}public void push(int x) {//如果為空,則直接存放進(jìn)去if(stack.size() == 0){minStack.addFirst(x);}else{//不為空,取出最小棧中的頭部,并進(jìn)行判斷大小,而且每次都會(huì)將比較//之后最小的,放入到最小棧中int temp = minStack.getFirst();if(temp > x){minStack.addFirst(x);}else{minStack.addFirst(temp);}}stack.addFirst(x);}public void pop() {stack.removeFirst();minStack.removeFirst();}public int top() {return stack.getFirst();}public int getMin() {return minStack.getFirst();} }

三十一:[LeetCode] 5. 最長回文子串

給你一個(gè)字符串 s,找到 s 中最長的回文子串。

?

示例 1:

輸入:s = "babad"
輸出:"bab"
解釋:"aba" 同樣是符合題意的答案。
示例 2:

輸入:s = "cbbd"
輸出:"bb"
示例 3:

輸入:s = "a"
輸出:"a"
示例 4:

輸入:s = "ac"
輸出:"a"
?

提示:

1 <= s.length <= 1000
s 僅由數(shù)字和英文字母(大寫和/或小寫)組成

題解:
??? ?

先給出官方題解,然后寫一下自己的理解,需要注意和深刻理解的點(diǎn):
?? ??? ?1.求dp[i][j]是否是回文串時(shí),需要dp[i+1][j-1]的狀態(tài)已知,而對于dp[i+1][j-1]的狀態(tài)也是需要它上面的狀態(tài)才行,所以,在進(jìn)行求值時(shí),我們需要先根據(jù)內(nèi)部的才能求出外部的,在
求解時(shí),需要根據(jù)長度進(jìn)行求取,先求出所有的回文子串長度為1的,然后再求出長度為2的,再求為3的
?? ??? ?2.所有循環(huán)時(shí),不是對i和j進(jìn)行循環(huán),外層對長度進(jìn)行循環(huán),內(nèi)層對開始位置i進(jìn)行循環(huán),根據(jù)i和長度來確定j的位置,這樣進(jìn)行求解

class Solution {public String longestPalindrome(String s) {String res = "";int length = s.length();boolean[][] dp = new boolean[length][length];//len代表當(dāng)前所遍歷的長度,因?yàn)橹挥衐p[i][j]判斷的條件是,dp[i+1][j-1]的狀態(tài)已知,而對于dp[i+1][j-1]的//狀態(tài)怎么得知的,所以,在進(jìn)行求值時(shí),是根據(jù)長度進(jìn)行的,先求出長度為3的,上面的那個(gè)循環(huán)已經(jīng)求得了長度為1和2是不是//回文串了,那就此時(shí)就從3開始判斷了for(int len=1; len<=length; len++){//我們判斷時(shí),是從i開始的,如果i+len如果大于length,則說明本次已不滿足條件了,因?yàn)?#xff0c;我們遍歷時(shí),是先根據(jù)長度進(jìn)行循環(huán)的,for(int i=0; i+len<=length; i++){//i+len-1,表示當(dāng)前遍歷的j在哪個(gè)位置,因?yàn)?#xff0c;本次判斷的長度為len,我們開始位置為i,那么j的位置就是i+len了,因?yàn)橄聵?biāo)//從0開始,則需要-1,就找到了j的位置//始終記住dp[i][j],表示的是從第i為到第j為是不是一個(gè)回文串,他們之間的間隔為lenint j = i + len - 1;if(len == 1){dp[i][j] = true;}else if(len == 2){dp[i][j] = (s.charAt(i) == s.charAt(j));}else if(s.charAt(i)==s.charAt(j) && dp[i+1][j-1]){dp[i][j] = true;}//滿足條件,則查看是否需要更新最大值if(dp[i][j] && res.length()<len){res = s.substring(i, j+1);}}}return res;} }

三十二:[LeetCode] 647. 回文子串

給定一個(gè)字符串,你的任務(wù)是計(jì)算這個(gè)字符串中有多少個(gè)回文子串。?

具有不同開始位置或結(jié)束位置的子串,即使是由相同的字符組成,也會(huì)被視作不同的子串。?

示例 1:?
輸入:"abc"
輸出:3
解釋:三個(gè)回文子串: "a", "b", "c"

示例 2:?
輸入:"aaa"
輸出:6
解釋:6個(gè)回文子串: "a", "a", "a", "aa", "aa", "aaa"?

提示:?
輸入的字符串長度不會(huì)超過 1000 。?
Related Topics 字符串 動(dòng)態(tài)規(guī)劃

題解:
?? ??? ?需要使用動(dòng)態(tài)規(guī)劃,思路和leetcode第5題思路是一樣的,主要的還是對于動(dòng)態(tài)轉(zhuǎn)移方式的判斷,和條件

需要注意的點(diǎn):
?? ??? ?1.dp[i][j]表示從數(shù)組下標(biāo)i到數(shù)組下標(biāo)j是否是一個(gè)回文串,它的轉(zhuǎn)移方程為dp[i][j] = (arr[i] == arr[j] && dp[i+1][j-1] == true)
?? ??? ?2.求解時(shí),根據(jù)長度進(jìn)行求解,讓回文字串不斷增大,先從長度為1 到長度為length進(jìn)行判斷

class Solution {public int countSubstrings(String s) {if(s == null || s.length() == 0){return 0;}int length = s.length();int res = 0;boolean[][] dp = new boolean[length][length];//len表示當(dāng)前的長度,先從1開始判斷,也就是判斷abc中的a是不是回文串//等長度為1的判斷完成后,判斷2的,如ab,bc是不是,再判斷長度為3的//如abc是不是for(int len=1; len<=length; len++){for(int i=0; i+len<=length; i++){//i+len-1,表示結(jié)束位置jint j = i + len -1;if(len == 1){dp[i][j] = true;}else if(len == 2){dp[i][j] = s.charAt(i) == s.charAt(j);}else if(s.charAt(i) == s.charAt(j) && dp[i+1][j-1]){dp[i][j] = true;}if(dp[i][j]){res++;}}}return res;} }

三十三:[LeetCode] 33. 搜索旋轉(zhuǎn)排序數(shù)組

整數(shù)數(shù)組 nums 按升序排列,數(shù)組中的值 互不相同 。?

在傳遞給函數(shù)之前,nums 在預(yù)先未知的某個(gè)下標(biāo) k(0 <= k < nums.length)上進(jìn)行了 旋轉(zhuǎn),使數(shù)組變?yōu)?[nums[k], nums[
k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下標(biāo) 從 0 開始 計(jì)數(shù))。例如, [0,1,2
,4,5,6,7] 在下標(biāo) 3 處經(jīng)旋轉(zhuǎn)后可能變?yōu)?[4,5,6,7,0,1,2] 。?

給你 旋轉(zhuǎn)后 的數(shù)組 nums 和一個(gè)整數(shù) target ,如果 nums 中存在這個(gè)目標(biāo)值 target ,則返回它的索引,否則返回 -1 。?

示例 1:?
輸入:nums = [4,5,6,7,0,1,2], target = 0
輸出:4

示例 2:?
輸入:nums = [4,5,6,7,0,1,2], target = 3
輸出:-1?

示例 3:?
輸入:nums = [1], target = 0
輸出:-1

提示:?
1 <= nums.length <= 5000?
-10^4 <= nums[i] <= 10^4?
nums 中的每個(gè)值都 獨(dú)一無二?
nums 肯定會(huì)在某個(gè)點(diǎn)上旋轉(zhuǎn)?
-10^4 <= target <= 10^4?

進(jìn)階:你可以設(shè)計(jì)一個(gè)時(shí)間復(fù)雜度為 O(log n) 的解決方案嗎??
Related Topics 數(shù)組 二分查找

題解:

? ? ? ?使用二分進(jìn)行查詢,當(dāng)將數(shù)組一分為二時(shí),肯定有一份是從小到大排序的,這個(gè)是可以肯定的,因?yàn)榇祟}有個(gè)前提-------數(shù)組中的值互不相同,所以,我們解題的關(guān)鍵是找到哪一半是升序的,然后依次判斷這個(gè)target是否在這個(gè)升序排列中,如果不存在,那么就在另一份了,所以此題也是二分的思想

class Solution {public int search(int[] nums, int target) {if(nums == null || nums.length == 0){return -1;}if(nums.length == 1){return nums[0] == target ? 0 : -1;}int left = 0;int right = nums.length-1;int mid = 0;while(left <= right){mid = (left + right)/2;if(nums[mid] == target){return mid;}//從有序的一方進(jìn)行判斷if(nums[left] <= nums[mid] ){//左邊有序if(nums[left] <= target && nums[mid] > target){right = mid - 1;}else{left = mid + 1;}}else{if(nums[mid] < target && nums[right] >= target){left = mid + 1;}else{right = mid - 1;}}}return -1;} }

三十四:[LeetCode] 56. 合并區(qū)間

以數(shù)組 intervals 表示若干個(gè)區(qū)間的集合,其中單個(gè)區(qū)間為 intervals[i] = [starti, endi] 。請你合并所有重疊的區(qū)間,并返
回一個(gè)不重疊的區(qū)間數(shù)組,該數(shù)組需恰好覆蓋輸入中的所有區(qū)間。?

示例 1:?
輸入:intervals = [[1,3],[2,6],[8,10],[15,18]]
輸出:[[1,6],[8,10],[15,18]]
解釋:區(qū)間 [1,3] 和 [2,6] 重疊, 將它們合并為 [1,6].
?
示例 2:?
輸入:intervals = [[1,4],[4,5]]
輸出:[[1,5]]
解釋:區(qū)間 [1,4] 和 [4,5] 可被視為重疊區(qū)間。?

提示:?
1 <= intervals.length <= 104?
intervals[i].length == 2?
0 <= starti <= endi <= 104?
?
Related Topics 排序 數(shù)組

題解:

? ? ? 進(jìn)行合并的區(qū)域,需要先進(jìn)行排序,因?yàn)?#xff0c;可能給的數(shù)組不是從小到達(dá)排序的,排序時(shí),按照開始節(jié)點(diǎn)進(jìn)行排序,例如給的用例為[[2,3],[4,5],[6,7],[8,9],[1,10]],此時(shí)就需要先進(jìn)行排序了,再排序之后我們就可以發(fā)現(xiàn)規(guī)律了,如

[[1,10],[2,3],[4,5],[6,7],[8,9]],此時(shí)如果需要進(jìn)行排序,那么肯定是當(dāng)前遍歷元素的開始比前一個(gè)元素的結(jié)束要小,此時(shí)就要進(jìn)行合并,而且,再查找一個(gè)最大值,作為結(jié)束。

class Solution {public int[][] merge(int[][] intervals) {if(intervals == null || intervals.length == 1){return intervals;}ArrayList<int[]> list = new ArrayList<>();//從小到大進(jìn)行排序,按照起始節(jié)點(diǎn)進(jìn)行排序Arrays.sort(intervals, (a,b) -> a[0] - b[0]);list.add(intervals[0]);for(int i=1; i<intervals.length; i++){//取出最后一個(gè)放入的元素,進(jìn)行判斷int[] temp = list.get(list.size() - 1);//如果當(dāng)前遍歷的開始,比列表中最后一個(gè)要小,也就是列表中最后一個(gè)更大,//此時(shí)需要加入到其中,并且更新列表中最后一個(gè)數(shù)的結(jié)尾,查看哪個(gè)數(shù)更大一點(diǎn)if(intervals[i][0] <= temp[1]){temp[1] = Math.max(intervals[i][1], temp[1]);}else{list.add(intervals[i]);}}return list.toArray(new int[][]{});} }

三十五:[LeetCode] 160. 相交鏈表

編寫一個(gè)程序,找到兩個(gè)單鏈表相交的起始節(jié)點(diǎn)。

例如,下面的兩個(gè)鏈表:

A: ? ? ? ? ?a1 → a2
? ? ? ? ? ? ? ? ? ?↘
? ? ? ? ? ? ? ? ? ? ?c1 → c2 → c3
? ? ? ? ? ? ? ? ? ?↗ ? ? ? ? ? ?
B: ? ? b1 → b2 → b3
在節(jié)點(diǎn) c1 開始相交。

注意:

如果兩個(gè)鏈表沒有交點(diǎn),返回?null.
在返回結(jié)果后,兩個(gè)鏈表仍須保持原有的結(jié)構(gòu)。
可假定整個(gè)鏈表結(jié)構(gòu)中沒有循環(huán)。
程序盡量滿足 O(n) 時(shí)間復(fù)雜度,且僅用 O(1) 內(nèi)存。
思路:如果沒有?O(n) 時(shí)間復(fù)雜度,且僅用 O(1) 內(nèi)存的條件,這個(gè)題很容器寫,但是因?yàn)閷τ跁r(shí)間復(fù)雜度和空間復(fù)雜度有要求,所以,我們應(yīng)該再做思考,我們知道,如果兩個(gè)鏈表相交,從相交點(diǎn)開始,以后的元素都是相同的,我們設(shè)A鏈表中,不重復(fù)的為listA,設(shè)B鏈表中,不重復(fù)的為listB,相同的那部分為AequalB。

????????那么就有l(wèi)istA+AequalB+listB=listB+AequalB+listA,當(dāng)然這也是顯然易見的,所以我們就可以找到交點(diǎn)了,

????????例如,對于A鏈表,我們a1 → a2 → c1→c2 → c3→b1 → b2 → b3→c1

???????????????????對于B鏈表,我們b1 → b2 → b3→c1 → c2 → c3→a1 → a2→c1

?????????可以發(fā)現(xiàn),我們從A鏈表開始,當(dāng)A鏈表遍歷完后,再從B鏈表頭節(jié)點(diǎn)找,?對于B,我們從B鏈表開始,當(dāng)B鏈表遍歷完后,再從A鏈表頭節(jié)點(diǎn)找,這樣會(huì)在相交的地方,兩值相等。
?

注意:

? ? ? ?分為兩種情況,如果A和B鏈一樣長,那么第一次遍歷時(shí),就直接a == b == null了,如果A和B鏈不一樣長,那么第一次遍歷時(shí),就直接a == null時(shí),b一定不是null,那么就需要進(jìn)行a = headB; b = headA的判斷

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA==null || headB==null){return null;}//分為兩種情況: 如果A和B鏈一樣長,那么第一次遍歷時(shí),就直接a == b == null了// 如果A和B鏈不一樣長,那么第一次遍歷時(shí),就直接a == null時(shí),b一定不是null,那么就需要進(jìn)行a = headB; b = headA的判斷//a -> b//c -> d, 如果A和B鏈一樣長,那么第一次遍歷時(shí),就直接a == b == null了,所以直接退出,如果A,B鏈不想等,那么的話,才需要進(jìn)行二次遍歷//如a -> b//c -> d -> e, 只有A,B鏈不同時(shí)為null時(shí),才會(huì)走掃描第二次,也就是a = headB; b = headA;才有用ListNode a = headA;ListNode b = headB;while(a != b){if(a == null){a = headB;}else{a = a.next;}if(b == null){b = headA;}else{b = b.next;}}return a;} }

三十六:[LeetCode] 31. 下一個(gè)排列

實(shí)現(xiàn)獲取 下一個(gè)排列 的函數(shù),算法需要將給定數(shù)字序列重新排列成字典序中下一個(gè)更大的排列。?

如果不存在下一個(gè)更大的排列,則將數(shù)字重新排列成最小的排列(即升序排列)。?

必須 原地 修改,只允許使用額外常數(shù)空間。?

示例 1:?
輸入:nums = [1,2,3]
輸出:[1,3,2]
?
示例 2:?
輸入:nums = [3,2,1]
輸出:[1,2,3]
?
示例 3: ?
輸入:nums = [1,1,5]
輸出:[1,5,1]
?
示例 4:?
輸入:nums = [1]
輸出:[1]
?
提示:?
1 <= nums.length <= 100?
0 <= nums[i] <= 100?
Related Topics 數(shù)組

題解:

? ? ? ? ?首先你要知道什么是字典樹,其次你要很清楚怎么根據(jù)一個(gè)字典樹,找到他的下一個(gè)排列的字典樹。這兩點(diǎn)清楚的話,這題并不難,主要的話,還是要理解字典樹,詳細(xì)的可以參考這篇:https://blog.csdn.net/qq_33594380/article/details/82377923。

1.字典序值

? ? ? ? ?字典序值就是當(dāng)前序列在字典序中的排列位置。那給定一個(gè)排列,我們應(yīng)該如何求出它的字典序值呢?

為了求排列對應(yīng)的序號,只要該序列到第一個(gè)序列即1,2,3…n?所需要移動(dòng)的次數(shù)。

移動(dòng)原則是a[i] 從大到小移動(dòng),對于每一個(gè)數(shù)字a[i],若i前面比a[i] 小的個(gè)數(shù)正好是a[i] - 1 個(gè),則這個(gè)數(shù)不需要向后移動(dòng)以達(dá)到目標(biāo)序列,否則i 后面必然有a[i] - t - 1 (此處t 的值為a[i] 之前比a[i] 小的數(shù)字的個(gè)數(shù))個(gè)比a[i] 小的數(shù),只要把a(bǔ)[i] 移動(dòng)到比自己小的數(shù)后面才能使得移動(dòng)中的序列正向目標(biāo)前進(jìn)。

因此只要求出每個(gè)數(shù)的移動(dòng)次數(shù),然后相加就是該序列的位置。即每個(gè)數(shù)到正確位置需要移動(dòng)的次數(shù)為

—— 這一段引自?https://blog.csdn.net/hello_tomorrow_111/article/details/78696294?并略作優(yōu)化

啥意思,有點(diǎn)模糊,舉個(gè)栗子來解釋一下。我們就以第一點(diǎn)中提到的[1, 2, 3] 的字典序?yàn)槔?#xff0c;我現(xiàn)在想知道321 在序列中的位置應(yīng)該怎么計(jì)算呢?

(1)找到該序列的第一個(gè)序列,即123

(2)從序列左邊開始,查找每個(gè)值應(yīng)該移動(dòng)的位數(shù)。

首先來看3,與第一個(gè)序列相比,3 之前本來應(yīng)該有兩個(gè)元素12,但是現(xiàn)在它前面沒有比它小的元素,所以它要移動(dòng)3 - 1 - 0 (a[i] - t - 1),即兩位到達(dá)正序位置。
接著來看2,2 之前應(yīng)該是有一個(gè)元素1,但是它前面也沒有比它小的元素,所以它要向后移動(dòng)2 - 1 - 0,即一位來達(dá)到正序。
對于1 來說,它是排列中最小的元素,它之前不應(yīng)該有元素,而事實(shí)也是如此,所以它不需要移動(dòng)。
將3 和2 移動(dòng)的次數(shù)相加就是321 在字典序中的字典序值。那么3 和2 分別需要移動(dòng)多少次才能移動(dòng)兩位或者一位呢,以3 為例來看一下:

321 中的3 往后移動(dòng)兩位需要經(jīng)歷以下流程(回退):231、213、132、123。

2. 下一個(gè)序列
字典序值說完了,說說下一個(gè)序列,下一個(gè)序列求解的思想非常簡單:

(1)從右向左找到第一個(gè)左臨小于右臨的位置,左臨的位置標(biāo)記為i

(2)從右向左找到第一個(gè)大于a[i] 的數(shù)值,位置為j

(3)交換a[i] 與a[j] 的值

(4)將i 右邊的部分排成正序

如果看這個(gè)流程看不懂的話,建議去看Leetcode 上的動(dòng)圖,保證看幾遍就懂:

https://leetcode-cn.com/problems/next-permutation/solution/

Say sth more (簡稱SM): 為什么要這么做呢,提供一個(gè)栗子結(jié)合理解,假設(shè)我們要求15499 + 1 的和,這個(gè)應(yīng)該怎么算呢?

解法是:從右向左找到第一個(gè)不為9 的數(shù)值,將該數(shù)值加1,然后該數(shù)值以后所有的9 都變成0。此例可以與求解下一個(gè)序列的過程結(jié)合理解。

class Solution {public void nextPermutation(int[] nums) {if(nums == null || nums.length == 1){return;}int length = nums.length;int ans = -1;//1 2 3 8 7 6 5 4// i-1 i//1, 2, 3// ans//第一步.從后向前找第一個(gè),a[i] > a[i-1]的數(shù)for(int i=length-1; i>0; i--){if(nums[i] > nums[i-1]){ans = i-1;//break語句不能忘記,不然就錯(cuò)了......break;}}if(ans == -1){//此時(shí)不存在下一個(gè)更大的排列,則將數(shù)字重新排列成最小的排列reverseArray(nums, 0, length-1);}else{for(int i=length-1; i>=ans; i--){if(nums[i] > nums[ans]){//第二步,元素的交換swapNum(nums, i, ans);//第三步,指定的數(shù)據(jù)反轉(zhuǎn)reverseArray(nums, ans+1, length-1);break;}}}}//交換元素private void swapNum(int[] nums, int i, int j){int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}//反轉(zhuǎn)指定下標(biāo)范圍內(nèi)的元素private void reverseArray(int[] nums, int l, int r){while(l<r){swapNum(nums, l, r);l++;r--;}} }

?

總結(jié)

以上是生活随笔為你收集整理的LeetCode整理----合集一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。