Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers as synonymous to the previous and next pointers in a doubly-linked list.html
Let's take the following BST as an example, it may help you understand the problem better:node
We want to transform this BST into a circular doubly linked list. Each node in a doubly linked list has a predecessor and successor. For a circular doubly linked list, the predecessor of the first element is the last element, and the successor of the last element is the first element.git
The figure below shows the circular doubly linked list for the BST above. The "head" symbol means the node it points to is the smallest element of the linked list.github
Specifically, we want to do the transformation in place. After the transformation, the left pointer of the tree node should point to its predecessor, and the right pointer should point to its successor. We should return the pointer to the first element of the linked list.ide
The figure below shows the transformed BST. The solid line indicates the successor relationship, while the dashed line means the predecessor relationship.函數
這道題給了一個二叉搜索樹,讓咱們將其轉化爲雙向鏈表。而且題目中給了一個帶圖的例子,幫助理解。題目自己並不難理解,仔細觀察下給的示例圖。首先,轉化成雙向鏈表的每一個結點都有 left 和 right 指針指向左右兩個結點,無論其原來是不是葉結點仍是根結點,轉換後通通沒有區別。其次,這是個循環雙向鏈表,即首尾結點是相連的,原先的二叉搜索樹中的最左結點和最右結點,如今也互相鏈接起來了。最後,返回的結點再也不是原二叉搜索樹的根結點 root 了,而是最左結點,即最小值結點。this
好,發現了上述規律後,來考慮如何破題。根據博主多年經驗,跟二叉搜索樹有關的題,確定要利用其性質,即左<根<右,即左子結點值小於根結點值小於右子結點值。並且十有八九都得用中序遍從來解,由於中序遍歷的順序就是左根右啊,跟性質吻合。觀察到原二叉搜索樹中結點4鏈接着結點2和結點5,而在雙向鏈表中,鏈接的是結點3和結點5,這就是爲啥要用中序遍歷了,由於只有中序遍歷,結點3以後纔會遍歷到結點4,這時候能夠將結點3和結點4串起來。決定了用中序遍歷以後,就要考慮是迭代仍是遞歸的寫法,博主建議寫遞歸的,通常寫起來都比較簡潔,並且遞歸是解樹類問題的神器啊,十有八九都是用遞歸,必定要熟練掌握。再寫中序遍歷以前,其實還有難點,由於須要把相鄰的結點鏈接起來,因此須要知道上一個遍歷到的結點是什麼,因此用一個變量 pre,來記錄上一個遍歷到的結點。還須要一個變量 head,來記錄最左結點,這樣的話,在遞歸函數中,先判空,以後對左子結點調用遞歸,這樣會先一直遞歸到最左結點,此時若是 head 爲空的話,說明當前就是最左結點,賦值給 head 和 pre,對於以後的遍歷到的結點,那麼能夠和 pre 相互鏈接上,而後 pre 賦值爲當前結點 node,再對右子結點調用遞歸便可,參見代碼以下:spa
解法一:指針
class Solution { public: Node* treeToDoublyList(Node* root) { if (!root) return NULL; Node *head = NULL, *pre = NULL; inorder(root, pre, head); pre->right = head; head->left = pre; return head; } void inorder(Node* node, Node*& pre, Node*& head) { if (!node) return; inorder(node->left, pre, head); if (!head) { head = node; pre = node; } else { pre->right = node; node->left = pre; pre = node; } inorder(node->right, pre, head); } };
雖說樹類問題首推遞歸解法,可是中序遍歷是能夠用迭代來寫的,能夠參見博主以前的博客 Binary Tree Inorder Traversal。迭代寫法借用了棧,其實總體思路和遞歸解法沒有太大的區別,遞歸的本質也是將斷點存入棧中,以便以後能夠返回,這裏就很少講解了,能夠參見上面的講解,參見代碼以下:code
解法二:
class Solution { public: Node* treeToDoublyList(Node* root) { if (!root) return NULL; Node *head = NULL, *pre = NULL; stack<Node*> st; while (root || !st.empty()) { while (root) { st.push(root); root = root->left; } root = st.top(); st.pop(); if (!head) head = root; if (pre) { pre->right = root; root->left = pre; } pre = root; root = root->right; } head->left = pre; pre->right = head; return head; } };
這道題還有一種使用分治法 Divide and Conquer 來作的方法。分治法,顧名思義,就是把一項任務分紅兩半,用相同的邏輯去分別處理,以後再粘合起來。混合排序 Merge Sort 用的也是這種思路。這裏能夠對左右子結點調用遞歸函數,suppose 咱們獲得了兩個各自循環的有序雙向鏈表,而後把根結點跟左右子結點斷開,將其左右指針均指向本身,這樣就造成了一個單個結點的有序雙向鏈表,雖然只是個光桿司令,但人家仍然是有序雙向鏈表,不是沙雕,就問你叼不叼。那麼此時只要再寫一個鏈接兩個有序雙向鏈表的子函數,就能夠將這三個有序雙向鏈表按順序連接起來了。
而連接兩個有序雙向鏈表的子函數也簡單,首先判空,若一個爲空,則返回另外一個。若是兩個都不爲空,則把第一個鏈表的尾結點的右指針鏈上第二個鏈表的首結點,同時第二個鏈表的首結點的左指針鏈上第一個鏈表的尾結點。同理,把第二個鏈表的尾結點的右指針鏈上第一個鏈表的首結點,同時第一個鏈表的首結點的左指針鏈上第二個鏈表的尾結點。有木有讀暈,能夠本身畫圖,其實很好理解的誒,參見代碼以下:
解法三:
class Solution { public: Node* treeToDoublyList(Node* root) { if (!root) return NULL; Node *leftHead = treeToDoublyList(root->left); Node *rightHead = treeToDoublyList(root->right); root->left = root; root->right = root; return connect(connect(leftHead, root), rightHead); } Node* connect(Node* node1, Node* node2) { if (!node1) return node2; if (!node2) return node1; Node *tail1 = node1->left, *tail2 = node2->left; tail1->right = node2; node2->left = tail1; tail2->right = node1; node1->left = tail2; return node1; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/426
相似題目:
參考資料:
https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/