昨天在看《極客時間》嚴嘉偉老師的《如何作出好的職業選擇——認識你的職業錨》專題直播時,嚴老師講到了關於選擇的一些問題,我認爲其中的一些點講的很是好,總結一下分享給你們。c#
人爲何難作選擇?數據結構
選擇意味着放棄學習
你選擇一方,也就意味着放棄了另外一方。擺在你面前的選擇項越接近,你的選擇就會越困難,由於放棄其中任何一個選擇項都不容易。若是擺在你面前的選擇項對比明顯,那麼選擇起來就會輕鬆許多,你們幾乎都會堅決果斷的選擇「好」的選擇項,放棄掉「差」的選擇項。spa
選擇永遠都不是完美的3d
選擇永遠都不可能十全十美,只可能知足儘可能多的側重點。選擇的時候想知足越多的側重點,可能就會越難作出選擇。因此在選擇上不要過於追求完美。指針
警戒逃避性選擇——不知道本身要去哪兒,還要選擇離開。code
有一種選擇是對現狀不滿,想逃離這種現狀,可是殊不知道去哪裏。舉個例子,可能目前的公司有各類問題,好比開發流程不規範等,若是由於這些問題離開,可能就會從一個坑跳到另一個更大的坑。當決定離開的時候,必定是本身有明確的目標,很清楚本身想要什麼。blog
二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中全部結點,使得每一個結點被訪問一次且僅被訪問一次。遞歸
爲何研究二叉樹的遍歷?
由於計算機只會處理線性序列,而咱們研究遍歷,就是把樹中的結點變成某種意義的線性序列,這給程序的實現帶來了好處。
遍歷二叉樹以前,首先咱們要有一個二叉樹。要建立一個以下圖的二叉樹,就要先進行二叉樹的擴展,也就是將二叉樹每一個結點的空指針引出一個虛結點,其值爲一個特定值,好比'#'。處理後的二叉樹稱爲原二叉樹的擴展二叉樹。擴展二叉樹的每一個遍歷序列能夠肯定一個一顆二叉樹,咱們採用前序遍歷建立二叉樹。前序遍歷序列:124##5##36##7##。
定義二叉鏈表結點:
/// <summary> /// 二叉鏈表結點類 /// </summary> /// <typeparam name="T"></typeparam> public class TreeNode<T> { /// <summary> /// 數據域 /// </summary> public T Data { get; set; } /// <summary> /// 左孩子 /// </summary> public TreeNode<T> LChild { get; set; } /// <summary> /// 右孩子 /// </summary> public TreeNode<T> RChild { get; set; } public TreeNode(T val, TreeNode<T> lp, TreeNode<T> rp) { Data = val; LChild = lp; RChild = rp; } public TreeNode(TreeNode<T> lp, TreeNode<T> rp) { Data = default(T); LChild = lp; RChild = rp; } public TreeNode(T val) { Data = val; LChild = null; RChild = null; } public TreeNode() { Data = default(T); LChild = null; RChild = null; } }
先序遞歸建立二叉樹:
/// <summary> /// 先序建立二叉樹 /// </summary> /// <param name="node"></param> public static void CreateTree(TreeNode<char> node) { node.Data = Console.ReadKey().KeyChar; if (node.Data == '#') { return; } node.LChild = new TreeNode<char>(); CreateTree(node.LChild); if (node.LChild.Data == '#') { node.LChild = null; } node.RChild = new TreeNode<char>(); CreateTree(node.RChild); if (node.RChild.Data == '#') { node.RChild = null; } }
具體過程:
- 先訪問根節點
- 再序遍歷左子樹
- 最後序遍歷右子樹
代碼實現:
public static void PreOrderRecur(TreeNode<char> treeNode) { if (treeNode == null) { return; } Console.Write(treeNode.Data); PreOrderRecur(treeNode.LChild); PreOrderRecur(treeNode.RChild); }
具體過程:
- 首先申請一個新的棧,記爲stack;
- 將頭結點head壓入stack中;
- 每次從stack中彈出棧頂節點,記爲cur,而後打印cur值,若是cur右孩子不爲空,則將右孩子壓入棧中;若是cur的左孩子不爲空,將其壓入stack中;
- 重複步驟3,直到stack爲空.
代碼實現:
public static void PreOrder(TreeNode<char> head) { if (head == null) { return; } Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>(); stack.Push(head); while (!(stack.Count == 0)) { TreeNode<char> cur = stack.Pop(); Console.Write(cur.Data); if (cur.RChild != null) { stack.Push(cur.RChild); } if (cur.LChild != null) { stack.Push(cur.LChild); } } }
過程模擬:
執行結果:
具體過程:
- 先中序遍歷左子樹
- 再訪問根節點
- 最後中序遍歷右子樹
代碼實現:
public static void InOrderRecur(TreeNode<char> treeNode) { if (treeNode == null) { return; } InOrderRecur(treeNode.LChild); Console.Write(treeNode.Data); InOrderRecur(treeNode.RChild); }
具體過程:
- 申請一個新棧,記爲stack,申請一個變量cur,初始時令cur爲頭節點;
- 先把cur節點壓入棧中,對以cur節點爲頭的整棵子樹來講,依次把整棵樹的左子樹壓入棧中,即不斷令cur=cur.left,而後重複步驟2;
- 不斷重複步驟2,直到發現cur爲空,此時從stack中彈出一個節點記爲node,打印node的值,並讓cur = node.right,而後繼續重複步驟2;
- 當stack爲空而且cur爲空時結束。
代碼實現:
public static void InOrder(TreeNode<char> treeNode) { if (treeNode == null) { return; } Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>(); TreeNode<char> cur = treeNode; while (!(stack.Count == 0) || cur != null) { while (cur != null) { stack.Push(cur); cur = cur.LChild; } TreeNode<char> node = stack.Pop(); Console.WriteLine(node.Data); cur = node.RChild; } }
過程模擬:
執行結果:
- 前後序遍歷左子樹
- 再後序遍歷右子樹
- 最後訪問根節點
代碼實現:
public static void PosOrderRecur(TreeNode<char> treeNode) { if (treeNode == null) { return; } PosOrderRecur(treeNode.LChild); PosOrderRecur(treeNode.RChild); Console.Write(treeNode.Data); }
具體過程:
使用兩個棧實現
- 申請兩個棧stack1,stack2,而後將頭結點壓入stack1中;
- 從stack1中彈出的節點記爲cur,而後先把cur的左孩子壓入stack1中,再把cur的右孩子壓入stack1中;
- 在整個過程當中,每個從stack1中彈出的節點都放在第二個棧stack2中;
- 不斷重複步驟2和步驟3,直到stack1爲空,過程中止;
- 從stack2中依次彈出節點並打印,打印的順序就是後序遍歷的順序;
代碼實現:
public static void PosOrderOne(TreeNode<char> treeNode) { if (treeNode == null) { return; } Stack<TreeNode<char>> stack1 = new Stack<TreeNode<char>>(); Stack<TreeNode<char>> stack2 = new Stack<TreeNode<char>>(); stack1.Push(treeNode); TreeNode<char> cur = treeNode; while (!(stack1.Count == 0)) { cur = stack1.Pop(); if (cur.LChild != null) { stack1.Push(cur.LChild); } if (cur.RChild != null) { stack1.Push(cur.RChild); } stack2.Push(cur); } while (!(stack2.Count == 0)) { TreeNode<char> node = stack2.Pop(); Console.WriteLine(node.Data); ; } }
過程模擬:
執行結果:
具體過程:
使用一個棧實現
申請一個棧stack,將頭節點壓入stack,同時設置兩個變量 h 和 c,在整個流程中,h表明最近一次彈出並打印的節點,c表明當前stack的棧頂節點,初始時令h爲頭節點,,c爲null;
每次令c等於當前stack的棧頂節點,可是不從stack中彈出節點,此時分一下三種狀況:
(1)若是c的左孩子不爲空,而且h不等於c的左孩子,也不等於c的右孩子,則吧c的左孩子壓入stack中
(2)若是狀況1不成立,而且c的右孩子不爲空,而且h不等於c的右孩子,則把c的右孩子壓入stack中;
(3)若是狀況1和2不成立,則從stack中彈出c並打印,而後令h等於c;
- 一直重複步驟2,直到stack爲空.
代碼實現:
public static void PosOrderTwo(TreeNode<char> treeNode) { if (treeNode == null) { return; } Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>(); stack.Push(treeNode); TreeNode<char> h = treeNode; TreeNode<char> c = null; while (!(stack.Count == 0)) { c = stack.Peek(); //c結點有左孩子 而且 左孩子沒被遍歷(輸出)過 而且 右孩子沒被遍歷過 if (c.LChild != null && h != c.LChild && h != c.RChild) stack.Push(c.LChild); //c結點有右孩子 而且 右孩子沒被遍歷(輸出)過 else if (c.RChild != null && h != c.RChild) stack.Push(c.RChild); //c結點沒有孩子結點 或者孩子結點已經被遍歷(輸出)過 else { TreeNode<char> node = stack.Pop(); Console.WriteLine(node.Data); h = c; } } }
過程模擬:
執行結果:
具體過程:
- 首先申請一個新的隊列,記爲queue;
- 將頭結點head壓入queue中;
- 每次從queue中出隊,記爲node,而後打印node值,若是node左孩子不爲空,則將左孩子入隊;若是node的右孩子不爲空,則將右孩子入隊;
- 重複步驟3,直到queue爲空。
代碼實現:
public static void LevelOrder(TreeNode<char> treeNode) { if(treeNode == null) { return; } Queue<TreeNode<char>> queue = new Queue<TreeNode<char>>(); queue.Enqueue(treeNode); while (queue.Any()) { TreeNode<char> node = queue.Dequeue(); Console.Write(node.Data); if (node.Left != null) { queue.Enqueue(node.Left); } if (node.Right != null) { queue.Enqueue(node.Right); } } }
執行結果:
參考:《大話數據結構》