国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

域名注冊平臺的網(wǎng)站怎么做頭條搜索

域名注冊平臺的網(wǎng)站怎么做,頭條搜索,專業(yè)做ppt的網(wǎng)站,ti外包網(wǎng)站建設(shè)文章目錄 1. 二叉樹的三種遍歷方式的實質(zhì)2. 二叉樹的序列化與反序列化3. 根據(jù)前序中序反序列創(chuàng)建二叉樹4. 二叉樹的路徑問題5. LCA公共祖先問題6. 二叉搜索樹的LCA問題7. 驗證搜索二叉樹8. 修建搜索二叉樹9. 二叉樹打家劫舍問題 1. 二叉樹的三種遍歷方式的實質(zhì) 這個相信大家都不…

文章目錄

    • 1. 二叉樹的三種遍歷方式的實質(zhì)
    • 2. 二叉樹的序列化與反序列化
    • 3. 根據(jù)前序中序反序列創(chuàng)建二叉樹
    • 4. 二叉樹的路徑問題
    • 5. LCA公共祖先問題
    • 6. 二叉搜索樹的LCA問題
    • 7. 驗證搜索二叉樹
    • 8. 修建搜索二叉樹
    • 9. 二叉樹打家劫舍問題

1. 二叉樹的三種遍歷方式的實質(zhì)

