在二叉搜索树(BST)中查找第K个大的结点之非递归实现
一个被广泛使用的面试题: 给定一个二叉搜索树,请找出其中的第K个大的结点。
PS:我第一次在面试的时候被问到这个问题而且让我直接在白纸上写的时候,直接蒙圈了,因为没有刷题准备,所以就会有伤害。知耻而后勇,于是我回家花了两个半小时(在不参考任何书本和网路上的源码的前提下),从构建BST开始,到实现中序遍历,最后用递归方法写出bst_findKthNode()并用gdb调试成功。 不过,使用递归实现这个实在是比较low,所以这个周末我决定用非递归方法实现。
先贴一下我的递归实现 (个人觉得比较low, 虽然实现了,但是不满意)
/*
* Find the Kth Node in BST, K = 1, 2, ...
*/
int
bst_findKthNode(bst_node_t *root, key_t *key, unsigned int k)
{
if (root == NULL)
return -; if (root->left != NULL && k > )
k = bst_findKthNode(root->left, key, k); if (--k == ) {
*key = root->key;
return ;
} if (root->right != NULL && k > )
k = bst_findKthNode(root->right, key, k); return k;
}
下面的代码是我写的非递归实现。
/*
* Find the Kth Node in BST, K = 1, 2, ...
*/
bst_node_t *
bst_findKthNode(bst_node_t *root, unsigned int k)
{
bst_node_t *kp = NULL; if (root == NULL)
return NULL; (void) stack_init(STACK_SIZE); while (root != NULL || !stack_isEmpty()) {
if (root != NULL) {
push((uintptr_t)root);
root = root->left;
continue;
} pop((uintptr_t *)(&root));
if (--k == ) {
kp = root;
break;
} root = root->right;
} stack_fini(); return kp;
}
使用Meld进行diff后的截图,

