您的位置:首页 > 职场人生

微软面试百题001——BST转化有序双向链表

2016-08-06 14:40 495 查看

001.微软面试百题之BST转双向链表

1.题意描述:

不允许开辟新的节点,只是改变指针的转向,将一颗标准的BST转化为一个双向链表,返回双向链表的头指针
测试用例描述:
7
5
9
-2
8 10
3
12
1
这棵二叉查找树转化为双向链表的操作结果是:-2 --1 --3 --5 --7 --8 --9 --10 --12

2.思维考虑:

BST:

本博主的BST总结详解

这里我们先考虑BST 的性质,BST作为一颗高效的二叉查找树,性质如下:
1.空树
2.非空树且左子树节点键值均小于根节点的键值,右子树所有的节点的键值均大于根节点的键值
  左右子树均为一颗BST

那么再转化成双向链表的时候,我们根据题意只能通过改变指针来进行操作,当然操作的前提是比该节点的键值小的节点已经存在于有序的双向链表中,这样子进行递归的定义我们就会发现,我们要保证是有序的双向链表,那么我们根据BST 的性质完全可以通过所谓的中序遍历来实现,在中序遍历的时候我们队当前的根节点的指针域进行操作就可以了

指针域的操作如下:

准备操作

首先我们要开辟两个辅助的指针来帮助我们根号的控制目前已经有序的双向链表
1.head指针:作为整个双向链表的头结点(只有两种状态,空或者非空,空代表目前双向链表不存在任何一个有序的节点,非空代表目前的双向链表中至少有一个有序的节点)
2.listtail指针:顾名思义,listtail时终止向目前的双向链表中的最后一个元素(只有两个状态,空和非空,空代表目前双向链表不存在任意一个有序的节点,非空代表至少存在一个有序的节点),实际上我们发现listtail和head的状态实际上是同步的,那我们为什么要新引入一个listtail指针呢,其实这完全是为了节省时间,如果我们不引入这个指针内存空间,那么我们就必须环肥O(n)复杂度的使劲去遍历一遍整个目前有序的双向链表,所谓空间换时间,相对而言,这么做是非常划算的

指针变化

(p代表当期吗遍历到的节点,方便后续我们的被描述)
1.如果head为空,那么我们令head只想当前遍历到的节点,然后listtail同样指向这个节点
2.如果head不为空,说明listtail目前有确定指向,那么我们将listtail->right=p,p->left=listtail,listtail=p(最后一步的作用实际上是扩展我们的listtail,保证期时终止向最后一个节点);

3.C++代码实现,类封装

因为在博主上一期的数据结构专题中,已经讨论过BST并且封装了BST的代码,这次我就直接拿来调用了,强调一点,对于Cbst我是考虑了公有继承,所以之后要用到root成员,所以bst我们这里设定成保护类型(实际上C++代码中,一旦要被继承的对象中的成员我们都是设定成保护类型的)
具体的操作和解释在代码中进行注释
#include"iostream"
#include"cstdio"
#include"cstdlib"

using namespace std;

typedef struct node
{
int key;
struct node* left;
struct node* right;
}point;

class errorsame
{
};

class bst
{
public:
bst()
{
root=NULL;
num=0;
}
virtual ~bst()
{
clear(root);
}
void add(int);
void del(int);
point* find(int);
void clear(point*);
void preorder(point*);
void midorder(point*);
void aftorder(point*);
void rankorder();
point* returnroot()
{
return root;
}
protected:
point* root;
int num;
};

void bst::clear(point* p)
{
if(p==NULL) return ;
else
{
clear(p->left);
clear(p->right);
free(p);
}
}

void bst::add(int p)
{
if(num==0)
{
root=new point;
root->right=root->left=NULL;
root->key=p;
num++;
return ;
}
else
{
try
{
point *k=root;
point* w=root;
while(w!=NULL)
{
if(w->key==p) throw errorsame();
else
{
if(w->key>p) w=w->left;
else w=w->right;
}
if(w!=NULL) k=w;
}
if(k->key>p)
{
point *a=new point();
a->left=a->right=NULL;a->key=p;
k->left=a;
}
else
{
point *a=new point();
a->left=a->right=NULL;a->key=p;
k->right=a;
}
num++;
}
catch(errorsame e)
{
cout<<"try to add the same point in the tree!"<<endl;
}
}
}

