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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

剑指offer和LeetCode题目笔记

發布時間:2024/9/30 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 剑指offer和LeetCode题目笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 劍指offer
    • 一、數組
    • 1. 數組中重復的數字
    • 2.二維數組中的查找
      • 多個if和if,elseif語句的區別
    • 3.旋轉數組的最小數字
    • 4.構建乘積數組
    • 5.把數組排成最小的數
    • 6.矩陣中的路徑
    • 7.調整數組順序使奇數位于偶數前面
    • 8.順時針打印矩陣
    • 9.棧的壓入、彈出序列
    • 10.數組中出現次數超過一半的數字
    • 11.最小的k個數
    • 13. 在排序數組中查找數字 I
    • 14. 0~n-1中缺失的數字
    • 15.數組中數字出現的次數
    • 16. 和為s的兩個數字
    • 17.撲克牌中的順子
    • 18.數組中的逆序對
    • 動態規劃
    • 1.連續子數組的最大和
  • 鏈表
    • 反轉鏈表
  • 二叉樹
    • 二叉樹的鏡像
    • 二叉搜索樹的第k大節點
    • 二叉樹的最近公共祖先

劍指offer

一、數組

1. 數組中重復的數字


我的簡單思路:先排序,再輸出答案,但是效率不高。

import java.util.Arrays; class Solution {public int findRepeatNumber(int[] nums) {Arrays.sort(nums);int a=0;for(int i=0;i<nums.length-1;i++){if(nums[i]==nums[i+1]){a=nums[i];}}return a;} }

下面是最好的思路:原地交換。數組元素的 索引 和 值 是 一對多 的關系。遍歷數組并通過交換操作,使元素的 索引 與 值 一一對應(即 nums[i] = inums[i]=i )。因而,就能通過索引映射對應的值,起到與字典等價的作用。

時間復雜度 O(N) : 遍歷數組使用 O(N)O(N) ,每輪遍歷的判斷和交換操作使用 O(1)O(1) 。
空間復雜度 O(1) : 使用常數復雜度的額外空間。

代碼:遍歷數組
1.所遍歷的數字=所在索引值,就遍歷下一個;
2.所遍歷的數字≠所在索引值,就和它該在的索引的數字進行交換;
3.所遍歷的數字=它該在的索引的數,則得出結果。

class Solution {public int findRepeatNumber(int[] nums) {for(int i=0;i<nums.length;i++){if(nums[i]==i){continue;}if(nums[i]==nums[nums[i]]){return nums[i];}int temp;temp=nums[i];nums[i]=nums[temp];nums[temp]=temp; }return -1;} }

2.二維數組中的查找


思路:從右上角開始。對于每個元素,其左分支元素更小、右分支元素更大。

復雜度分析:

時間復雜度 O(M+N)O(M+N) :其中,NN 和 MM 分別為矩陣行數和列數,此算法最多循環 M+NM+N 次。

空間復雜度 O(1)O(1) : i, j 指針使用常數大小額外空間。

多個if和if,elseif語句的區別

①if無論是否滿足條件都會向下執行,知道程序結束,else if 滿足一個條件就會停止執行。
②由于if都會執行一遍,則可能會同一個需要判斷的事件,會進入2個if語句中,出現錯誤,而else if就不會發生這樣的事情。

