算法(三)-二叉樹

一、概念

  • :由n(n>=1)個有限結(jié)點組成一個具有層次關(guān)系的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上蔗候,而葉朝下的。
  • 二叉樹:每個結(jié)點至多只有二棵子樹埂软。
  • 滿二叉樹:葉子節(jié)點都在同一層并且除葉子節(jié)點外的所有節(jié)點都有兩個子節(jié)點锈遥。
  • 完全二叉樹:對于一顆二叉樹,假設(shè)其深度為d(d>1)。除第d層外的所有節(jié)點構(gòu)成滿二叉樹所灸,且第d層所有節(jié)點從左向右連續(xù)地緊密排列丽惶,這樣的二叉樹被稱為完全二叉樹;
  • 二叉查找樹(BST):若任意節(jié)點的左子樹不空庆寺,則左子樹上所有節(jié)點的值均小于它的根節(jié)點的值蚊夫; 若任意節(jié)點的右子樹不空,則右子樹上所有節(jié)點的值均大于它的根節(jié)點的值懦尝; 任意節(jié)點的左知纷、右子樹也分別為二叉查找樹;
  • 平衡二叉樹(AVL):它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1陵霉,并且左右兩個子樹都是一棵平衡二叉樹琅轧,同時,平衡二叉樹必定是二叉搜索樹踊挠。

二乍桂、二叉樹性質(zhì)

  • 在二叉樹中,第i層的結(jié)點總數(shù)不超過2^(i-1)效床;
  • 深度為h的二叉樹最多有2h-1個結(jié)點(h>=1)睹酌,最少有h個結(jié)點;
  • 對于任意一棵二叉樹剩檀,如果其葉結(jié)點數(shù)為N0憋沿,而度數(shù)為2的結(jié)點總數(shù)為N2, 則N0=N2+1沪猴;
  • 具有n個結(jié)點的完全二叉樹的深度為int(log2n)+1

三辐啄、二叉樹常見算法題

class TreeNode {
    TreeNode left;
    TreeNode right;
    int value;
    public TreeNode(int v) {
        value = v;
    }
}
public class test {
    /**
     * 構(gòu)建完全二叉查找樹 4 2 6 1 3 5 7
     */
    public static TreeNode getBinarySortTree() {
        TreeNode root = new TreeNode(4);
        TreeNode l = new TreeNode(2);
        TreeNode r = new TreeNode(6);
        TreeNode ll = new TreeNode(1);
        TreeNode lr = new TreeNode(3);
        TreeNode rl = new TreeNode(5);
        TreeNode rr = new TreeNode(7);
        root.left = l;
        root.right = r;
        l.left = ll;
        l.right = lr;
        r.left = rl;
        r.right = rr;
        return root;
    }
    /**
     * 隨便構(gòu)建一棵二叉樹
     */
    public static TreeNode getBinaryTree() {
        TreeNode root = new TreeNode(4);
        TreeNode l = new TreeNode(2);
        TreeNode r = new TreeNode(6);
        TreeNode lr = new TreeNode(3);
        TreeNode rl = new TreeNode(5);
        TreeNode rr = new TreeNode(7);
        TreeNode lrl = new TreeNode(1);
        root.left = l;
        root.right = r;
        l.right = lr;
        r.left = rl;
        r.right = rr;
        lr.left = lrl;
        return root;
    }
    /**
     * 1 二叉樹的遍歷
     * 
     * @param args
     */
    // 前序遍歷: 根左右
    //遞歸
    public static void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.print(root.value);
        preOrder(root.left);
        preOrder(root.right);
    }
    //非遞歸
    public static void preOrder2(TreeNode head) {
        if (head == null) {
            return;
        }
        //幾乎與層序遍歷一樣,除了隊列換成堆棧
        Stack<TreeNode> stack = new Stack<>();
        stack.push(head);
        while (!stack.isEmpty()) {
            head = stack.pop();
            System.out.print(head.value + " ");
            if (head.right != null)
                stack.push(head.right);
            if (head.left != null)
                stack.push(head.left);
        }
    }

    // 中序遍歷: 左根右
//遞歸
    public static void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.value);
        inOrder(root.right);
    }
 //非遞歸
    public static void midOrder2(TreeNode head) {
        if (head == null) {
            return;
        }
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || head != null) {
            if (head != null) {
                // 當(dāng)前節(jié)點不為空, 將自己壓進(jìn)棧并將自己的左孩子作為當(dāng)前節(jié)點(壓入左邊界)
                stack.push(head);
                head = head.left;
            } else {
                // 當(dāng)前節(jié)點為空(沒有左孩子了), 將棧頂元素彈出作為當(dāng)前節(jié)點, 并將當(dāng)前節(jié)點的右孩子壓進(jìn)棧
                head = stack.pop();
                System.out.print(head.value + " ");
                head = head.right;
            }
        }
    }


    // 后序遍歷: 左右根