注意: 题目请参见《剑指Offer》(何海涛著)面试题63: 二叉搜索树的第k个结点, 其cpp答案在这里。
最后,贴出完整的代码和测试运行结果。
o libstack.h 和 libstack.c (参见 将递归函数非递归化的一般方法(cont) 一文)
o libbst.h
#ifndef _LIBBST_H
#define _LIBBST_H #ifdef __cplusplus
extern "C" {
#endif #define STACK_SIZE 16 typedef int key_t; typedef struct bst_node_s {
key_t key;
struct bst_node_s *left;
struct bst_node_s *right;
} bst_node_t; int bst_init(bst_node_t **root, key_t a[], size_t n);
void bst_fini(bst_node_t *root);
void bst_walk(bst_node_t *root);
bst_node_t *bst_findKthNode(bst_node_t *root, unsigned int k); #ifdef __cplusplus
}
#endif #endif /* _LIBBST_H */
o libbst.c
#include <stdio.h>
#include <stdlib.h>
#include "libbst.h"
#include "libstack.h" static int bst_add_node(bst_node_t **root, key_t key); int
bst_init(bst_node_t **root, key_t a[], size_t n)
{
*root = NULL;
for (int i = ; i < n; i++) {
if (bst_add_node(root, a[i]) != )
return -;
} return ;
} #define UMEM_FREE_PATTERN 0xdeadbeefdeadbeefULL
static inline void
BST_DESTROY_NODE(bst_node_t *p)
{
p->left = NULL;
p->right = NULL;
*(unsigned long long *)p = UMEM_FREE_PATTERN;
} void
bst_fini(bst_node_t *root)
{
if (root == NULL)
return; bst_fini(root->left);
bst_fini(root->right); BST_DESTROY_NODE(root);
free(root);
} static int
bst_add_node(bst_node_t **root, key_t key)
{
bst_node_t *leaf = NULL;
leaf = (bst_node_t *)malloc(sizeof (bst_node_t));
if (leaf == NULL) {
fprintf(stderr, "failed to malloc\n");
return -;
} /* init leaf node */
leaf->key = key;
leaf->left = NULL;
leaf->right = NULL; /* add leaf node to root */
if (*root == NULL) { /* root node does not exit */
*root = leaf;
} else {
bst_node_t **pp = NULL;
while () {
if (leaf->key < (*root)->key)
pp = &((*root)->left);
else
pp = &((*root)->right); if (*pp == NULL) {
*pp = leaf;
break;
} root = pp;
}
} return ;
} void
bst_walk(bst_node_t *root)
{
if (root == NULL)
return; (void) stack_init(STACK_SIZE); while (root != NULL || !stack_isEmpty()) {
if (root != NULL) {
push((uintptr_t)root);
root = root->left;
continue;
} pop((uintptr_t *)(&root));
printf("%d\n", root->key); root = root->right;
} stack_fini();
} /*
* Find the Kth Node in BST, K = 1, 2, ...
*/
bst_node_t *
bst_findKthNode(bst_node_t *root, unsigned int k)
{
bst_node_t *kp = NULL; if (root == NULL)
return NULL; (void) stack_init(STACK_SIZE); while (root != NULL || !stack_isEmpty()) {
if (root != NULL) {
push((uintptr_t)root);
root = root->left;
continue;
} pop((uintptr_t *)(&root));
if (--k == ) {
kp = root;
break;
} root = root->right;
} stack_fini(); return kp;
}
o foo.c (简单测试)
#include <stdio.h>
#include <stdlib.h>
#include "libbst.h" int
main(int argc, char *argv[])
{
if (argc != ) {
fprintf(stderr, "Usage: %s <Kth>\n", argv[]);
return -;
} int a[] = {, , , , , , , , };
int n = sizeof (a) / sizeof (int); bst_node_t *root = NULL;
bst_init(&root, a, n); bst_walk(root); unsigned int k = atoi(argv[]);
bst_node_t *p = NULL;
if ((p = bst_findKthNode(root, k)) == NULL) {
printf("\nOops, the %dth node not found\n", k);
goto done;
}
printf("\nWell, the %dth node found, its key is %d\n", k, p->key); done:
bst_fini(root); return ;
}
o Makefile
CC = gcc
CFLAGS = -g -Wall -std=gnu99 -m32
INCS = TARGET = foo all: ${TARGET} foo: foo.o libstack.o libbst.o
${CC} ${CFLAGS} -o $@ $^ foo.o: foo.c
${CC} ${CFLAGS} -c $< ${INCS} libstack.o: libstack.c libstack.h
${CC} ${CFLAGS} -c $< libbst.o: libbst.c libbst.h
${CC} ${CFLAGS} -c $< clean:
rm -f *.o
clobber: clean
rm -f ${TARGET}
o 编译并测试运行
$ make
gcc -g -Wall -std=gnu99 -m32 -c foo.c
gcc -g -Wall -std=gnu99 -m32 -c libstack.c
gcc -g -Wall -std=gnu99 -m32 -c libbst.c
gcc -g -Wall -std=gnu99 -m32 -o foo foo.o libstack.o libbst.o $ ./foo Well, the 6th node found, its key is $ ./foo | egrep 'Oops,'
Oops, the 16th node not found
$
扩展题目: "寻找两个数组的中位数"。 题目描述如下:
有两个数组, 第一个数组a里的元素按照升序排列, e.g. int a[] = {10, 30, 40, 70, 80, 90};
第二个数组b里的元素按照降序排列, e.g. int b[] = {60, 50, 30, 20, 10};
请寻找数组a和b的合集的中位数,e.g. 50。
解决方案:
- 使用数组a构建一个无重复key的BST
- 将数组b里的元素加入BST (若某个元素已经在BST中存在,不予加入)
- 设BST中的所有结点总数为N (a) 若N为偶数, 查找第K, K+1个元素 (K=N/2) 并求其平均值; (b) 若N为奇数, 查找第K+1个元素(K=N/2)。
关于此题目的详细描述和解决方案请参见 《剑指Offer》(何海涛著)面试题64: 数据流中的中位数。
在二叉搜索树(BST)中查找第K个大的结点之非递归实现的更多相关文章
- C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解
剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...
- 萌新笔记之二叉搜索树(BST)
前言,以前搞过线段树,二叉树觉得也就那样= =.然后数据结构的课也没怎么听过,然后下周期中考... 本来以为今天英语考完可以好好搞ACM了,然后这个数据结构期中考感觉会丢人,还是好好学习一波. 二叉搜 ...
- 二叉搜索树(BST)---python实现
github:代码实现 本文算法均使用python3实现 1. 二叉搜索树定义 二叉搜索树(Binary Search Tree),又名二叉排序树(Binary Sort Tree). 二叉搜 ...
- 给定一个二叉搜索树(BST),找到树中第 K 小的节点
问题:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深技术专家. 考察点: 1. 基础数据结构的理解和编码能力 2. 递归使用 参考答案 ...
- 二叉搜索树 (BST) 的创建以及遍历
二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...
- 二叉搜索树(BST)学习笔记
BST调了一天,最后遍历参数错了,没药救了-- 本文所有代码均使用数组+结构体,不使用指针! 前言--BFS是啥 BST 二叉搜索树是基于二叉树的一种树,一种特殊的二叉树. 二叉搜索树要么是一颗空树, ...
- 二叉搜索树(BST)
(第一段日常扯蛋,大家不要看)这几天就要回家了,osgearth暂时也不想弄了,毕竟不是几天就能弄出来的,所以打算过完年回来再弄.这几天闲着也是闲着,就掏出了之前买的算法导论看了看,把二叉搜索树实现了 ...
- hdu 3791:二叉搜索树(数据结构,二叉搜索树 BST)
二叉搜索树 Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submiss ...
- 数据结构---二叉搜索树BST实现
1. 二叉查找树 二叉查找树(Binary Search Tree),也称为二叉搜索树.有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一 ...
随机推荐
- 朴素贝页斯分类法 c++实现
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法.对于搞机器学习的同学们来说,这是相对简单但效果较好的模型. 朴素贝叶斯方法的理论 设输入为n维特征向量X={x1,x2,...,xn},输出为 ...
- Jquery框架
现在Jquery框架对于开发人员基本上是无人不知,无人不晓了,用起来十分的方便,特别是选择器十分强大,提高了我们的开发速度.但是好多人也只是停留在了会用的基础上,我个人觉得会用一个框架不算什么,只能说 ...
- golang切片slice
切片slice是引用类型 len()函数获取元素的个数 cap()获取数组的容量 1.申明方式 (1)var a []int 与数组不同的是他不申明长度(2)s2 := make([]int, 3, ...
- python读写protobuf
0. 前期准备 官方protobuf定义 https://code.google.com/p/protobuf/ python使用指南 https://developers.google. ...
- C/C++基础知识总结——多态性
1. 多态性的概述 1.1 多态是指同样的消息被不同类型的对象接收时导致不同的行为.所谓消息是指对垒的成员函数的调用,不同行为是指不同的实现. 1.2 多态的实现 (1) 实现角度讲多态可分为两类:编 ...
- 在Netbeans上配置Android开发环境
在NetBeans下开发Android的所需要的基本条件:NetBeans(包含JDK)+Android SDK+NBAndroid(为Netbeans设计的Android 开发插件) 详情:http ...
- NuGet的本地服务器安装与Package的发布
NuGet的本地服务器安装与Package的发布 主要的步骤是按照下面的例子来做的: NuGet学习笔记(1)——初识NuGet及快速安装使用 NuGet学习笔记(2)——使用图形化界面打包自己的类库 ...
- [置顶] 学习JDK源码:可进一步优化的代码
1.参数化类型的构造函数比较啰嗦 new HashMap<String, List<String>>() 如果你调用参数化类的构造函数,那么很不幸,你必须要指定类型参数,即便上 ...
- storm源码之巧用java反射反序列化clojure的defrecord获取属性值
[原创]storm源码之巧用java反射反序列化clojure的defrecord获取属性值 [原创]storm源码之巧用java反射反序列化clojure的defrecord获取属性值 storm源 ...
- C++应用程序在Windows下的编译、链接(一)概述
C++应用程序在Windows下的编译.链接(一)概述 本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 c ...