這個相信大家都不會陌生, 但是大家學習這個知識點的時候, 往往并不會在意這三種遍歷的順序到底有什么意義(一般都只會打印一下值即可), 而且對這個是怎么來的也不是很清晰, 下面我們通過代碼的注釋仔細解釋一下

	public static void dfs(TreeNode node){//遞歸的終止條件(其實也就是深度優(yōu)先搜索)//即當遞歸(其實也就是搜索到空節(jié)點的時候, 就直接結(jié)束(返回),但是該函數(shù)無返回信息)if(node == null){return;}//下面是第一次來到該節(jié)點的時機(對應直接對節(jié)點進行操作,前序)System.out.println(node.val);//往左子樹搜索dfs(node.left);//下面是第二次來到該節(jié)點的時機(左子樹搜索完畢之后進行操作,中序)System.out.println(node.val);//往右子樹搜索dfs(node.right);//下面是第三次來到該節(jié)點的時機(左右子樹都搜索完畢進行操作,后序)System.out.println(node.val);}

我們現(xiàn)在創(chuàng)建一顆樹, 樹的結(jié)構(gòu)是[1,2,3,4,null], 看一下上面的代碼的結(jié)果

	public static void main(String[] args) {TreeNode node1 = new TreeNode(1);TreeNode node2 = new TreeNode(2);TreeNode node3 = new TreeNode(3);TreeNode node4 = new TreeNode(4);node1.left = node2;node1.right = node3;node2.left = node4;dfs(node1);}運行結(jié)果是 1 2 4 4 4 2 2 1 3 3 3 1

我們仔細研究一下上面的打印結(jié)果
把每一個數(shù)字
第一次出現(xiàn)的結(jié)果提取出來也就是
1 2 4 3(前序)
第二次出現(xiàn)的結(jié)果提取出來也就是
4 2 1 3(中序)
第三次出現(xiàn)的結(jié)果提取出來也就是
4 2 3 1(后序)
上述的結(jié)果其實正式對應二叉樹中的遞歸序
也就是幾大遍歷順序的實質(zhì)
下面我們做題的時候要時刻分析我們的遍歷順序!

2. 二叉樹的序列化與反序列化

對于一個樹狀存儲的數(shù)據(jù)來說我們需要將其序列化轉(zhuǎn)換為文本文件(字符串)便于存儲與傳輸,然后在通過反序列化的方式將其還原出來, 值得一提的是, 反序列化只能操作前序或者后序或者層序的字符串,而不能操作中序的字符串, 原因是中序的字符串是不唯一的,比如下面這一行代碼

	public static void main(String[] args) {//第一棵樹TreeNode node1 = new TreeNode(1);TreeNode node2 = new TreeNode(1);node1.left = node2;//第二棵樹node1.right = node2;}

第一顆樹的序列化結(jié)果是 " # 1 # 1 # "
第二棵樹的序列化結(jié)果也是 " # 1 # 1 # "
但是二者不是同一棵樹, 所以中序的序列化是有問題的
首先展示的我們序列化的代碼(前序舉例子)

	//創(chuàng)建一個拼接字符的static StringBuilder sp = new StringBuilder();//序列化的過程其實就是前序遍歷輸出結(jié)果的時候進行拼接即可public static String creatStringUsePreOrder(TreeNode node){//遞歸終止條件if(node == null){sp.append("#,");return sp.toString();}sp.append(node.val + ",");//遞歸的返回值其實沒有被接收(//實質(zhì)上只有最高層級的結(jié)果被調(diào)用者接收了)creatStringUsePreOrder(node.left);creatStringUsePreOrder(node.right);return sp.toString();}

序列化的過程是比較簡單的,其實就是前序遍歷加上字符串的拼接, 那如何將字符串還原為一顆二叉樹呢, 下面是我們的實現(xiàn)(已經(jīng)用split方法去除連接的","而轉(zhuǎn)化為一個String[]數(shù)組)

//定義一個下標遍歷字符串(思考為什么定義到外側(cè))private static int index = 0;public static TreeNode creatTreeUsePreString(String[] s){//遞歸終止條件if(s[index++].equals("#")){return null;}//遞歸創(chuàng)建二叉樹//(前序遍歷的方案, 樹的連接其實是從底部連接的, 逐級返回)TreeNode root = new TreeNode(Integer.parseInt(s[index++]));root.left = creatTreeUsePreString(s);root.right = creatTreeUsePreString(s);return root;}

上述代碼我們從遞歸的角度解釋, 該函數(shù)的作用就是(創(chuàng)建一個二叉樹), 那創(chuàng)建一顆二叉樹需要創(chuàng)建出左子樹, 也需要創(chuàng)建出一顆右子樹, 所以出現(xiàn)了子問題的反復調(diào)用, 我們只需要把這個函數(shù)想象為一個"黑匣子"…

3. 根據(jù)前序中序反序列創(chuàng)建二叉樹

其實該問題就是給我們一個兩個數(shù)組(無重復數(shù)據(jù)), 一個是前序遍歷的結(jié)果, 一個是中序遍歷的結(jié)果, 然后讓我們創(chuàng)建出一顆完整的二叉樹, 我們這個問題給定的兩個數(shù)組, 可以是前序加中序, 也可以是中序加后序, 但是不可以是前序加后序(創(chuàng)建不出唯一的二叉樹), 例子自己想…, 比如下面這個例子

TreeNode node1 = new TreeNode(1);
TreeNode node2 = new TreeNode(2);//第一顆樹
node1.left = node2;
//第二顆樹
node1.right = node2

下面我們分析一下如何創(chuàng)建一個二叉樹(前序加上中序)
假設(shè)有一個函數(shù) func, 這個函數(shù)的功能就是創(chuàng)建二叉樹, 那么該函數(shù)的參數(shù)應該就是(傳入數(shù)據(jù)的所有信息)
> func(int[] pre,int l1,int r1,int[] in,int l2,int r2) 我們假設(shè)數(shù)組的長度都是5, 那么我們創(chuàng)建二叉樹的過程一定是從前序的結(jié)果開始的, 首先我們new出來根節(jié)點, 然后找到根節(jié)點在中序數(shù)組中所處的位置, 也就是在這個左側(cè)就是我們的左樹的部分(我們可以知道節(jié)點的規(guī)模), 右側(cè)就是右樹, 也就是說我們可以知道每一個元素在中序的位置然后控制左右邊界的位置, 最終完成二叉樹的創(chuàng)建, 由于這個過程相對抽象, 我們下面舉一個例子方便大家理解
在這里插入圖片描述
下面是我們的代碼實現(xiàn)(用HashMap加速查詢的過程)

/*** 從前序跟中序構(gòu)建一顆完整的二叉樹* 用的是一個封裝的構(gòu)建函數(shù)來構(gòu)建 f(pre,l1,r1,in,l1,r1)*/public TreeNode buildTree(int[] preorder, int[] inorder) {if (preorder == null || inorder == null || preorder.length != inorder.length) return null;//構(gòu)建出來一張表加快構(gòu)建二叉樹的過程HashMap<Integer, Integer> map = new HashMap<>();for (int i = 0; i < inorder.length; ++i) {map.put(inorder[i], i);}return creatTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length, map);}private TreeNode creatTree(int[] pre, int l1, int r1, int[] in, int l2, int r2, HashMap<Integer, Integer> map) {//遞歸的終止條件if (l1 > r1) {return null;}if (r1 == l1) {return new TreeNode(pre[r1]);}//借助HashMap加快查找的過程int k = map.get(pre[l1]);TreeNode node = new TreeNode(pre[l1]);node.left = creatTree(pre, l1 + 1, l1 + k - l2, in, l2, k, map);node.right = creatTree(pre, l1 + k - l2 + 1, r1, in, k + 1, r2, map);return node;}

從遞歸的角度來說, 創(chuàng)建二叉樹的這個函數(shù)相當于就是一個"黑盒", 我們要相信它可以根據(jù)我們給定的參數(shù)創(chuàng)建出一顆完整的樹, 然后我們需要去創(chuàng)建我們的左子樹, 創(chuàng)建右子樹(就出現(xiàn)了子問題復現(xiàn)的情況), 所以出現(xiàn)了遞歸

后序加上中序其實是同理的

4. 二叉樹的路徑問題

該問題涉及到回溯這一算法的概念, 其實遞歸的過程天然就帶著回溯, 那我們?yōu)槭裁匆没厮菽? 是因為在該類問題當中, 我們用一個stack收集節(jié)點的時候, 如果該節(jié)點的左右深度優(yōu)先搜索完畢之后, 我們需要把該節(jié)點彈出, 進行其他深度路徑的搜索, 其實回溯的實際也正式我們該節(jié)點的搜索任務(wù)結(jié)束的時機, 從內(nèi)存的角度來看, 也是該函數(shù)棧幀彈棧的時機

/*** 二叉樹的所有路徑* 1. 返回值是void類型 2. 遍歷的順序我們采用的是前序遍歷的順序* 3. 中途會進行節(jié)點的彈出其實也就是回溯的思路(回溯和遞歸是不分家的)*/private List<String> binaryTreePathsRes = new ArrayList<>();private ArrayDeque<Integer> stack = new ArrayDeque<>();public List<String> binaryTreePaths(TreeNode root) {binaryTreePathsFunc(root);return binaryTreePathsRes;}private void binaryTreePathsFunc(TreeNode node) {//遞歸的終止條件if (node == null) return;stack.add(node.val);//證明遞歸到了葉子節(jié)點該收獲了, 不能在null節(jié)點處收獲if (node.left == null && node.right == null) {StringBuilder sp = new StringBuilder();Iterator<Integer> it = stack.iterator();while (it.hasNext()) sp.append(it.next() + "->");sp.delete(sp.lastIndexOf("-"), sp.lastIndexOf(">") + 1);binaryTreePathsRes.add(sp.toString());stack.removeLast();}//下面就是深度優(yōu)先搜索的過程binaryTreePathsFunc(node.left);binaryTreePathsFunc(node.right);stack.removeLast();}

還有一個搜索和為targerSum的路徑然后返回, 其實是一樣的思路, 定義一個全局變量sum, 每次遍歷到的時候進行 sum += root.val , 搜索完畢之后進行回溯操作, sum -= root.val, 思路其實是一樣的

5. LCA公共祖先問題

其實就是求兩個節(jié)點p,q在一顆二叉樹的最近的公共祖先, 也是著名的LCA問題(該問題本身是很復雜的,我們先弄一個入門題), 我們知道, p,q對于一顆樹來說, 他們可能在一支上(此時q或者是p就是最近的公共祖先), 又或者是分屬于兩顆不同的樹, 此時樹的根節(jié)點就是最近的公共祖先, 我們現(xiàn)在創(chuàng)建, 其實也就是dfs對左右子樹進行深度優(yōu)先搜索的思路)
在這里插入圖片描述

其實代碼是很簡單的

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null || root == q || root == p){return root;}//往左邊搜索TreeNode ln = lowestCommonAncestor(root.left,p,q);//往右邊搜索TreeNode rn = lowestCommonAncestor(root.right,q,p);if(ln == null && rn == null) return null;if(ln != null && rn != null) return root;return ln == null ? rn : ln;}
}

