【数据结构】kd树实现,范围查询,邻近查询笔记【1】2d树的构造

k dimension tree,一种高维几何搜索的数据结构,一维对应二叉搜索树,适用于多个搜索特征的数据类型。


1.2d树类实现

2d-树的构造核心思想:

每次选取一个维度进行划分

点深度为偶(奇)时,则沿垂直(水平)方向切分

如何保证每次划分尽量居中?

每次切分都在中位点(对应坐标居中点),保证全树不高于O(logn)

代码实现:   

先做一个Point类,2D Tree的节点继承Point:

class Point//点类
{
public:
    //使用初始化表初始化点类
    Point(double a = 0, double b = 0 ,char na='a'):x(a), y(b),name(na){}
    Point(const Point &p); //复制构造函数
    const double getX();//得到x坐标
    const double getY();//得到y坐标
    const char getName();//得到name
    void setX(double sx);//更改x坐标
    void setY(double sy);//更改y坐标
    void setName(char sc);//更改y坐标
    // bool isSmaller(int dimension,Point &p);
    // bool isEqual(Point &p);
    // bool isBigger(int dimension,Point &p);

    //重载运算符
    friend ostream& operator<<(ostream &output, Point &p);
    bool operator >(Point &p);
	bool operator <(Point &p);
	bool operator ==(Point &p);

    int P_dimension=0;

protected:
    double x;//x坐标
    double y;//y坐标
    char name;
};

Point::Point(const Point &p){
    x=p.x;
    y=p.y;
    name=p.name;
}

//得到x的值
const double Point::getX(){ return x;}
 
//得到y的值
const double Point::getY(){ return y;}

//得到name
const char Point::getName(){  return name;}

//更改x的值
void Point::setX(double sx){ x=sx; }

//更改y的值
void Point::setY(double sy){ y=sy; }
 
//更改name
void Point::setName(char sc){ name=sc; }

//重载运算符
ostream& operator<<(ostream &output, Point &p)
{
    output<<"("<<p.x<<","<<p.y<<")"<<endl;
    return output;
}

bool Point::operator >(Point &p){
    if (P_dimension>=2) return 0;
       double coor1[2]={x,y} ;
       double coor2[2]={p.getX(),p.getY()} ;
       if(coor1[P_dimension]>coor2[P_dimension]) return 1;
       else return 0;
}

bool Point::operator <(Point &p){
    if (P_dimension>=2) return 0;
       double coor1[2]={x,y} ;
       double coor2[2]={p.getX(),p.getY()} ;
       if(coor1[P_dimension]<coor2[P_dimension]) return 1;
       else return 0;
}

bool Point::operator ==(Point &p){
       double px=p.getX();
       double py=p.getY();
       if((x==px)&&(y==py)) return 1;
       else return 0;
}

// //根据维度demension比较大小
//     bool Point::isSmaller(int dimension,Point &p){
//        if (dimension>=2) return 0;
//        double coor1[2]={x,y} ;
//        double px=p.getX();
//        double py=p.getY();
//        double coor2[2]={px,py} ;
//        if(coor1[dimension]<coor2[dimension]) return 1;
//        else return 0;
//     }

//     bool Point::isEqual(Point &p){
//        double px=p.getX();
//        double py=p.getY();
//        if((x==px)&&(y==py)) return 1;
//        else return 0;
//     }

//     bool Point::isBigger(int dimension,Point &p){
//        if (dimension>=2) return 0;
//        double coor1[2]={x,y} ;
//        double px=p.getX();
//        double py=p.getY();
//        double coor2[2]={px,py} ;
//        if(coor1[dimension]>coor2[dimension]) return 1;
//        else return 0;
//     }


struct Coordinate{
    double m_coor[2]; // 点的坐标
};
struct TreeNode{
    Point p;
    int m_dimesion; 
    // 0为x方向划分,1为y方向划分
    TreeNode(const Point &p1): p(p1){}
    TreeNode* m_pLeftChild; // 左孩子
    TreeNode* m_pRightChild; // 右孩子
};
struct RecArea{
    // 存放矩形区域左下和右上坐标
    Coordinate m_minCoor;
    Coordinate m_maxCoor;
};

