在二叉搜索树(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),是指一 ...
随机推荐
- 在Visual Studio中Git的基本使用
什么是Git Github : https://github.com/git Pro Git: http://git-scm.com/book Git 是一款免费的.开源的.分布式的版本控制系统.旨在 ...
- Js模块模式
模块模式 索引 引子 什么是模块模式 命名空间模式 声明依赖 私有和特权成员 即时函数 揭示模块模式 结语 引子 这篇算是对第9篇中内容的发散和补充,当时我只是把模块模式中的一些内容简单的归为函数篇中 ...
- JAVA修饰符类型(转帖)
JAVA修饰符类型(public,protected,private,friendly) public的类.类属变量及方法,包内及包外的任何类均可以访问:protected的类.类属变量及方法,包内的 ...
- twisted学习笔记No.3 Web Clients
原创博文,转载请注明出处. 这一章我们学习利用twisted建立web 客户端. twisted.web.client.getPage用来异步下载一个页面,并且返回一个deferred from tw ...
- 苹果APNs’ device token特性和过期更新
APNs全名是Apple Push Notification Service.用iPhone的应该都习惯了,每次安装完一个新应用启动后,几乎都会弹出个警告框,“XXX应用”想要给您发送推送通知.这个警 ...
- cubieboard 配置WIFI
1.先装好驱动程序 2.ifconfig查看无线网卡的名称,比如 3.修改/etc/networking/interfaces,配置 wpa-essid填写WIFI名称 wpa-psk填写密码 4. ...
- WCF学习笔记之传输安全
WCF学习笔记之传输安全 最近学习[WCF全面解析]下册的知识,针对传输安全的内容做一个简单的记录,这边只是简单的记录一些要点:本文的内容均来自[WCF全面解析]下册: WCF的传输安全主要涉及认证. ...
- AOP in Spring
AOP in Spring 是不是已经对包裹在每个业务周围的异常处理.事务管理.性能监控.日志记录等重复出现的代码感到厌倦,那么是时候轮到AOP出场了.不得不承认程序员的惰性有时候会是一件好事(毕竟提 ...
- JavaEE:XML解析
XML解析技术概述1.XML 技术主要企业应用1)存储和传输数据 2)作为框架的配置文件2.使用xml 存储和传输数据涉及到以下两点1)通过程序生成xml2)读取xml 中数据 ---- xml 解析 ...
- Winform DataGridView CheckBoxColumn c# 单选 解决方案
这个问题由来已久,我最近在工作中也遇到了这个问题,不过属于这个问题比较简单初级的涉及. 发现网上对这个问题的解决方案很多不对,答非所问. 所以这里将我测试成功的解决方案记录下来. 首先,DataGri ...