其實就是一個簡單的搜索的邏輯
總結(jié)一下本節(jié), 我們遞歸要如何想到, 我們要把這個方法當成一個黑盒子, 然后確定返回值, 終止條件等等, 另外遞歸的過程其實就是深度優(yōu)先搜索, 搜索和遞歸是不分家的, 還有搜索跟回溯是不分家的

6. 二叉搜索樹的LCA問題

常規(guī)樹的LCA解決之后, 二叉搜索樹的LCA問題其實也被包括在里面了, 但是是否有一個更好的思路來解決二叉搜索樹的最近公共祖先呢?
我們都知道, 對于二叉搜索樹來說, 左側(cè)的所側(cè)節(jié)點都小于當前節(jié)點, 右側(cè)都大于
所以我們可以把問題抽象為下面這個問題, 記作當前的節(jié)點為cur
cur == p || cur == q : cur就是我們要找到的祖先節(jié)點
cur > Math.max(p,q) : cur = cur.left (向左側(cè)移動)
cur < Math.min(p,q) : cur = cur.right (向右側(cè)移動)
Math.min(p,q) < cur < Math.max(p,q) 此時 cur就是最近公共祖先
翻譯為實現(xiàn)代碼如下

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {return p.val > q.val ? lca(root,q,p) : lca(root,p,q);}//該方法默認的p為較小的值, q為較大的值public TreeNode lca(TreeNode root,TreeNode p,TreeNode q){while(p != root && q != root){if(root.val > p.val && root.val < q.val){return root;}root = root.val > q.val ? root.left : root.right;}return root;}
}

