找最近公共祖先,將二叉搜索樹轉換爲雙向鏈表,根據先序和中序結果構造二叉樹(不含空節點),將先序結果轉爲字符串

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.根據二叉樹創建字符串

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章