zoukankan      html  css  js  c++  java
  • 【Java】 剑指offer(36) 二叉搜索树与双向链表

    本文参考自《剑指offer》一书,代码采用Java语言。

    更多:《剑指Offer》Java实现合集  

    题目 

      输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

    思路

      二叉搜索树、排序链表,想到使用中序遍历。

      要实现双向链表,必须知道当前结点的前一个结点。根据中序遍历可以知道,当遍历到根结点的时候,左子树已经转化成了一个排序的链表了,根结点的前一结点就是该链表的最后一个结点(这个结点必须记录下来,将遍历函数的返回值设置为该结点即可),链接根结点和前一个结点,此时链表最后一个结点就是根结点了。再处理右子树,遍历右子树,将右子树的最小结点与根结点链接起来即可。左右子树的转化采用递归即可。

      大概思想再理一下:首先想一下中序遍历的大概代码结构(先处理左子树,再处理根结点,之后处理右子树),假设左子树处理完了,就要处理根结点,而根结点必须知道左子树的最大结点,所以要用函数返回值记录下来;之后处理右子树,右子树的最小结点(也用中序遍历得到)要和根结点链接。

      注意搞清楚修改后的中序遍历函数的意义(见代码注释)

    测试算例 

      1.功能测试(一个结点;左右斜树;完全二叉树;普通二叉树)

      2.特殊测试(根结点为null)

    Java代码

    //题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求
    //不能创建任何新的结点,只能调整树中结点指针的指向。
    
    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为根结点的树转化为排序链表,链表头部与lastNode链接,并返回最后一个结点
    	private TreeNode convertHelper(TreeNode node,TreeNode lastNode) {
    		//处理左子树,获得最大结点
    		if(node.left!=null)
    			lastNode=convertHelper(node.left, lastNode);
    		//链接最大结点和根结点
    		node.left=lastNode;
    		if(lastNode!=null)
    			lastNode.right=node;
    		//处理右子树
    		lastNode=node;
    		if(node.right!=null)
    			lastNode=convertHelper(node.right, lastNode);
    		return lastNode;
    	}
    }
    

      

    上面的代码是参考《剑指OFFER》写的,下面的代码是复习时重新写过的,思路比较简洁一点。非递归方法的核心是中序遍历的非递归实现

    public class Solution {
    	/*
    	* 递归版本
    	* 1.已知函数返回的是转换好的双向链表头结点
    	* 2.左子树处理完后与根结点连接
    	* 3.右子树处理,也与根结点连接
    	* 4.最后返回头结点
    	*/
    	public TreeNode Convert(TreeNode root) {
    		if (root == null)
    			return root;
    		// 处理左子树,获得左子树链表的头结点
    		TreeNode left = Convert(root.left);
    		TreeNode p = left;
    		if (left != null) {
    			// 找到左子树链表的末尾结点
    			while (p.right != null)
    				p = p.right;
    			// 连接结点
    			p.right = root;
    			root.left = p;
    		}
    		// 处理右子树,获得右子树链表的头结点
    		TreeNode right = Convert(root.right);
    		// 连接结点
    		if (right != null) {
    			root.right = right;
    			right.left = root;
    		}
    		return left == null ? root : left;
    	}
    
    	/* 非递归版本
    	 * 1.利用非递归中序遍历来连接结点
    	 */
    	public TreeNode Convert1(TreeNode root) {
    		TreeNode head = null;
    		TreeNode pre = null;
    		LinkedList<TreeNode> stack = new LinkedList<>();
    		while (root != null || !stack.isEmpty()) {
    			// 把root当作指针使用
    			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; // 别漏写了
    			}
    			root = node.right;
    		}
    		return head;
    	}
    }
    

      

    收获

      题目较复杂时,不要慌。这道题和中序遍历有关,把树分为三部分:根结点、左子树和右子树,思考在中序遍历中根结点应该如何处理,这是关键——要将左子树的最大结点、根结点、右子树的最小结点链接起来。左右子树的处理是相同的,因此采用递归。

    更多:《剑指Offer》Java实现合集  

      

  • 相关阅读:
    sencha touch 扩展篇之将sencha touch打包成安装程序(上)- 使用sencha cmd打包安装程序
    sencha touch 扩展篇之使用sass自定义主题样式 (下)通过css修改官方组件样式以及自定义图标
    一个不错的android组件的网站
    sencha touch 扩展篇之使用sass自定义主题样式 (上)使用官方的api修改主题样式
    sencha touch 入门系列 (九) sencha touch 布局layout
    面试题总结
    国外接活网站Elance, Freelancer和ScriptLance的介绍和对比
    sencha touch 入门系列 扩展篇之sencha touch 项目打包压缩
    Android Design Support Library——Navigation View
    设计模式——命令模式
  • 原文地址:https://www.cnblogs.com/yongh/p/9860700.html
Copyright © 2011-2022 走看看