#数据结构与算法学习笔记#剑指Offer25:二叉搜索树转双向链表+先序与中序遍历做法+非递归中序遍历+测试用例(Java、C/C++)

版权声明:本文为博主NJU_ChopinXBP原创文章,发表于CSDN,仅供交流学习使用,转载请私信或评论联系,未经博主允许不得转载。感谢您的评论与点赞。 https://blog.csdn.net/qq_20304723/article/details/82255369

2018.8.31

这道题作为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<TreeNode> stack = new Stack<TreeNode>();
		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一秒钟。留个言点个赞呗,谢谢你#

猜你喜欢

转载自blog.csdn.net/qq_20304723/article/details/82255369