7. 驗證搜索二叉樹

這個問題如何想到遞歸呢, 首先二叉搜索樹的左子樹也是一個二叉搜索樹, 右子樹也是一個二叉搜索樹, 遞歸的終點其實就是葉子節(jié)點, 對于任何一個樹, 左子樹的最大值, 一定要小于中間節(jié)點的值, 右側(cè)子樹的最小值一定要大于中間節(jié)點的值, 而對于任意一個樹, 如何找到最大最小值呢, 其實就是左子樹一直向右側(cè)扎, 右子樹一直向左側(cè)扎, 代碼實現(xiàn)如下

class Solution {public boolean isValidBST(TreeNode root) {//遞歸的終止條件if(root == null) return true;if(root.left == null && root.right == null) return true;//尋找左側(cè)最大, 右側(cè)最小(設(shè)置一個前驅(qū)節(jié)點)TreeNode curl = root.left;TreeNode prel = null;TreeNode curr = root.right;TreeNode prer = null;while(curl != null){prel = curl;curl = curl.right;}while(curr != null){prer = curr;curr = curr.left;}if(prel == null){if(root.val >= prer.val) return false;}if(prer == null){if(root.val <= prel.val) return false;}if(prel != null && prer != null){if(root.val <= prel.val || root.val >= prer.val) return false;}return isValidBST(root.left) && isValidBST(root.right);}
}

遍歷方式其實就是后續(xù)遍歷, 收集到左右子樹的信息然后返回

8. 修建搜索二叉樹

在這里插入圖片描述

這個題的意思就是上面描述的這樣, 分析的思路如下

