記一次失敗的面試經歷


昨天晚上跟一家公司進行遠程視頻二面,面試官上來先扔過來一道編程題,要求在文檔中手擼代碼,不能用IDE編碼。

一開始沒想好,思路有問題,做到最后發現行不通,於是掛了。。。。。

 

第二天到公司靜下心來整理了一下思路,寫出來了。  做下記錄,算是給自己提個醒,以后遇事要沉着冷靜~~~~~

 

題目如下:

 

 

 

首先,觀察題目。  第一個圖代表的是二叉樹, 第二個圖代表的是那種某個節點的左子樹指向右子樹的情況,第三個圖代表了某個節點的子節點指向父節點或者指向祖輩節點的情況

注意我標紅的部分,這三個圖要表達的是它們各自代表一類情況,而非僅僅是圖中的這一種情形,所以你的程序邏輯要考慮周全

 

我們先來分析,按照二叉樹的先序遍歷規則(根->左->右), 圖一的遍歷順序應該是 ABDECF,圖二的遍歷規則應該是(假如也按照二叉樹規則來遍歷)ABDCFCF,圖三的遍歷順序是ABDBDBDBD......

通過以上分析可以知道,圖一的各個節點只會遍歷一次; 圖二的某些節點,會被遍歷兩次; 而圖三的某幾個節點,會形成一個死循環,一直遍歷下去。。。

 

那么,這道題的答案就呼之欲出了:

1. 寫一個遞歸程序,從A節點開始,把樹遍歷一次。   

2. 遍歷過程中,記錄每個節點經過的次數

3. 如果各個節點只經歷了一次,那就是tree;如果存在經歷了兩次的節點,並且沒有經歷三次的節點,就是no-tree; 如果存在經歷了三次的節點,就是circle.

4. 注意,圖三的情況特殊,因為是個死循環,要考慮何時退出遞歸函數、如何才能正常的退出遞歸函數,遞歸函數的退出方法,是個坑,不小心就會出錯

 

代碼如下:

1.定義TreeNode類型

    public class TreeNode
    {
        /// <summary>
        /// 節點的唯一ID,不重復
        /// </summary>
        public int nodeId { get; set; }

        /// <summary>
        /// 節點的名稱,可能會出現重名,需要注意
        /// </summary>
        public string nodeName { get; set; }

        /// <summary>
        /// 當前節點的子節點
        /// </summary>
        public List<TreeNode> nextNodes { get; set; }
    }

