Day62:二叉搜索树的第k个结点

时间:2022-07-24
本文章向大家介绍Day62:二叉搜索树的第k个结点,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

剑指Offer_编程题——二叉搜索树的第k个结点

题目描述:

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

具体要求:

时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M

具体实现:

背景知识介绍:   在前面的文章中我们详细的介绍了二叉搜索树的相关概念,接下来我们介绍二叉搜索树的具体应用。二叉排序树又称二叉查找树(二叉搜索树),它或是一棵空的二叉树,或是具有下列性质的二叉树: 1、若它的左子树不空,则左子树上所有节点的值均小于根节点的值 2、若它的右子树不空,则右子树上所有节点的值均大于根节点的值 3、它的左右子树也都是二叉排序树   对一个二叉树进行中序遍历,如果是单调递增的,则可以说明这个树是二叉搜索树。二叉搜索树的基本操作有:查找 key(其中:右树的值 > 根节点的值 > 左树的值)、插入、删除等。以下就是一颗典型的搜索二叉树:

思路一:   本题很明显考察的就是二叉树的中序遍历,因此在做题之前,我们先给大家详细介绍二叉树的后序遍历。在前面的文章中给大家介绍过二叉树的遍历。因此我们就不详细给大家介绍,如果想要了解二叉树的遍历可以看此文章。根据我们前面对二叉搜索树的介绍,本题很明显用到中序遍历,具体的实现思路如下: 1、二叉搜素树的中序遍历访问队列,自然的满足升序排序的条件。 2、中序遍历二叉搜索树,暂存输出队列。 3、 从保存的输出队列中找到满足条件的值。   我们分别用java和python两种编程语言将其实现: 1、首先我们用java将其实现:

import java.util.ArrayList;
public class Solution{
	ArrayList<TreeNode> arr = new ArrayList<>();
	TreeNode KthNode(TreeNode pRoot, int k){
		if(pRoot == null || k == 0)
			return null;
		inOrder(pRoot);
		TreeNode tr = pRoot;
		if(k <= arr.size()){
			tr = arr.get(k - 1);
		}else{
			return null;
		}
		return tr;
	}
	public void inOrder(TreeNode root){
		if(root == null)
			return;
		if(root.left != null)
			inOrder(root.left);
		arr.add(root);
		if(root.right != null)
			inOrder(root.right);
	}
}

代码效果图如图所示:

  正如前面说的那样,如果在牛客网中,我们可以直接通过,但是如果是本地的编译器,则还需要定义其结点,具体结点的实现定义用java实现如下:

public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    TreeNode next = null;
    TreeNode(int val) {
        this.val = val;
    }
}

2、接下来我们用python将其实现

class Solution:
	def KthNode(self, pRoot, k):
		if k <= 0:
			return None
		res = []
		self.inOrder(pRoot, res)
		if len(res) < k:
			return None
		return res[k - 1]
	def inOrder(self, root, res):
		if not root:
			return
		if root.left:
			self.inOrder(root.left, res)
		res.append(root)
		if root.right:
			self.inOrder(root.right, res)

代码效果图如图所示:

  但是在本地编译器中我们还需要定义树结构才能正常运行,具体用python定义树结构实现如下:

class TreeNode:
	def __init__(self, pNode):
		self.val = x
		self.left = None
		self.right = None

思路二:   从上述的题目可知,本题实际就是考察中序遍历。不同之处仅仅在于:中序遍历返回二叉树中序遍历后所有节点的值的列表,而这道题返回中序遍历过程中填入列表的第k个节点。我们当然可以先对平衡二叉树进行中序遍历,在返回的列表中找到第k个节点;但很显然还有更直接的方式,即中序遍历到需要的位置后立即返回,而不需要遍历完整棵二叉树。具体过程如下: 1、改进思路1,不再暂存所有输出。 2、改为设置一个计数器。中序遍历过程中,累加计算访问过的节点数目,当计数器等于要求的k时,则返回该节点。   具体我们用java和python将其实现: 1、首先我们用java将其实现:

public class Solution{
	int index = 0;
	TreeNode KthNode(TreeNode root, int k){
		if(root != null){
			TreeNode node = KthNode(root.left, k);
			if(node != null){
				return node;
			}
			index++;
			if(index == k){
				return root;
			}
			node = KthNode(root.right, k);
			if(node != null)
				return node;
		}
		return null;
	}
}

代码效果图如图所示:

2、接下来我们用Python将其实现:

class Solution:
	count = 0
	def KthNode(self, pRoot, k):
		if not pRoot:
			return None
		node = self.KthNode(pRoot.left, k)
		if node:
			return node
		self.count += 1
		if self.count == k:
			return pRoot
		node = self.KthNode(pRoot.right, k)
		if node:
			return node

代码效果图如图所示:

思路三:   其实也是对思路一的改进,我们两种思路均是用的递归,我们思路三可以用非递归的方法实现中序遍历。接下来我们用java将其实现:

import java.util.Stack;
public class Solution{
	int count = 0;
	TreeNode KthNode(TreeNode pRoot, int k){
		if(count > k || pRoot == null)
			return null;
		TreeNode p = pRoot;
		Stack<TreeNode> LDRStack = new Stack<>();
		TreeNode kthNode = null;
		while(p != null || !LDRStack.isEmpty()){
			while(p != null){
				LDRStack.push(p);
				p = p.left;
			}
			TreeNode node = LDRStack.pop();
			System.out.println(node.val+",");
			count++;
			if(count == k){
				kthNode = node;
				break;
			}
			p = node.right;
		}
		return kthNode;
	}
}

代码效果图如图所示:

总结

  本题主要通过二叉树搜索树的第k个结点来考察我们对二叉搜索树的理解以及对二叉树中的中序遍历的掌握情况。本文给出了三种解题思路,实质上就是实现二叉树的中序遍历的过程,其中有递归,并且分别用python和java两种编程语言将其实现。另外也通过栈的“先进后出”的特性来实现二叉树中序遍历的非递归。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!

参考文献

[1] 负雪明烛 [2] goddaniel [3] ouyangyanlan