 /*** 修剪二叉搜索樹* 問題分析 : 如果當前節(jié)點的值小于左邊界, 那么我們當前節(jié)點及其左邊都不會被保留*           如果當前節(jié)點的值大于右邊界, 那么我們當前節(jié)點及其右邊都不會被保留*           如果當前節(jié)點的值介于范圍內(nèi)部, 那么就保留當前節(jié)點遞歸修建左子樹跟右子樹** 代碼邏輯 : 我們的 "黑盒函數(shù)" 的功能是修建一顆二叉樹, 并返回修剪之后的頭節(jié)點* 實在不行自己畫遞歸圖去理解*/public TreeNode trimBST(TreeNode root, int low, int high) {//遞歸的終止條件if(root == null){return null;}if(root.val < low){return trimBST(root.right,low,high);}if(root.val > high){return trimBST(root.left,low,high);}root.left = trimBST(root.left,low,high);root.right = trimBST(root.right,low,high);return root;}

9. 二叉樹打家劫舍問題

這道題其實已經(jīng)是樹形dp了, 但是我們今天的解法大家都能聽懂, 我們用遞歸去做這道題, 我們首先定義一個全局變量yes, 和一個全局變量no, 前者代表的含義是對一個頭節(jié)點來說, 我偷了頭節(jié)點的最大收益, 后者來說是我
不偷頭節(jié)點的最大收益, 設(shè)置我們的當前節(jié)點的狀態(tài)就是n(不偷當前節(jié)點), y(偷當前節(jié)點), 所以我們的狀態(tài)轉(zhuǎn)移方程就是
y += this.no;
n += Math.max(this.yes,this.no);
我們下面的func函數(shù)執(zhí)行完畢之后就會更新全局的yes和no變量

class Solution {//yes的意思是偷頭節(jié)點的情況下, 我們能獲得的最大收益public int yes = 0;//no的意思是不偷頭節(jié)點的情況下, 我們能獲得的最大收益public int no = 0;public int rob(TreeNode root) {func(root);return Math.max(this.yes,this.no);}//該函數(shù)的功能就是給定一個頭節(jié)點, 可以得到我們此時節(jié)點的兩種最優(yōu)解的情況private void func(TreeNode root){//遞歸終止條件if(root == null){this.yes = 0;this.no = 0;}else{int y = root.val;int n = 0;func(root.left);y += this.no;n += Math.max(this.no,this.yes);func(root.right);y += this.no;n += Math.max(this.no,this.yes);this.yes = y;this.no = n;}}
}
http://m.aloenet.com.cn/news/37197.html

相關(guān)文章:

  • 重慶快建網(wǎng)站網(wǎng)絡(luò)推廣技術(shù)外包
  • 網(wǎng)站算信息化建設(shè)電腦版百度網(wǎng)盤
  • 成都網(wǎng)站建設(shè)公司常熟網(wǎng)站建設(shè)
  • 做鞋的垂直網(wǎng)站百度客服24小時電話人工服務(wù)
  • b2c購物網(wǎng)站開發(fā)西安seo網(wǎng)站關(guān)鍵詞優(yōu)化
  • 做旅行網(wǎng)站的意義百度官網(wǎng)app下載安裝
  • 哪些網(wǎng)站可以做邀請函精準大數(shù)據(jù)獲客系統(tǒng)
  • 免費行情的軟件入口下載路由器優(yōu)化大師
  • 宣城市建設(shè)銀行網(wǎng)站建站流程
  • 外貿(mào)網(wǎng)站建設(shè)步驟網(wǎng)站設(shè)計公司多少錢
  • 青島中小企業(yè)網(wǎng)站制作公司網(wǎng)站建設(shè)費用多少
  • 百度制作的wordpress工具欄seo在線工具
  • 個人做網(wǎng)站的流程淘寶推廣
  • 找人做公司網(wǎng)站百度商城
  • 網(wǎng)站建設(shè)項目采購公告天津seo優(yōu)化公司
  • 怎么做接口網(wǎng)站企業(yè)推廣方法
  • 在局網(wǎng)站 作風建設(shè)免費頂級域名注冊網(wǎng)站
  • 高端網(wǎng)站建設(shè)公司興田德潤可以不上海網(wǎng)絡(luò)推廣排名公司
  • wordpress登陸頁面保護插件seo點擊工具
  • 建筑電工證查詢網(wǎng)站企業(yè)seo案例
  • 網(wǎng)站名和域名的區(qū)別seo推廣多少錢
  • 如何創(chuàng)建自己網(wǎng)站電商項目策劃書
  • 做阿里巴巴網(wǎng)站圖片大全如何做公司網(wǎng)站推廣
  • 做曖視頻網(wǎng)站大全什么平臺可以做引流推廣
  • 網(wǎng)站推廣的優(yōu)勢有哪些seo課程培訓視頻
  • 怎么做網(wǎng)站前臺站長之家怎么找網(wǎng)址
  • 免費網(wǎng)站建設(shè)塔山雙喜泉州全網(wǎng)營銷推廣
  • 門戶網(wǎng)站和社交網(wǎng)絡(luò)的區(qū)別百度平臺商家我的訂單查詢
  • 網(wǎng)站登錄賬號密碼保存在哪里推廣手段有哪些
  • 螞蟻幣是什么網(wǎng)站建設(shè)網(wǎng)站推廣應該怎么做?