2. 定義TreeData類型

 public enum TreeType 
    {
        Tree,
        NoTree,
        Circle
    }

    //根據傳入的類型,構造樹結構的數據
    public class TreeData
    {
        public TreeNode root { get; }

        public TreeData(TreeType treeType) 
        {
            if (treeType == TreeType.Tree)
            {
                TreeNode nodeD = new TreeNode();
                nodeD.nodeId = 4;
                nodeD.nodeName = "節點D";
                nodeD.nextNodes = null;

                TreeNode nodeE = new TreeNode();
                nodeE.nodeId = 5;
                nodeE.nodeName = "節點E";
                nodeE.nextNodes = null;

                TreeNode nodeF = new TreeNode();
                nodeF.nodeId = 6;
                nodeF.nodeName = "節點F";
                nodeF.nextNodes = null;

                TreeNode nodeC = new TreeNode();
                nodeC.nodeName = "節點C";
                nodeC.nodeId = 3;
                nodeC.nextNodes = new List<TreeNode> { nodeF };

                TreeNode nodeB = new TreeNode();
                nodeB.nodeId = 2;
                nodeB.nodeName = "節點B";
                nodeB.nextNodes = new List<TreeNode> { nodeD, nodeE };

                TreeNode nodeA = new TreeNode();
                nodeA.nodeId = 1;
                nodeA.nodeName = "節點A";
                nodeA.nextNodes = new List<TreeNode> { nodeB, nodeC };

                root = nodeA;
            }
            else if (treeType == TreeType.NoTree)
            {
                TreeNode nodeD = new TreeNode();
                nodeD.nodeId = 4;
                nodeD.nodeName = "節點D";
                nodeD.nextNodes = null;

                TreeNode nodeE = new TreeNode();
                nodeE.nodeId = 5;
                nodeE.nodeName = "節點E";
                nodeE.nextNodes = null;

                TreeNode nodeF = new TreeNode();
                nodeF.nodeId = 6;
                nodeF.nodeName = "節點F";
                nodeF.nextNodes = null;

                TreeNode nodeC = new TreeNode();
                nodeC.nodeId = 3;
                nodeC.nodeName = "節點C";
                nodeC.nextNodes = new List<TreeNode> { nodeF };

                TreeNode nodeB = new TreeNode();
                nodeB.nodeName = "節點B";
                nodeB.nodeId = 2;
                nodeB.nextNodes = new List<TreeNode> { nodeD, nodeC };

                TreeNode nodeA = new TreeNode();
                nodeA.nodeId = 1;
                nodeA.nodeName = "節點A";
                nodeA.nextNodes = new List<TreeNode> { nodeB, nodeC };

                root = nodeA;
            }
            else 
            {
                TreeNode nodeD = new TreeNode();
                TreeNode nodeB = new TreeNode();

                nodeD.nodeId = 4;
                nodeD.nodeName = "節點D";
                nodeD.nextNodes = new List<TreeNode> { nodeB };

                nodeB.nodeName = "節點B";
                nodeB.nodeId = 2;
                nodeB.nextNodes = new List<TreeNode> { nodeD };

                TreeNode nodeE = new TreeNode();
                nodeE.nodeId = 5;
                nodeE.nodeName = "節點E";
                nodeE.nextNodes = null;

                TreeNode nodeF = new TreeNode();
                nodeF.nodeId = 6;
                nodeF.nodeName = "節點F";
                nodeF.nextNodes = null;

                TreeNode nodeC = new TreeNode();
                nodeC.nodeId = 3;
                nodeC.nodeName = "節點C";
                nodeC.nextNodes = new List<TreeNode> { nodeF };

                TreeNode nodeA = new TreeNode();
                nodeA.nodeId = 1;
                nodeA.nodeName = "節點A";
                nodeA.nextNodes = new List<TreeNode> { nodeB, nodeC };

                root = nodeA;
            }
        }
    }

3. 在Program.cs中定義遞歸函數

public static void JudgeTreeType(TreeNode root)
        {
            //需要退出遞歸時,修改標志位的狀態,遞歸會層層退出
            if (needStop)
            {
                return;
            }
            else
            {
                if (root != null && root.nextNodes != null)
                {
                    if (logDic.Keys.Contains(root.nodeId))
                    {
                        //如果節點root存在,累加一
                        logDic[root.nodeId] = logDic[root.nodeId] + 1;
                    }
                    else
                    {
                        //如果不存在,添加到集合中
                        logDic.Add(root.nodeId, 1);
                    }

                    //判定是否存在某個節點出現過次數3的情況
                    var hasThree = logDic.ContainsValue(3);
                    if (hasThree)
                    {
                        needStop = true;
                    }

                    foreach (var node in root.nextNodes)
                    {
                        JudgeTreeType(node);
                    }
                }
            }
        }

4. 定義變量,記錄每個節點經過的次數

 //定義數據集,記錄每個node被經過的次數
        static Dictionary<int, int> logDic = new Dictionary<int, int>();

        //注意,遞歸函數是層層調用的,想要在某一層讓整個遞歸程序退出循環
        //單純用return方法是行不通的,return只是跳出了當前層的循環
        //想要退出遞歸,需要定義一個全局變量,通過變量控制層層退出
        static bool needStop = false;

5. main函數調用

static void Main(string[] args)
        {
            //構造root節點數據
            JudgeTreeType(new TreeData(TreeType.Tree).root);

            //判定樹是哪種樹
            var hasThree = logDic.ContainsValue(3);
            var hasTwo = logDic.ContainsValue(2);

            if (hasThree)
            {
                Console.WriteLine("circle");
            }
            else if (!hasThree && hasTwo)
            {
                Console.WriteLine("no-tree");
            }
            else
            {
                Console.WriteLine("tree");
            }

            Console.ReadLine();
        }

 

其實整個程序很簡單,需要特別注意的是遞歸函數的退出方法。 

僅僅在某一層遞歸循環中去return是無法真正退出整個遞歸的,它只是退出了本層遞歸。

想要退出整個遞歸,需要定義一個全局變量,通過它來控制后續遞歸的執行!!!

另外一個難點就是不讓用IDE進行編寫,必須在文檔上手寫代碼,這對編程習慣和編碼能力是個考驗

 

源碼附上:

下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM