1.給定兩個數,找到其公共的最近祖先
題目如上,需要注意的是,節點本身也可以是自己的祖先。
例如,7和2的公共祖先就是2
6和4的公共祖先就是5,而不是3,因爲3不是最近的
首先的思路就是,通過遍歷二叉樹,如果通過一個節點,可以找到這兩個值,說明這個節點就是一個公共祖先,但問題是,我們不知道這個節點是不是她的最近祖先。我們分析可以得出,只要這兩個要找的節點分別位於:根節點,左子樹,右子樹中的任意兩個,就認爲他是最近的。可能這個思路是比較難想到的,我們想想,假設兩個節點都在左子樹,或者右子樹,,那麼這個祖先就肯定離這兩個節點比較遠的,所以纔可以把這兩個節點都包含在自己的左子樹,或者右子樹。
下來我們通過代碼進一步分析:
//找最近公共父節點
private TreeNode lca = null;
public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q) {
if (root == null) {
return null;
}
findNode(root,p,q);
return lca;
}
private boolean findNode (TreeNode root,TreeNode p,TreeNode q) {
if (root == null) {
return false;
}
int left = findNode(root.left,p,q)?1:0;
int right = findNode(root.right,p,q)?1:0;
int mid = (root == p||root == q)?1:0;
if (left+right+mid == 2) {
lca = root;
}
return (right+left+mid)>0;
}
第一部還是判斷數是否爲空,爲空的話是肯定找不到的,返回空,然後我們再通過一個輔助函數找到這個滿足條件的節點,並返回。關於這個找節點函數,第一步判斷是否爲空,爲空返回false,注意這個函數的返回值類型是boolean類型,是有原因的。最後我們通過遞歸看左右子樹是否能找到兩個節點,注意,這裏呢,我們這裏只要找到其中的一個,就認爲找到了,然後通過三目表達式,返回1,沒找到返回0,最後,我們要找到最近公共祖先,根據我上面講的那個判斷是否爲最近的思路,可以轉換爲,只要left,right,mid這三個變量之和等於2,就說明肯定這三個位置兩個爲1,一個爲0,這是這個節點就是最近的公共祖先,把這個節點賦給變量lca,最後我們返回值找到就是true,沒找到就是false,我們可以直接返回這個三個變量之和大於0這個表達式的結果。
2.將二叉搜索樹轉換爲雙向鏈表
題目:輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
public TreeNode Convert(TreeNode root) {
if (root == null) {
return null;
}
if (root.right == null&&root.left == null) {
return root;
}
TreeNode left = Convert(root.left);
TreeNode leftTail = left;
while (leftTail!=null&&leftTail.right!=null) {
leftTail = leftTail.right;
}
if (leftTail != null) {
leftTail.right = root;
root.left = leftTail;
}
TreeNode right = Convert(root.right);
if (right!=null) {
root.right = right;
right.left = root;
}
return left != null?left:root;
}
這個思路也是比較簡單的,如果爲空返回null,只有一個根節點,返回根節點本身,然後通過遞歸進行中序遍歷,訪問完左子樹後,就已經把左子樹創建完畢,然後需要把根節點插入到她的後面,給定一個leftTail變量指向左子樹鏈表的頭節點,然後開始遍歷,知道爲空,此時這個節點就指向了最後一個節點,這裏要注意,leftTail不能爲空,即left不能爲空,這裏我們就不需要管爲空會怎麼樣了,只要非空就行,然後利用雙向鏈表的尾插進行插入,然後遍歷右子樹,不爲空時,直接把右子樹插入帶根節點的後面,最後返回整個鏈表就行了,要注意,left可能爲空,我們就用三目表達式,如果不爲空返回left,否則直接返回根節點。
3.根據先序和中序結果構造二叉樹
要注意,我們這裏的遍歷結果是不含null的,所以我們不能根據其中一個直接構造二叉樹。
private int index = 0;
public TreeNode buildTree(int[] preorder,int[] inorder) {
index = 0;
return buildTreeHelper(preorder,inorder,0,inorder.length);
}
private TreeNode buildTreeHelper(int[] preorder,int[] inorder,
int inorderLeft,int inorderRight) {
if (inorderLeft>=inorderRight) {
return null;
}
if (index>=preorder.length) {
return null;
}
TreeNode newNode = new TreeNode(preorder[index]);
int pos = find(inorder,inorderLeft,inorderRight,newNode.val);
index++;
newNode.left = buildTreeHelper(preorder,inorder,inorderLeft,pos);
newNode.right = buildTreeHelper(preorder,inorder,pos+1,inorderRight);
return newNode;
}
private int find(int[] inorder,int inorderLeft,int inorderRight,int val) {
for (int i = inorderLeft;i<inorderRight;i++) {
if (inorder[i] == val) {
return i;
}
}
return -1;
}
我們可以根據先序結果,再結合中序而分析出,設index表示當前遍歷先序的第幾個值,然後通過樹的構造函數返回值。再構造樹的函數中,參數裏,我們給了兩個遍歷結果的數組和中序的左邊界和右邊界,這個就是再每次遞歸中,當前節點再中序遍歷結果中左子樹和右子樹的範圍,如果左邊界比右邊界大了,說明爲空,返回null,還有index比先序遍歷數組大了,說明遍歷完了,也返回null,然後把先序數組裏第index個也就是頭節點加入樹中,然後再利用find函數再中序數組中找到這個數的下標pos,再index++,是爲了下次再去中序中尋找節點,然後利用遞歸,我們發現,左子樹範圍是[inorderLeft,pos) ,右子樹範圍是[pos+1,inorderRight),根據這個不斷遞歸連接節點,然後返回樹的頭節點。
4.根據二叉樹創建字符串