class KdTree {
    private:
        int count;

    public:
        TreeNode* m_pRoot;
        KdTree() : m_pRoot(nullptr),count(0){}; //无参构造函数
        //KdTree(TreeNode*& rhs) : m_pRoot(rhs),count(0){}; //复制构造函数
        bool Empty(); // 判断二叉树是否为空
        //void Traverse(int key,void (*visit)(T));//用于遍历二叉树
        TreeNode* buildTree(std::vector<Point>&pList, int d);
};//BinartTree  二叉树类

Point findMedialPoint(int dimension, vector<Point>&pList){
    int len=pList.size();
    //for (int i = 0; i < len; i++) pList[i].P_dimension=dimension;
    sort(pList.begin(),pList.end());                    // algorithm.h 中的向量排序算法
    Point mp=pList[len/2];
    cout<<"mp:"<<mp<<endl;
    return mp;
}


TreeNode* KdTree::buildTree(std::vector<Point>&pList, int d)
{      // 在深度为d的层次,构造一棵对应于(子)集合pointList(pL)的(子)2d-树
    if (pList.empty()) return NULL;
    int dimension = d % 2;        
    for (int i = 0; i < pList.size(); i++) pList[i].P_dimension=dimension;                      // 确定划分方向
    Point medianP = findMedialPoint(dimension, pList);
                                                            // 确定中位点
    std::vector<Point> pListLeft, pListRight;       // 左右子树的点集
    for (int i = 0; i < pList.size(); i++)
    {
        if (medianP>pList[i])  //点pList[i]的dimension轴坐标小于中位点
		pListLeft.push_back(pList[i]);              // 归入左子树
        else if (medianP< pList[i])  // 轴坐标大于中位点
		pListRight.push_back(pList[i]);             // 归入右子树
        else if (!( medianP==pList[i]))       // 多个点落在中位线的情况
		pListRight.push_back(pList[i]);             // 除了中位点外其它归入右子树
    }
    TreeNode* pTreeNode = new TreeNode(medianP); //创建子树根,复制节点数据为mediaP
    pTreeNode->m_pLeftChild = buildTree(pListLeft, d + 1);   // 深度d+1构建左子树      
    pTreeNode->m_pRightChild = buildTree(pListRight, d + 1); // 深度d+1构建右子树 
    pTreeNode->m_dimesion = dimension;                 // 设置节点的分割轴方向
    return pTreeNode;
}

构建树思路:

如上图所示的7个点构建二维搜索树,维度定义:dimension 1--x, 0--y;

首先 dimension=1,把七个点按x周排序,找到x轴坐标中点“C”,作为根节点,然后比Cx小的入左子树,比Cx大的入右子树。然后再递归生成子树,次数深度+1,维度改变。

以左子树为例,输入点向量(G B A ),此时dimension=0, 按y坐标排序,为: G-B-A,中位点为B,则G入B左子树,A入右子树,再递归就完成了二维搜索树的构造。

测试:

int main( )
{  
    int pointnum,quirynum;
    cin>>pointnum>>quirynum;
    vector<Point > pList;

    Point parray[pointnum];

    double m_x,m_y;
    char m_name;
    for(int i=0;i<pointnum;i++){
        cin>>m_x>>m_y;
        cin>> m_name;
        parray[i]=Point(m_x,m_y,m_name);
        pList.push_back(parray[i]);
    }
    //vector<Point> pList(parray,parray+pointnum);
    cout<<pList.size()<<endl;

    for(int i=0;i<pointnum;i++){
        cout<<pList[i]<<endl;
    }


    KdTree m_2dtree;
    m_2dtree.m_pRoot=m_2dtree.buildTree( pList, 1);

    system("pause");
    return 0;
}

输入输出:

首先输入测试点个数,然后输出生成的点向量

在构建2D树时,每生成一个节点,输入节点Point 

根节点b,左子树a,右子树c,和预期的相符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值