class Solution {public boolean findNumberIn2DArray(int[][] matrix, int target) {int i = matrix.length - 1, j = 0;while(i >= 0 && j < matrix[0].length){if(matrix[i][j] == target){return true;}if(matrix[i][j] > target) i--;else if(matrix[i][j] < target) j++;//換成if就報錯}return false;} }

3.旋轉數組的最小數字


思路:普通的太簡單了,使用二分查找。注意性質(下圖一目了然)。

循環二分: 設 m = (i + j) / 2m=(i+j)/2 為每次二分的中點( “/” 代表向下取整除法,因此恒有 i≤m<j ),可分為以下三種情況:
①當 nums[m] > nums[j]時: m一定在 左半,最小值一定在 [m+1,j] 閉區間內,因此執行 i = m + 1;
②當 nums[m] < nums[j] 時: m 一定在 右半,最小值 一定在[i, m]閉區間內,因此執行 j = m;
③當 nums[m] = nums[j] 時: 無法判斷 m 在哪個排序數組中,即無法判斷旋轉點 x 在 [i, m還是 [m + 1, j]區間中。解決方案: 執行 j = j - 1縮小判斷范圍

class Solution {public int minArray(int[] numbers) {int i = 0, j = numbers.length - 1;while (i < j) {int mid = (i + j) / 2;if (numbers[mid] > numbers[j]) {i = mid + 1;}else if (numbers[mid] < numbers[j]){j = mid;} else j--;}return numbers[i];}}

補充思考: 為什么本題二分法不用 nums[m]和 nums[i]作比較?

二分目的是判斷 m 在哪個排序數組中,從而縮小區間。而在 nums[m] > nums[i]情況下,無法判斷 m 在哪個排序數組中。本質上是由于 j 初始值肯定在右排序數組中; i 初始值無法確定在哪個排序數組中。舉例如下:

對于以下兩示例,當 i = 0, j = 4, m = 2時,有 nums[m] > nums[i] ,而結果不同。
[1, 2, 3, 4 ,5]旋轉點 x = 0 : m 在右排序數組(此示例只有右排序數組);
[3, 4, 5, 1 ,2] 旋轉點 x = 3 : m 在左排序數組。

4.構建乘積數組

思路:把圖畫出來就知道了。

class Solution {public int[] constructArr(int[] a) {int length=a.length;if(length==0){return a;}int[] b=new int[length];int temp=1;b[0]=1;int i=0;for(i=1;i<length;i++){b[i]=b[i-1]*a[i-1];看圖易知B2=B1*A1}for(i=length-2;i>=0;i--){temp=temp*a[i+1];b[i]=b[i]*temp;}return b;} }

5.把數組排成最小的數


Arrays.sort(Object[] oj1,new SortComparator()):這種方式能夠對引用類型數組,按照Comparator中聲明的compare方法對對象數組進行排序.
lamda表達式

compareTo()方法
增強for循環

相對于for(;;)而言 增強for循環有兩個好處:
1.寫起來簡單
2.遍歷集合、容器簡單

//沒弄明白 class Solution {public String minNumber(int[] nums) {String[] strs = new String[nums.length];for(int i = 0; i < nums.length; i++)strs[i] = String.valueOf(nums[i]);//String.valueOf()把基本類型轉換成string類型Arrays.sort(strs, (x, y) -> (x + y).compareTo(y + x));//自定義排序需要深入研究一下,還有lamda表達式StringBuilder res = new StringBuilder();//StringBuilder非線程安全但是效率高for(String s : strs)//增強for循環res.append(s);return res.toString();//轉換成string類型} }

6.矩陣中的路徑

class Solution {public boolean exist(char[][] board, String word) {char[] words = word.toCharArray();//String轉換成char數組// 遍歷圖for(int i = 0; i < board.length; i++) {for(int j = 0; j < board[0].length; j++) {// 如果找到了,就返回true。否則繼續找if(dfs(board, words, i, j, 0)) {return true;}}}// 遍歷結束沒找到falsereturn false;}boolean dfs(char[][] board, char[] word, int i, int j, int k) {// 判斷傳入參數的可行性: i 與圖行數row比較,j與圖列數col比較,i,j初始都是0,都在圖左上角// k是傳入字符串當前索引,一開始是0,如果當前字符串索引和圖當前索引對應的值不相等,表示第一個數就不相等// 所以繼續找第一個相等的數。題目說第一個數位置不固定,即路徑起點不固定(不一定是左上角為第一個數)// 如果board[i][j] == word[k],則表明當前找到了對應的數,就繼續執行(標記找過,繼續dfs 上下右左) //board[i][j] != word[k]要放里面否則會有越界報錯if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]){return false;} // 表示每個字符都找到了// 一開始k=0,而word.length肯定不是0,所以沒找到,就執行dfs繼續找。if(k == word.length - 1) return true; // 訪問過的暫時標記空字符串:不標記會導致搜回去,//“ ”是空格 '\0'是空字符串,不一樣的!board[i][j] = '\0';// 順序是 上下 右 左(這四個順序順便);上面找到了對應索引的值所以k+1boolean res = dfs(board, word, i-1, j, k + 1) || dfs(board, word, i +1, j, k + 1) || dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);// 還原找過的元素,因為之后可能還要訪問它的其他路徑board[i][j] = word[k];// 返回結果,如果false,則if(dfs(board, words, i, j, 0)) return true;不會執行,就會繼續找return res;} }

7.調整數組順序使奇數位于偶數前面


考慮定義雙指針 i , j 分列數組左右兩端,循環執行:

指針 i 從左向右尋找偶數;
指針 j 從右向左尋找奇數;
將 偶數nums[i] 和 奇數 nums[j] 交換。
可始終保證: 指針 i 左邊都是奇數,指針 j 右邊都是偶數 。

class Solution {public int[] exchange(int[] nums) {int i = 0, j = nums.length - 1, tmp;while(i < j) {while(i < j && (nums[i] %2) == 1){i++;} while(i < j && (nums[j] %2 ) == 0) {j--;}tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}return nums;} }

8.順時針打印矩陣


思路:四個邊界,注意最后一個元素可能會重復讀取

class Solution {public int[] spiralOrder(int[][] matrix) {if(matrix.length == 0) return new int[0];int a = 0, d = matrix[0].length - 1, w = 0, s = matrix.length - 1, x = 0;int i,j,temp=0;int[] sum = new int[(d + 1) * (s + 1)];while(true){for(j=a;j<=d;j++){// a to d.sum[temp++]=matrix[w][j];}w++;if(temp >= sum.length) break;//每個for循環因為有等號,所以到了最后會重復加進去,所以要加這個for(i=w;i<=s;i++){// w to s.sum[temp++]=matrix[i][d];}d--;if(temp >= sum.length) break;for(j=d;j>=a;j--){// d to a.sum[temp++]=matrix[s][j];}s--;if(temp >= sum.length) break;for(i=s;i>=w;i--){// s to w.if(++a > d) break;sum[temp++]=matrix[i][a];}a++;if(temp >= sum.length) break; }return sum;} }

9.棧的壓入、彈出序列

class Solution {public boolean validateStackSequences(int[] pushed, int[] popped) {Stack<Integer> stack = new Stack<>();int j=0;for(int i=0;i<pushed.length;i++){stack.push(pushed[i]);// 進棧while(!stack.isEmpty()&&stack.peek()==popped[j]){// 棧頂元素和出棧的元素相等就出棧,不相等繼續進棧stack.pop();j++;//出棧就往后遍歷poped}}return stack.isEmpty();} }

10.數組中出現次數超過一半的數字

核心就是對拼消耗

假設有一個擂臺,有一組人,每個人有編號,相同編號為一組,依次上場,沒人時上去的便是擂主(x),若有人,編號相同則繼續站著(人數+1),若不同,假設每個人戰斗力相同,都同歸于盡,則人數-1;那么到最后站著的肯定是人數占絕對優勢的那一組啦~

class Solution {public int majorityElement(int[] nums) {int persons=0;int leizhu=0;for(int i=0;i<nums.length;i++){if(persons==0){leizhu=nums[i];}persons=persons+(leizhu==nums[i]?1:-1);}return leizhu;} }

11.最小的k個數

思路:題目只要求返回最小的 k 個數,對這 k 個數的順序并沒有要求。因此,只需要將數組劃分為 最小的 k 個數 和 其他數字 兩部分即可,而快速排序的哨兵劃分可完成此目標。

根據快速排序原理,如果某次哨兵劃分后 基準數正好是第 k+1 小的數字 ,那么此時基準數左邊的所有數字便是題目所求的 最小的 k 個數 。

根據此思路,考慮在每次哨兵劃分后,判斷基準數在數組中的索引是否等于 k ,若 true 則直接返回此時數組的前 k 個數字即可。

《啊哈!算法》 關于快速排序法為什么一定要哨兵j 先出動的原因?
假如i先動,如果后面沒有大于基準的數就導致排序錯誤!!

class Solution {public int[] getLeastNumbers(int[] arr, int k) {if (k == arr.length) return arr;return quickSort(arr, k, 0, arr.length - 1);}public int[] quickSort(int[] arr, int k, int l, int r) {int i = l, j = r;//l:left,r:rigthtwhile (i < j) {while (i < j && arr[j] >= arr[l]) j--;//i<j控制越界,j先動,改成i就報錯了while (i < j && arr[i] <= arr[l]) i++;swap(arr, i, j);//快排思路,后面小于基準的和前面大于基準的要交換}swap(arr, i, l);//交換完了一輪基準,把基準放到該放的位置if (i > k) {//i > k時基準在k后面,所以在前一半找return quickSort(arr, k, l, i - 1);}if (i < k){//i < k時基準在k前面,所以在后一半找return quickSort(arr, k, i + 1, r);}return Arrays.copyOf(arr, k);//i==k時就可以返回前k個數}public void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;} }

13. 在排序數組中查找數字 I


思路:二分查找找右邊界,簡單來說就是找到第一個比target大的數的位置。

假如tar-1不存在呢?比如{1,2,3,5,7,7,7,8},tar=7,而tar-1=6并不在數組nums里邊?;乜吹诙€問題if(nums[m] <= tar)合并了兩個條件, 上邊說了nums[m] = tar,這里說說nums[m] < tar。其實tar-1存在與否并不重要,按照開頭的數組,設tar=6。
第一次i = 0,j = 7,m=3,nums[m]=5<tar;
第二次 i = 4,j = 7,m = 5,nums[m] = 7 > tar;
第三次i = 4,j = 4,m = 4,nums[m] = 7 >tar;
第四次 i = 4,j = 3,i>j 退出while,renturn 4,tar的右邊界為下標4。

為什么tar不存在不影響?因為helper方法是為了求右邊界(簡單來說就是找到第一個比target大的數的位置),tar不存在時,不考慮nums[m] == tar的判斷;nums[m] > tar,j = m-1;nums[m] < tar,i =m+1,最終返回的是tar的右邊界(return i 保證是返回右邊界,不可用return j)。所以tar的右邊界一定存在,即使tar不存在。

class Solution {public int search(int[] nums, int target) {//helper(nums, target):找到target的右邊界//helper(nums, target - 1):找到target-1的右邊界return helper(nums, target) - helper(nums, target - 1);//target-1不存在也可以找到重復數字的左邊界}int helper(int[] nums, int tar) {int i = 0, j = nums.length - 1;while(i <= j) {int m = (i + j) / 2;if(nums[m] <= tar){//找重復數字的右邊界i = m + 1;}if(nums[m] > tar){j = m - 1;//中間值大于目標值,就在左半找} }return i;} }

14. 0~n-1中缺失的數字


思路:

返回值: 跳出時,變量 i 和 j 分別指向 “右子數組的首位元素” 和 “左子數組的末位元素” 。因此返回 i 即可。

class Solution {public int missingNumber(int[] nums) {int i = 0, j = nums.length - 1;while(i <= j) {int mid = (i + j) / 2;if(nums[mid] == mid){//是正常數就找右半i = mid + 1;}else{j = mid - 1;//不是正常數就找左半} }return i;} }

15.數組中數字出現的次數


思路:回歸異或的本質,1^0 = 1, 1^1 = 0, 0^0 = 1。a^b的結果里,為1的位表明a與b在這一位上不相同。這個不相同很關鍵,不相同就意味著我們在結果里任選一位位1的位置i,所有數可以按照i位的取值(0,1)分成兩組,那么a與b必然不在同一組里。再對兩組分別累計異或。那么兩個異或結果就是a、b。

一個例子:5,3,2,3,4,5
1.遍歷異或:結果為2=0100異或4=0010,n=0110=6
2.使用&找出a與b在這一位上不相同,不相同就可以把a和b劃分開。先0110&0001=0,然后0110&0010=1,m=2
3,劃分成了[0010=2,0011=3],[0100=4,0101=5],兩個子數組異或結果為2,5

class Solution {public int[] singleNumbers(int[] nums) {int x = 0, y = 0, n = 0, m = 1;for(int num : nums) // 1. 遍歷異或,初始化為0,因為0異或a=a;n ^= num;while((n & m) == 0) // 2. 循環左移,計算出第一個二進制異或結果為1的m值,也就是這兩個值一個&m=0,另一個&m≠0,所以可以起到劃分作用m <<= 1;for(int num: nums) { // 3. 遍歷 nums 分組if((num & m) != 0) x ^= num; // 4. 當 num & m != 0else y ^= num; // 4. 當 num & m == 0}return new int[] {x, y}; // 5. 返回出現一次的數字} }class Solution {public int[] singleNumbers(int[] nums) {int n=0,m=1,x=0,y=0;for(int i=0;i<nums.length;i++){// 1. 遍歷異或,初始化為0,因為0異或a=a;n^=nums[i];}while((n&m)==0){//中間要加括號,因為判斷符先計算,循環左移,計算出第一個二進制異或結果為1的m值,也就是這兩個值一個&m=0,另一個&m≠0,所以可以起到劃分作用m=m<<1;}for(int i=0;i<nums.length;i++){if((nums[i]&m)!=0){x=x^nums[i];}else{y=y^nums[i];}}return new int[]{x,y};} }

16. 和為s的兩個數字

思路:就是簡單的雙指針

class Solution {public int[] twoSum(int[] nums, int target) {int i = 0, j = nums.length - 1;while(i < j) {int s = nums[i] + nums[j];if(s < target) i++;else if(s > target) j--;else return new int[] { nums[i], nums[j] };}return new int[0];} }

17.撲克牌中的順子


思路:為什么不直接寫成==4,因為可能是0,0,3, 4 ,5

class Solution {public boolean isStraight(int[] nums) {int joker = 0;Arrays.sort(nums); // 數組排序for(int i = 0; i < 4; i++) {if(nums[i] == 0) joker++; // 統計大小王數量else if(nums[i] == nums[i + 1]) return false; // 若有重復,提前返回 false}return nums[4] - nums[joker] < 5; // 最大牌 - 最小牌 < 5 則可構成順子} }

18.數組中的逆序對


思路:歸并排序中分治計算。
雙指針,因為子數組已經排好了序,每當遇到 左子數組當前元素 > 右子數組當前元素 時,意味著
「左子數組當前元素 至 左子數組末尾元素」 與 「右子數組當前元素」 構成了幾個 「逆序對」 。

class Solution {int count;//全局變量計數器public int reversePairs(int[] nums) {count=0;//默認返回值GB(nums,0,nums.length-1);return count;}public void GB(int[] nums,int left,int right){int mid=(left+right)/2;if(left<right){//沒有=,因為一個數字不需要排序GB(nums,left,mid);//處理左半GB(nums,mid+1,right);//處理右半GBpaixu(nums,left,mid,right);//歸并排序}}public void GBpaixu(int[] nums,int left,int mid,int right){int[] temparr=new int[right-left+1];//創建臨時數組存儲排序好的數int index=0;//臨時數組指針//定義兩個指針int temp1=left;int temp2=mid+1;while(temp1<=mid&&temp2<=right){//兩個指針要滿足的條件if(nums[temp1]<=nums[temp2]){//不是逆序temparr[index++]=nums[temp1];temp1++;//指針往后走}else{//是逆序count=count+(mid+1-temp1);//因為左邊已經排好序,所以左邊的后面全滿足temparr[index++]=nums[temp2];temp2++;//指針往后走}}//兩個子數組可能剩下數字while(temp1<=mid){temparr[index++]=nums[temp1++];}while(temp2<=right){temparr[index++]=nums[temp2++];}//把排好的臨時數組賦值到原始數組numsfor(int i=0;i<temparr.length;i++){nums[i+left]=temparr[i];}}}

動態規劃

1.連續子數組的最大和


思路:以某個數作為結尾,意思就是這個數一定會加上去,那么要看的就是這個數前面的部分要不要加上去。大于零就加,小于零就舍棄。

1.狀態定義: 設動態規劃列表 dp ,dp[i] 代表以元素 nums[i]為結尾的連續子數組最大和。

為何定義最大和 dp[i] 中必須包含元素 nums[i]:保證 dp[i]遞推到 dp[i+1] 的正確性;如果不包含 nums[i],遞推時則不滿足題目的 連續子數組 要求。
轉移方程: 若 dp[i?1]≤0 ,說明 dp[i?1] 對dp[i] 產生負貢獻,即 dp[i-1] + nums[i] 還不如 nums[i] 本身大。

①當 dp[i - 1] > 0 時:執行 dp[i] = dp[i-1] + nums[i] ;
②當 dp[i?1]≤0 時:執行 dp[i]=nums[i] ;
初始狀態: dp[0]=nums[0],即以 nums[0] 結尾的連續子數組最大和為 nnums[0] 。

返回值: 返回 dp 列表中的最大值,代表全局最大值。

class Solution {public int maxSubArray(int[] nums) {int max= nums[0];//最大值初始化為第一個數for(int i = 1; i < nums.length; i++) {if(nums[i-1]>0){//如果dp[i-1]>0,則dp[i]=dp[i-1]+nums[i] (代表以元素 nums[i]為結尾的連續子數組最大和。)nums[i]=nums[i]+nums[i-1];}if(nums[i]>max){//更新最大值max=nums[i];}}return max;} }

鏈表

反轉鏈表

題目:

答案:

public ListNode reverseList(ListNode head) {if(head==null){//如為空返回空return null;}if(head.next==null){//如只有一個頭結點就返回頭結點return head;}ListNode last=reverseList(head.next);head.next.next=head;head.next=null;return last;}

解析:
①ListNode last = reverse(head.next);

這個 reverse(head.next) 執行完成后,整個鏈表就成了這樣:

②head.next.next = head;


head.next = null;
return last;

二叉樹

二叉樹的鏡像

題目:請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。

答案:

遞歸解析:

  • 終止條件: 當節點 root 為空時(即越過葉節點),則返回 null;
  • 遞推工作:
    初始化節點 tmp ,用于暫存 root 的左子節點;
    開啟遞歸 右子節點 mirrorTree(root.right) ,并將返回值作為 root 的 左子節點 。
    開啟遞歸 左子節點 mirrorTree(tmp),并將返回值作為 root 的 右子節點 。
  • 返回值: 返回當前節點 root ;

當結點是葉子結點是就會return它自己。

復雜度分析:
時間復雜度 O(N): 其中 N 為二叉樹的節點數量,建立二叉樹鏡像需要遍歷樹的所有節點,占用O(N) 時間。
空間復雜度 O(N) : 最差情況下(當二叉樹退化為鏈表),遞歸時系統需使用 O(N)大小的??臻g。
代碼:

class Solution {public TreeNode mirrorTree(TreeNode root) {if(root == null) return null;TreeNode tmp = root.left;//先暫存左右結點都一樣root.left = mirrorTree(root.right);root.right = mirrorTree(tmp);return root;} }

二叉搜索樹的第k大節點

題目:給定一棵二叉搜索樹,請找出其中第k大的節點。

答案:求 “二叉搜索樹第 k 大的節點” 可轉化為求 “此樹的中序遍歷倒序的第 k 個節點”。

關鍵還是用類變量來維護k和res。類變量(也叫靜態變量)是類中獨立于方法之外的變量,用static 修飾。

public static int count=0, res=0;//形參k不能隨著dfs的迭代而不斷變化,為了記錄迭代進程和結果,引入類變量count和res。public int kthLargest(TreeNode root, int k) {count=k;//利用形參值k對類變量count進行初始化dfs(root);//這里不要引入形參k,dfs中直接使用的是初始值為k的類變量countreturn res; }public void dfs(TreeNode root){if(root==null||count==0) return;//當root為空或者已經找到了res時,直接返回dfs(root.right);if(--count==0){//先--,再判斷,比如count=3:2,1,0即代表第三個了res = root.val;return;//這里的return可以避免之后的無效迭代dfs(root.left);}dfs(root.left); }

錯誤做法:

public static int n;public int kthLargest(TreeNode root, int k) {if(root==null){return 0;}kthLargest(root.right,k);n=--k;//這樣做n的值一直不變,所以k要作為類變量if(n==0){res=root.val;}kthLargest(root.left,k);return res;}

二叉樹的最近公共祖先


答案:
根據以上定義,若 root 是p,q 的 最近公共祖先 ,則只可能為以下情況之一:

  • p 和 q 在 root 的子樹中,且分列 root 的 異側(即分別在左、右子樹中);
  • p=root ,且q 在 root 的左或右子樹中;
  • q=root ,且 p 在 root 的左或右子樹中;
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) return null; // 如果樹為空,直接返回nullif(root == p || root == q) return root; // 如果 p和q中有等于 root的,那么它們的最近公共祖先即為root(一個節點也可以是它自己的祖先)TreeNode left = lowestCommonAncestor(root.left, p, q); // 遞歸遍歷左子樹,只要在左子樹中找到了p或q,則先找到誰就返回誰TreeNode right = lowestCommonAncestor(root.right, p, q); // 遞歸遍歷右子樹,只要在右子樹中找到了p或q,則先找到誰就返回誰if(left == null) return right; // 如果在左子樹中 p和 q都找不到,則 p和 q一定都在右子樹中,右子樹中先遍歷到的那個就是最近公共祖先(一個節點也可以是它自己的祖先)else if(right == null) return left; // 否則,如果 left不為空,在左子樹中有找到節點(p或q),這時候要再判斷一下右子樹中的情況,如果在右子樹中,p和q都找不到,則 p和q一定都在左子樹中,左子樹中先遍歷到的那個就是最近公共祖先(一個節點也可以是它自己的祖先)else return root; //否則,當 left和 right均不為空時,說明 p、q節點分別在 root異側, 最近公共祖先即為 root} }

總結

以上是生活随笔為你收集整理的剑指offer和LeetCode题目笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。