【Java】 剑指offer(36) 二叉搜索树与双向链表
本文參考自《劍指offer》一書(shū),代碼采用Java語(yǔ)言。
更多:《劍指Offer》Java實(shí)現(xiàn)合集??
題目
輸入一棵二叉搜索樹(shù),將該二叉搜索樹(shù)轉(zhuǎn)換成一個(gè)排序的雙向鏈表。要求不能創(chuàng)建任何新的結(jié)點(diǎn),只能調(diào)整樹(shù)中結(jié)點(diǎn)指針的指向。
思路
二叉搜索樹(shù)、排序鏈表,想到使用中序遍歷。
要實(shí)現(xiàn)雙向鏈表,必須知道當(dāng)前結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)。根據(jù)中序遍歷可以知道,當(dāng)遍歷到根結(jié)點(diǎn)的時(shí)候,左子樹(shù)已經(jīng)轉(zhuǎn)化成了一個(gè)排序的鏈表了,根結(jié)點(diǎn)的前一結(jié)點(diǎn)就是該鏈表的最后一個(gè)結(jié)點(diǎn)(這個(gè)結(jié)點(diǎn)必須記錄下來(lái),將遍歷函數(shù)的返回值設(shè)置為該結(jié)點(diǎn)即可),鏈接根結(jié)點(diǎn)和前一個(gè)結(jié)點(diǎn),此時(shí)鏈表最后一個(gè)結(jié)點(diǎn)就是根結(jié)點(diǎn)了。再處理右子樹(shù),遍歷右子樹(shù),將右子樹(shù)的最小結(jié)點(diǎn)與根結(jié)點(diǎn)鏈接起來(lái)即可。左右子樹(shù)的轉(zhuǎn)化采用遞歸即可。
大概思想再理一下:首先想一下中序遍歷的大概代碼結(jié)構(gòu)(先處理左子樹(shù),再處理根結(jié)點(diǎn),之后處理右子樹(shù)),假設(shè)左子樹(shù)處理完了,就要處理根結(jié)點(diǎn),而根結(jié)點(diǎn)必須知道左子樹(shù)的最大結(jié)點(diǎn),所以要用函數(shù)返回值記錄下來(lái);之后處理右子樹(shù),右子樹(shù)的最小結(jié)點(diǎn)(也用中序遍歷得到)要和根結(jié)點(diǎn)鏈接。
注意搞清楚修改后的中序遍歷函數(shù)的意義(見(jiàn)代碼注釋)
測(cè)試算例
1.功能測(cè)試(一個(gè)結(jié)點(diǎn);左右斜樹(shù);完全二叉樹(shù);普通二叉樹(shù))
2.特殊測(cè)試(根結(jié)點(diǎn)為null)
Java代碼
//題目:輸入一棵二叉搜索樹(shù),將該二叉搜索樹(shù)轉(zhuǎn)換成一個(gè)排序的雙向鏈表。要求 //不能創(chuàng)建任何新的結(jié)點(diǎn),只能調(diào)整樹(shù)中結(jié)點(diǎn)指針的指向。public class ConvertBinarySearchTree {public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}public TreeNode convert(TreeNode head) {if(head==null)return head;TreeNode lastNodeInList=null;lastNodeInList=convertHelper(head,lastNodeInList);TreeNode firstNodeInList=lastNodeInList;while(firstNodeInList.left!=null) {firstNodeInList=firstNodeInList.left;} return firstNodeInList;}//將以node為根結(jié)點(diǎn)的樹(shù)轉(zhuǎn)化為排序鏈表,鏈表頭部與lastNode鏈接,并返回最后一個(gè)結(jié)點(diǎn)private TreeNode convertHelper(TreeNode node,TreeNode lastNode) {//處理左子樹(shù),獲得最大結(jié)點(diǎn)if(node.left!=null)lastNode=convertHelper(node.left, lastNode);//鏈接最大結(jié)點(diǎn)和根結(jié)點(diǎn)node.left=lastNode;if(lastNode!=null)lastNode.right=node;//處理右子樹(shù)lastNode=node;if(node.right!=null)lastNode=convertHelper(node.right, lastNode);return lastNode;} }
上面的代碼是參考《劍指OFFER》寫(xiě)的,下面的代碼是復(fù)習(xí)時(shí)重新寫(xiě)過(guò)的,思路比較簡(jiǎn)潔一點(diǎn)。非遞歸方法的核心是中序遍歷的非遞歸實(shí)現(xiàn)。
public class Solution {/** 遞歸版本* 1.已知函數(shù)返回的是轉(zhuǎn)換好的雙向鏈表頭結(jié)點(diǎn)* 2.左子樹(shù)處理完后與根結(jié)點(diǎn)連接* 3.右子樹(shù)處理,也與根結(jié)點(diǎn)連接* 4.最后返回頭結(jié)點(diǎn)*/public TreeNode Convert(TreeNode root) {if (root == null)return root;// 處理左子樹(shù),獲得左子樹(shù)鏈表的頭結(jié)點(diǎn)TreeNode left = Convert(root.left);TreeNode p = left;if (left != null) {// 找到左子樹(shù)鏈表的末尾結(jié)點(diǎn)while (p.right != null)p = p.right;// 連接結(jié)點(diǎn)p.right = root;root.left = p;}// 處理右子樹(shù),獲得右子樹(shù)鏈表的頭結(jié)點(diǎn)TreeNode right = Convert(root.right);// 連接結(jié)點(diǎn)if (right != null) {root.right = right;right.left = root;}return left == null ? root : left;}/* 非遞歸版本* 1.利用非遞歸中序遍歷來(lái)連接結(jié)點(diǎn)*/public TreeNode Convert1(TreeNode root) {TreeNode head = null;TreeNode pre = null;LinkedList<TreeNode> stack = new LinkedList<>();while (root != null || !stack.isEmpty()) {// 把root當(dāng)作指針使用while (root != null) {stack.push(root);root = root.left;}TreeNode node = stack.pop();if (head == null) {head = node;pre = node;} else {node.left = pre;pre.right = node;pre = node; // 別漏寫(xiě)了}root = node.right;}return head;} }
收獲
題目較復(fù)雜時(shí),不要慌。這道題和中序遍歷有關(guān),把樹(shù)分為三部分:根結(jié)點(diǎn)、左子樹(shù)和右子樹(shù),思考在中序遍歷中根結(jié)點(diǎn)應(yīng)該如何處理,這是關(guān)鍵——要將左子樹(shù)的最大結(jié)點(diǎn)、根結(jié)點(diǎn)、右子樹(shù)的最小結(jié)點(diǎn)鏈接起來(lái)。左右子樹(shù)的處理是相同的,因此采用遞歸。
?
更多:《劍指Offer》Java實(shí)現(xiàn)合集??
轉(zhuǎn)載于:https://www.cnblogs.com/yongh/p/9860700.html
總結(jié)
以上是生活随笔為你收集整理的【Java】 剑指offer(36) 二叉搜索树与双向链表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: quality center 支持的平台
- 下一篇: Java IO流(三)