void bst::del(int p)
{
point* now=root;
point* father=NULL;
while(now->key!=p)
{
if(now->key>p)
{
father=now;
now=now->left;
}
else
{
father=now;
now=now->right;
}
if(now==NULL)
{
cout<<"can not find the point!"<<endl;
return ;
}
}
if(father==NULL)
{
point* help=root;
if(root->right==NULL) root=root->left;
else
{
if(root->right->left==NULL)
{
root->right->left=root->left;
root=root->right;
}
else
{
point* z=NULL;
point* k=root->right;
point* w=root;
while(k->left!=NULL)
{
if(k->left->left==NULL) z=k;
k=k->left;
}
z->left=k->right;
k->left=root->left;
k->right=root->right;
root=k;
}
}
free(help);
}
else if(now->right==NULL)
{
point* help=now;
if(father->left==now) father->left=now->left;
else father->right=now->left;
free(help);
}
else
{
if(now->right->left==NULL)
{
point* help=now;
now->right->left=now->left;
if(father->left==now) father->left=now->right;
else father->right=now->right;
free(help);
}
else
{
point* z;
point* k=now->right;
while(k->left!=NULL)
{
if(k->left->left==NULL) z=k;
k=k->left;
}
if(father->left==now)
{
z->left=k->right;
father->left=k;
k->left=now->left;
k->right=now->right;
}
else
{
z->left=k->right;
father->right=k;
k->left=now->left;
k->right=now->right;
}
}
}
}

point* bst::find(int p)
{
point* a=root;
while(a!=NULL)
{
if(a->key==p) return a;
if(a->key>p) a=a->left;
else a=a->right;
}
}

void bst::preorder(point* p)
{
if(p==NULL) return ;
else
{
printf("%d ",p->key);
preorder(p->left);
preorder(p->right);
}
}

void bst::midorder(point* p)
{
if(p==NULL) return ;
else
{
midorder(p->left);
printf("%d ",p->key);
midorder(p->right);
}
}

void bst::aftorder(point* p)
{
if(p==NULL) return ;
else
{
aftorder(p->left);
aftorder(p->right);
printf("%d ",p->key);
}
}

void bst::rankorder()
{
point* queue[100];
int head=1;
int tail=2;
queue[1]=root;
while(head!=tail)
{
if(queue[head]->left!=NULL) queue[tail++]=queue[head]->left;
if(queue[head]->right!=NULL) queue[tail++]=queue[head]->right;
head++;
}
for(int i=1;i<=tail-1;i++) printf("%d ",queue[i]->key);
}

class Cbst:public bst
{
public:
Cbst():bst()
{
head=listtail=NULL;
}
virtual ~Cbst()  //这里就不再赘语为什么用虚析构函数了
{
//强调一点,在这里我们必须要对bst进行变换,因为我懒得写那几行切换的代码了
point* p=head;
while(p!=NULL)
{
point* w=p;
p=p->right;
free(w);
}
root=NULL;    //很尴尬,我的虚析构函数知识点忘了,所以这里手工将root设定成NULL,防止指针玄关的错误发生,尴尬,继续去复习指针玄关的知识点去
}
void visit(point*);     //中序遍历
void change(point*);    //中序遍历中对指针的进行的节点指针操作
void scan();    //程序正确性与否的测试代码段
private:
point* head;
point* listtail;
};

void Cbst::visit(point* p)
{
if(p==NULL) return ;
else
{
visit(p->left);
change(p);
visit(p->right);
}
}

void Cbst::change(point* p)
{
if(head==NULL)
{
head=p;
listtail=p;
}
else
{
p->left=listtail;
listtail->right=p;
listtail=p;
return ;
}
}

void Cbst::scan()
{
point* p=head;
while(p!=NULL)
{
cout<<p->key<<' ';
p=p->right;
}
cout<<endl;
}

int main()
{
Cbst my;
my.add(7);
my.add(5);
my.add(9);
my.add(-2);
my.add(10);
my.add(3);
my.add(1);
my.add(8);
my.add(12);
my.visit(my.returnroot());
my.scan();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息