669. Trim a Binary Search Tree

Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so that all its elements lies in [L, R] (R >= L). You might need to change the root of the tree, so the result should return the new root of the trimmed binary search tree.
Example 1:
Input:
1
/
0 2
L = 1
R = 2
Output:
1
\
2

通過這道題我明白了「形參,實參」這個概念。
1.形參變量只有在被調用時才分配內存單元,在調用結束時, 即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。 函數調用結束返回主調函數后則不能再使用該形參變量。

一開始我的代碼:

        if (root == null) return null;
        if (root.val < L) {
            root = root.right;
            return trimBST(root, L, R);
        }
        if (root.val > R) {
            root = root.left;
            return trimBST(root, L, R);
        }
        if (root.left != null && root.left.val < L) {
            root.left = root.left.right;
        }
        if (root.right != null && root.right.val > R) {
            root.right = root.right.left;
        }
        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);
        return root;
    }

注意

        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);

起初我想不通為什么要給root.left和root.right賦值,明明我已經在遞歸中改變了它的孩子的內存了?。╮oot = root.right)。但是由于進入了一個新的函數,這樣做其實并沒有改變它孩子的內存,而是制造了一個新的引用(棧內存)指向了root.right;而如果這時我改變root.right的值,root.right會指向另一塊內存,始終不會對root造成影響。

    public TreeNode trimBST(TreeNode root, int L, int R) {
        if (root == null) return null;
        if (root.val < L) {
            return trimBST(root.right, L, R);
        }
        if (root.val > R) {
            return trimBST(root.left, L, R);
        }
        if (root.left != null && root.left.val < L) {
            root.left = root.left.right;
        }
        if (root.right != null && root.right.val > R) {
            root.right = root.right.left;
        }
        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);
        return root;
    }

Or:

    public TreeNode trimBST(TreeNode root, int L, int R) {
        if (root == null) return null;
        
        if (root.val < L) return trimBST(root.right, L, R);
        if (root.val > R) return trimBST(root.left, L, R);
        
        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);
        
        return root;
    }

Dec 7 2017 review

今天又蒙逼了,不知怎么又繞內存/引用的問題上了,我寫了一段測試代碼,發(fā)現(xiàn)在函數里改變一個對象,那個對象竟然真的被改變了。比如我初始化一個數組是{1,2,3}(數組是對象,它是new出來的),在函數里改變數組第三個元素的值,我預想的是,因為函數和它的參數是保存在方法區(qū)的,函數參數中的數組,其實只是一個指向原來arr的引用,所以我在函數中改變arr又沒有return回去,按理說是數組還會是{1,2,3}吧,但事實并非如此,函數執(zhí)行完后arr被改變了。我的世界觀又一次崩塌了。。。


image.png

然后我看了下這個blog
里的例子,看到String拼接那部分才發(fā)現(xiàn)原因。其實這個我早就知道,只是長期不刷算法忘記了。。我傳入的int數組,參數里的arr確實只是一個引用沒錯,但是我改變的不是這個對象的引用,我改變的是它里面的元素。

我在上面說過,「而如果這時我改變root.right的值,root.right會指向另一塊內存,始終不會對root造成影響。」為什么會指向另一塊內存,而不是直接在原來的堆內存上修改?用形參實參來解釋其實不太嚴謹,下面是我看的另外一個問題得出的思考:
靜態(tài)方法會導致內存泄露嗎?

這種寫法不會造成內存泄漏。為什么不會呢?要想造成內存泄漏,你的工具類對象本身要持有指向傳入對象的引用才行!但是當你的業(yè)務方法調用工具類的靜態(tài)方法時,會生產一個稱為棧幀(stack frame)的東西(每次方法調用,JVM都會生成一個棧幀),當方法調用結束返回的時候,當前方法棧幀就已經被彈出了并且被釋放掉了。 整個過程結束時,工具類對象本身并不會持有傳入對象的引用。

也就是說之前理解的并不對,傳入的參數并不是對象的引用,而是一個棧幀(stack frame),或者說棧幀的一部分,我猜是放在stack frame的局部變量變里。對象的引用存在虛擬機棧,棧幀也存放在虛擬機棧,其實也就是通常理解的棧內存,每個棧幀都有自己的局部變量表。可以這么理解,對象的引用劫持了對象,指向了對象在堆內存中的首地址,通過等于號賦值的都是引用傳遞,但是棧幀中的那個「參數」沒有改變堆內存的權利,如果要改變它指向的對象,只能自己重新創(chuàng)建一塊內存。

每當一個java方法被執(zhí)行時都會在虛擬機中新創(chuàng)建一個棧幀,方法調用結束后即被銷毀。
棧幀存儲空間為虛擬機棧,每一個棧幀都有自己的局部變量表、操作數棧和指向當前方法所屬的類的運行時常量池的引用。
在每個線程中,只有目前正在執(zhí)行的那個方法的棧幀是活動的。這個棧幀就稱為當前棧幀(current frame),這個棧幀對應的方法稱之為當前方法(current method)。定義這個方法的類就稱之為當前類(current class)。對局部變量表和操作數棧的各種操作,通常都指的是對當前棧幀的局部變量表和操作數棧進行的操作。如下圖所示,

如果用全局變量,就不會有這種問題了。

我感覺我真的有點鉆牛角尖了。。
貼一個我的回答:
https://www.zhihu.com/question/27764932/answer/272648864

ref:
https://www.cnblogs.com/editice/p/5420716.html
http://blog.csdn.net/zgrjkflmkyc/article/details/8774511

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容