2018.8.31 《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门
这道题作为8月最后一题可以说是很棒了。这道题同时考察了二叉搜索树和双向链表,实际上如果题目改成顺序的单链表,也是一样的,双向链表无非多了一步。如果用非递归中序遍历,还要用到栈。
当然这道题首先要明白二叉搜索树的特点,二叉搜索树相关的题实际上做过4道了。回顾一下,有总结才会有提升。
1.#数据结构与算法学习笔记#PTA8:实现一棵二叉搜索树(C/C++)
2.#数据结构与算法学习笔记#PTA12:二叉搜索树判断(C/C++)
3.#数据结构与算法学习笔记#剑指Offer22:判断是否二叉搜索树的后序遍历序列 + 测试用例(Java、C/C++)
4.#数据结构与算法学习笔记#PTA16:完全二叉搜索树(C/C++)
回到本题,这题看似很复杂,但是二叉树无非就是四种遍历(先序中序后序层序),挨个尝试总会能够简单化,然后用递归或者循环来做。二叉树的题目无不如此。
思路一:先序遍历
由于我一开始先从根节点开始想,思维直接进入先序遍历,也稍微会复杂一点,但是也可以做。二叉搜索树的根节点左子树值均比根节点小,右子树值均比根节点大。因此根节点左边应该连接左子树的最大值,也即左子树的最右结点值;右边应该连接右子树最小值,也即右子树的最左结点值。
每次先序遍历的递归,先记录下当前结点的左右最值(防止递归过程中子树结点连接改变),递归访问左右子树,将根节点左边连接到左子树最大值,右边连接到右子树最小值即可。因为是先序遍历访问的,实质上在连接过程中是后序的,最后连接根节点。
思路二:递归遍历
中序遍历是比较正统的思路,如果第一开始从链表最小值,也就是最右结点开始考虑的话,可以自然想到中序遍历的做法。中序遍历做法是,定义一个尾结点,每次将尾结点与中序遍历访问的下一个结点双向连接,连接完后更新尾结点。从思想方式上可以分成递归实现和非递归实现,非递归实现需要借助栈完成。
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
Java实现(三种方式):
/**
*
* @author ChopinXBP
* 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
* 要求不能创建任何新的结点,只能调整树中结点指针的指向。
*
*/
import java.util.Stack;
public class Bintree2BinList_25 {
public static class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeNode root = new TreeNode(5);
Init(root);
root = Convert(root);
while(root.right != null){
System.out.print(root.val);
root = root.right;
}
while(root.left != null){
System.out.print(root.val);
root = root.left;
}
}
public static TreeNode Init(TreeNode root){
TreeNode pNode = root;
pNode.left = new TreeNode(1);
pNode.right = new TreeNode(7);
pNode = root.left;
pNode.left = new TreeNode(0);
pNode.right = new TreeNode(3);
pNode = root.right;
pNode.left = new TreeNode(6);
pNode.right = new TreeNode(8);
pNode = root.left.right;
pNode.left = new TreeNode(2);
pNode.right = new TreeNode(4);
pNode = root.right.right;
pNode.right = new TreeNode(9);
return root;
}
/////////////////方法1:先序遍历转换法/////////////////////
public static TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return null;
if(pRootOfTree.left == null && pRootOfTree.right == null) return pRootOfTree;
pRootOfTree = Solution(pRootOfTree);
//将根结点移至表头结点
while(pRootOfTree.left != null){
pRootOfTree = pRootOfTree.left;
}
return pRootOfTree;
}
public static TreeNode Solution(TreeNode root){
if(root == null) return null;
//先记录下左右结点的最大值和最小值用于连接
TreeNode leftnode = LeftChildrightest(root);
TreeNode rightnode = RightChildLeftest(root);
//先序遍历各结点(递归调用转换过程中为后序)
root.left = Solution(root.left);
root.right = Solution(root.right);
//双向链表转换
if(leftnode != null){
leftnode.right = root;
root.left = leftnode;
}
if(rightnode != null){
rightnode.left = root;
root.right = rightnode;
}
return root;
}
//返回左子树最右结点,也即左子树最大值
public static TreeNode LeftChildrightest(TreeNode root){
if(root.left == null)return null;
TreeNode pNode = root.left;
while(pNode.right != null){
pNode = pNode.right;
}
return pNode;
}
//返回右子树最左结点,也即右子树最小值
public static TreeNode RightChildLeftest(TreeNode root){
if(root.right == null)return null;
TreeNode pNode = root.right;
while(pNode.left != null){
pNode = pNode.left;
}
return pNode;
}
/////////////////方法2:中序遍历非递归转换法/////////////////////
//借助栈实现非递归中序遍历。
public TreeNode Convert2(TreeNode root) {
if (root == null)
return null;
Stack stack = new Stack();
TreeNode p = root;
TreeNode tail = null;// 用于保存已成链表的尾部结点
boolean isFirst = true;
while (p != null || !stack.isEmpty()) {
while (p != null) {
stack.push(p);
p = p.left;
}
p = stack.pop();
if (isFirst) {
root = p;// 将中序遍历序列中的第一个节点记为root
tail = root;
isFirst = false;
} else {
tail.right = p;
p.left = tail;
tail = p;
}
p = p.right;
}
return root;
}
/////////////////方法3:中序遍历递归转换法/////////////////////
TreeNode tail = null;
TreeNode realHead = null; //保留链表头结点位置,也就是最左结点位置
public TreeNode Convert3(TreeNode pRootOfTree) {
ConvertSub(pRootOfTree);
return realHead;
}
private void ConvertSub(TreeNode pRootOfTree) {
if (pRootOfTree == null)return;
ConvertSub(pRootOfTree.left);
//每次将上一次已成链表的尾部与当前结点双向连接,移动尾结点
if (tail == null) {
tail = pRootOfTree;
realHead = pRootOfTree;
} else {
tail.right = pRootOfTree;
pRootOfTree.left = tail;
tail = pRootOfTree;
}
ConvertSub(pRootOfTree.right);
}
}
C++实现示例(中序遍历递归转换法):
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr) return nullptr;
TreeNode* pre = nullptr;
convertHelper(pRootOfTree, pre);
TreeNode* res = pRootOfTree;
while(res ->left)
res = res ->left;
return res;
}
void convertHelper(TreeNode* cur, TreeNode*& pre)
{
if(cur == nullptr) return;
convertHelper(cur ->left, pre);
cur ->left = pre;
if(pre) pre ->right = cur;
pre = cur;
convertHelper(cur ->right, pre);
}
};
测试代码:
// ====================测试代码====================
void PrintDoubleLinkedList(BinaryTreeNode* pHeadOfList)
{
BinaryTreeNode* pNode = pHeadOfList;
printf("The nodes from left to right are:\n");
while(pNode != NULL)
{
printf("%d\t", pNode->m_nValue);
if(pNode->m_pRight == NULL)
break;
pNode = pNode->m_pRight;
}
printf("\nThe nodes from right to left are:\n");
while(pNode != NULL)
{
printf("%d\t", pNode->m_nValue);
if(pNode->m_pLeft == NULL)
break;
pNode = pNode->m_pLeft;
}
printf("\n");
}
void DestroyList(BinaryTreeNode* pHeadOfList)
{
BinaryTreeNode* pNode = pHeadOfList;
while(pNode != NULL)
{
BinaryTreeNode* pNext = pNode->m_pRight;
delete pNode;
pNode = pNext;
}
}
void Test(char* testName, BinaryTreeNode* pRootOfTree)
{
if(testName != NULL)
printf("%s begins:\n", testName);
PrintTree(pRootOfTree);
BinaryTreeNode* pHeadOfList = Convert(pRootOfTree);
PrintDoubleLinkedList(pHeadOfList);
}
// 10
// / \
// 6 14
// /\ /\
// 4 8 12 16
void Test1()
{
BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
BinaryTreeNode* pNode6 = CreateBinaryTreeNode(6);
BinaryTreeNode* pNode14 = CreateBinaryTreeNode(14);
BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
BinaryTreeNode* pNode8 = CreateBinaryTreeNode(8);
BinaryTreeNode* pNode12 = CreateBinaryTreeNode(12);
BinaryTreeNode* pNode16 = CreateBinaryTreeNode(16);
ConnectTreeNodes(pNode10, pNode6, pNode14);
ConnectTreeNodes(pNode6, pNode4, pNode8);
ConnectTreeNodes(pNode14, pNode12, pNode16);
Test("Test1", pNode10);
DestroyList(pNode4);
}
// 5
// /
// 4
// /
// 3
// /
// 2
// /
// 1
void Test2()
{
BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);
ConnectTreeNodes(pNode5, pNode4, NULL);
ConnectTreeNodes(pNode4, pNode3, NULL);
ConnectTreeNodes(pNode3, pNode2, NULL);
ConnectTreeNodes(pNode2, pNode1, NULL);
Test("Test2", pNode5);
DestroyList(pNode1);
}
// 1
// \
// 2
// \
// 3
// \
// 4
// \
// 5
void Test3()
{
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);
BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
ConnectTreeNodes(pNode1, NULL, pNode2);
ConnectTreeNodes(pNode2, NULL, pNode3);
ConnectTreeNodes(pNode3, NULL, pNode4);
ConnectTreeNodes(pNode4, NULL, pNode5);
Test("Test3", pNode1);
DestroyList(pNode1);
}
// 树中只有1个结点
void Test4()
{
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);
Test("Test4", pNode1);
DestroyList(pNode1);
}
// 树中没有结点
void Test5()
{
Test("Test5", NULL);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
return 0;
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#