题记
之前对二叉查找树的基本操作写了一篇文章《二叉查找树》,觉得还不过瘾。毕竟还没有实践。所以找了几个笔试题做做。小结如下:
经典题1:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。
比如,二叉查找树的结构为
10
/ \
6 14
/ \ / \
4 8 12 16
调整后的结果是 4=6=8=10=12=14=16
经典题2:
输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。用递归和循环两种方法完成树的镜像转换。
比如
10
/ \
6 14
/ \ / \
4 8 12 16
变成
10
/ \
14 6
/ \ / \
16 12 8 4
以上两题的二叉树结构都是
struct BSTreeNode // a node in the binary search tree (BST)
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
请先自己试着解答,然后看下面的分析和代码!【注意,本代码最后没有对bstree的内存释放!仅做参考】
题一:分析部分请见注释!!
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct BSTreeNode{
int m_value;
struct BSTreeNode *m_pLeft;
struct BSTreeNode *m_pRight;
}BSTreeNode;
//生成二叉查找树
BSTreeNode *insert(BSTreeNode *tree,BSTreeNode *n){
if(tree == NULL){
tree = n;
}else if(n->m_value < tree->m_value){
tree->m_pLeft = insert(tree->m_pLeft,n);
}else{
tree->m_pRight = insert(tree->m_pRight,n);
}
return tree;
}
//方法一:递归思想。
//假设节点左右子树都已经是"已排序的双向链表",比如左子树链表指针为tLeft,右子树链表指针为tRight
//那么,只需要把此时的节点加入到这个链表里,最后返回指向这个调整后的链表即可!
//参数含义:
//cnode:当前处理的节点
//asLeft: 值取-1,0或1。-1表示无父节点;1表示当前节点是其父节点的左孩子;0表示是其父节点的右孩子;
//之所以asLeft这样取值,是因为最后返回的指针会指向链表的尾部或头部(尾部是cnode的前驱,头部是cnode的后继)!需要用它来判断!
//连接左孩子时需要链表的尾指针(tLeft),连接右孩子时需要链表的头指针(tRight)!
BSTreeNode * convert(BSTreeNode *cnode,int asLeft){
if(cnode == NULL){
return NULL;
}
BSTreeNode *tLeft=NULL, *tRight=NULL;
if(cnode->m_pLeft != NULL){
tLeft = convert(cnode->m_pLeft,1);
}
if(tLeft != NULL){//调整好左子树
cnode->m_pLeft = tLeft;
tLeft->m_pRight = cnode;//左指针指向前驱节点,右指针指向后继节点
}
if(cnode->m_pRight != NULL){
tRight = convert(cnode->m_pRight,0);
}
if(tRight != NULL){//调整好右子树
cnode->m_pRight = tRight;
tRight->m_pLeft = cnode;
}
//以上部分已经调整好左右子树了,下面来返回该链表指针
if(asLeft == 0 || asLeft == -1){//如果没有父亲,则让其指向最小节点!
while(cnode->m_pLeft != NULL){
cnode = cnode->m_pLeft;
}
}else{//如果以cnode为根的子树是其父亲的左子树,那么父亲的前驱就是这颗子树中最大的节点
while(cnode->m_pRight != NULL){
cnode = cnode->m_pRight;
}
}
return cnode;
}
//方法二:还是递归法
//区别方法一:本方法利用中序遍历,每次递归时只假设当前节点cnode的前驱已经是一个排序的双向链表!
BSTreeNode * convert2(BSTreeNode *cnode,BSTreeNode *orderedList){
if(cnode == NULL){
return NULL;
}
//先访问左子树
if(cnode->m_pLeft != NULL){
orderedList = convert2(cnode->m_pLeft,orderedList);
}
if(orderedList != NULL){//调整orderedList和cnode的关系
cnode->m_pLeft = orderedList;
orderedList->m_pRight = cnode;
}
//生成新的orderedList
orderedList = cnode;
//访问右子树
if(cnode->m_pRight != NULL){
orderedList = convert2(cnode->m_pRight,orderedList);
}
return orderedList;//返回的是最大节点的指针,这点和方法一不同(方法一也可以改成这样!)
}
//生成测试的二叉查找树
BSTreeNode * create(){
int data[]={10,6,14,4,8,12,16};
int i;
BSTreeNode * tree=NULL;
for(i=0;i<sizeof(data)/sizeof(int);i++){
BSTreeNode *fresh = (BSTreeNode*)malloc(sizeof(BSTreeNode));
fresh->m_value = data[i];
fresh->m_pLeft = fresh->m_pRight = NULL;
tree = insert(tree,fresh);
}
return tree;
}
//测试方法一
void test_convert(){
BSTreeNode *p=NULL, *fptr=NULL;
BSTreeNode *tree = create();
tree = convert(tree,-1);
p=tree;
while(p != NULL){
printf("%d,",p->m_value);
fptr = p;
p = p->m_pRight;
free(fptr);
}
printf("\n");
}
//测试方法二
void test_convert2(){
BSTreeNode *p=NULL, *fptr=NULL;
BSTreeNode *tree = create();
printf("convert2:\n");
tree = convert2(tree,NULL);
p=tree;
while(p != NULL){//从右向左遍历
printf("%d,",p->m_value);
fptr=p;
p=p->m_pLeft;
free(fptr);
}
printf("\n");
}
int main(){
test_convert();
test_convert2();
return 0;
}
题二:
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 20
typedef struct BSTreeNode{
int m_value;
struct BSTreeNode *m_pLeft;
struct BSTreeNode *m_pRight;
}BSTreeNode;
//先得写个简单的int栈
typedef struct stack{
BSTreeNode* data[MAXSIZE];
int top;
}Stack;
//-------- 栈的各种操作--------------------//
int isEmpty(Stack *s){
if(s->top == 0){
return 1;
}else{
return 0;
}
}
int push(Stack *s, BSTreeNode *node){
if(s->top>=MAXSIZE){
printf("error:栈已满\n");
return -1;
}
s->data[s->top++] = node;
return 1;
}
BSTreeNode * pop(Stack *s){
if(s->top == 0){
printf("error:栈内没有元素\n");
return NULL;
}
return s->data[--(s->top)];
}
//------- 树的各种操作 --------------------------//
//生成二叉查找树
BSTreeNode *insert(BSTreeNode *tree,BSTreeNode *n){
if(tree == NULL){
tree = n;
}else if(n->m_value < tree->m_value){
tree->m_pLeft = insert(tree->m_pLeft,n);
}else{
tree->m_pRight = insert(tree->m_pRight,n);
}
return tree;
}
//生成测试的二叉查找树
BSTreeNode * create(){
int data[]={10,6,14,4,8,12,16};
int i;
BSTreeNode * tree=NULL;
for(i=0;i<sizeof(data)/sizeof(int);i++){
BSTreeNode *fresh = (BSTreeNode*)malloc(sizeof(BSTreeNode));
fresh->m_value = data[i];
fresh->m_pLeft = fresh->m_pRight = NULL;
tree = insert(tree,fresh);
}
return tree;
}
//非递归的将二叉查找树变为其镜像
BSTreeNode * mirror(BSTreeNode *cnode){
if(cnode == NULL){
return NULL;
}
Stack s;
s.top=0;//初始化栈指针
BSTreeNode *p = cnode, *tmp=NULL;
while(cnode!=NULL || !isEmpty(&s)){
if(cnode){
push(&s,cnode);
cnode = cnode->m_pLeft;
}else{
cnode = pop(&s);
//交换cnode的左右孩子
tmp = cnode->m_pLeft;
cnode->m_pLeft = cnode->m_pRight;
cnode->m_pRight = tmp;
cnode = cnode->m_pLeft;
}
}
return p;
}
//递归版本
BSTreeNode *re_mirror(BSTreeNode *cnode){
BSTreeNode * tmp=NULL;
if(cnode != NULL){
//交换左右子树的指针
tmp = cnode->m_pLeft;
cnode->m_pLeft = cnode->m_pRight;
cnode->m_pRight = tmp;
//递归
cnode->m_pLeft = re_mirror(cnode->m_pLeft);
cnode->m_pRight = re_mirror(cnode->m_pRight);
}
return cnode;
}
void printTree(BSTreeNode *cnode){
if(cnode != NULL){
printTree(cnode->m_pLeft);
printf("%d,",cnode->m_value);
printTree(cnode->m_pRight);
}
}
int main(){
BSTreeNode *tree = create();
tree = re_mirror(tree);
BSTreeNode *p=NULL, *fptr=NULL;
printTree(tree);
printf("\n");
return 0;
}
小结:要想解决二叉树中的大多数问题一定要理解"递归"的思想!而且要知道非递归算法的写法!我自己对非递归算法都还有点不熟,汗!!
本文重点参考了 http://zhedahht.blog.163.com/ 的内容,深表感谢!!
我的代码都是用纯C语言实现的,而且分析写在了注释中。代码没有做过"优化",主要是便于理解解题思想!
(全文完)