//遞歸
    public static void postOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.value);
    }
  //非遞歸
    public static void postOrder2(TreeNode head) {
        if (head == null) {
            return;
        }
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();//借助輔助存儲實現(xiàn) 根右左运嗜,利用棧的特性輸出左右根
        stack1.push(head);
        while (!stack1.isEmpty()) {
            head = stack1.pop();
            stack2.push(head);
            // 有左孩子就先壓入左孩子
            if (head.left != null)
                stack1.push(head.left);
            // 有右孩子就后壓入右孩子
            if (head.right != null)
                stack1.push(head.right);
        }
        // 逆序打印 根 右 左 的結(jié)果壶辜,就是后序遍歷的結(jié)果
        while (!stack2.isEmpty())
            System.out.print(stack2.pop().value + " ");
    }

    // 層次遍歷
    public static void levelOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        // 這里借助隊列來實現(xiàn)
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            System.out.print(cur.value);
            if (cur.left != null) {
                queue.offer(cur.left);
            }
            if (cur.right != null) {
                queue.offer(cur.right);
            }
        }
    }
    /**
     * 2 二叉樹深度、葉子節(jié)點相關(guān)
     */
    // 求二叉樹的深度
    public static int getTreeDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = getTreeDepth(root.left);
        int right = getTreeDepth(root.right);
        // 返回max是最大深度担租,min是最小深度
        return Math.max(left, right) + 1;
    }
    // 深度為N的滿二叉樹砸民,葉子結(jié)點數(shù)為:2^n-1
    public static int getLeafCount(int N) {
        return (int) Math.pow(2, N - 1);
    }
    // 3 反轉(zhuǎn)二叉樹/求二叉樹的鏡像
    public static void invertBanaryTree(TreeNode root) {
        if (root == null) {
            return;
        }
        invertBanaryTree(root.left);
        invertBanaryTree(root.right);
        TreeNode node = root.left;
        root.left = root.right;
        root.right = node;
    }
    /**
     * 4 二叉搜索樹轉(zhuǎn)換成一個排序的雙向鏈表
     */
    // 用于儲存中序遍歷當(dāng)前的節(jié)點,作為中間變量翩活,將雙向指針鏈接起來
    static TreeNode cur = null;
    // 遞歸到最深層阱洪,返回雙向鏈表的頭
    static TreeNode head = null;
    public static TreeNode convertDoubleLinkedList(TreeNode root) {
        if (root == null) {
            return null;
        }
        convertDoubleLinkedList(root.left); // 左
        // 中序遍歷在中間進(jìn)行處理
        // left= 前驅(qū), right=后繼
        root.left = cur;
        if (cur != null) {
            cur.right = root;
        }
        // pre指向root
        cur = root;
        // 遞歸到最深處才將頭賦值菠镇,也就是雙向鏈表的頭
        if (head == null) {
            head = root;
        }
        convertDoubleLinkedList(root.right); // 右
        return head;
    }
    // 5 二叉查找樹的第k個結(jié)點(給定一棵二叉搜索樹冗荸,請找出其中的第k小的結(jié)點)
    static int count = 0;
    public static TreeNode getKthNode(TreeNode root, int k) {
        if (root == null) {
            return null;
        }
        // 二叉搜索樹按照中序遍歷的順序打印出來正好就是從小到大順序排列,然后找到第k個結(jié)點就是結(jié)果利耍。
        TreeNode left = getKthNode(root.left, k);
        if (left != null) {
            return left;
        }
        count++;
        if (count == k) {
            return root;
        }
        TreeNode right = getKthNode(root.right, k);
        if (right != null) {
            return right;
        }
        return null;
    }
    // 6 二叉樹中和為某個值的路徑
    public static void findPath(TreeNode root, int k) {
        if (root == null)
            return;
        Stack<Integer> stack = new Stack<Integer>();
        findPath(root, k, stack);
    }
    public static void findPath(TreeNode root, int k, Stack<Integer> path) {
        if (root == null)
            return;
        if (root.left == null && root.right == null) {
            if (root.value == k) {
                for (int i : path) {
                    System.out.print(i + ",");
                }
                // 還要加上當(dāng)前等于K的value
                System.out.print(root.value);
            }
        } else {
            path.push(root.value);
            findPath(root.left, k - root.value, path);
            findPath(root.right, k - root.value, path);
            path.pop();
        }
    }
    public static void main(String[] args) {
        TreeNode root = getBinarySortTree();
        TreeNode linkedList = convertDoubleLinkedList(root);
        TreeNode tail = null;
        while (linkedList != null) {
            System.out.print(linkedList.value);
            tail = linkedList;
            linkedList = linkedList.right;
        }
        System.out.println("");
        while (tail != null) {
            System.out.print(tail.value);
            tail = tail.left;
        }
    }
}

簡單寫了幾道高頻率出現(xiàn)的二叉樹算法題蚌本,用得比較多的算法思想還是遞歸盔粹,大部分用到的核心邏輯還是排序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載程癌,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者舷嗡。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嵌莉,隨后出現(xiàn)的幾起案子进萄,更是在濱河造成了極大的恐慌,老刑警劉巖锐峭,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件中鼠,死亡現(xiàn)場離奇詭異,居然都是意外死亡沿癞,警方通過查閱死者的電腦和手機援雇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椎扬,“玉大人惫搏,你說我怎么就攤上這事〔系樱” “怎么了筐赔?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長揖铜。 經(jīng)常有香客問我川陆,道長,這世上最難降的妖魔是什么蛮位? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮鳞绕,結(jié)果婚禮上失仁,老公的妹妹穿的比我還像新娘。我一直安慰自己们何,他們只是感情好萄焦,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冤竹,像睡著了一般拂封。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鹦蠕,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天冒签,我揣著相機與錄音,去河邊找鬼钟病。 笑死萧恕,一個胖子當(dāng)著我的面吹牛刚梭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播票唆,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼朴读,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了走趋?” 一聲冷哼從身側(cè)響起衅金,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎簿煌,沒想到半個月后氮唯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡啦吧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年您觉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片授滓。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡琳水,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出般堆,到底是詐尸還是另有隱情在孝,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布淮摔,位于F島的核電站私沮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏和橙。R本人自食惡果不足惜仔燕,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魔招。 院中可真熱鬧晰搀,春花似錦、人聲如沸办斑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡翅。三九已至鳞疲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蠕蚜,已是汗流浹背尚洽。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工靶累, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人争舞。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓叁熔,卻偏偏與公主長得像荣回,于是被迫代替她去往敵國和親心软。 傳聞我的和親對象是個殘疾皇子删铃,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容