二叉树的创建

//定义二叉树结点
struct node{
    int data;
    node* lchild;
    node* rchild;
};
//创建一个二叉树结点
node* newNode(int v)
{
    node* anode = new node;
    anode->data = v;
    anode->lchild = anode->rchild = NULL;
    return anode;
}
/*
    function:往BST中插入一个结点
    这里的BST是二叉搜索树,任意结点的值大于其左孩子,小于等于其右孩子
    params:
    root为二叉树根结点
    v为要插入的二叉树结点的值
*/
void insertNode(node* &root,int v)
{
    if(root == NULL){
         root = newNode(v);
         return;
    }else if(v < root->data){
        insertNode(root->lchild,v);//递归式的往左子树搜索
    }else if(v >= root->data){
         insertNode(root->rchild,v);//递归式的往右子树搜索
    }
}

/*
    function:创建一棵二叉树
    params:
    data[]存储要插入的各个结点的值
    n为结点个数
*/
node* createBinaryTree(int data[],int n)
{
    node* root = NULL;
    for(int i=0;i<n;i++){
        insertNode(root,data[i]);
    }
    return root;
}

/*
  function:由前序遍历和中序遍历创建二叉树
  params:
  前序遍历下标:[preL,preR]
  中序遍历下标:[inL,inR]

*/
node* createByPre(int preL,int preR,int inL,int inR)
{
    node* root = new node;
    root->data = preOrder[preL];
    if(preL > preR)
        return NULL;
    int k;
    //在中序遍历中,找到前序遍历的那个节点,从而划分中序遍历为左子树和右子树
    for(k = inL;k<=inR;k++)
    {
        if(inOrder[k] == preOrder[preL])
            break;
    }
    int numLeft = k - inL;
    //左子树
    root->lchild = createByPre(preL+1,preL+numLeft,inL,k-1);
    //右子树
    root->rchild = createByPre(preL+numLeft+1,preR,k+1,inR);

    return root;
}

/*
  function:由后序遍历和中序遍历创建二叉树
  params:
  后序遍历下标:[postL,postR]
  中序遍历下标:[inL,inR]
*/
node* createByPost(int postL,int postR,int inL,int inR)
{
    node* root = new node;
    root->data = postOrder[postR];

    if(postL > postR)
        return NULL;
    int k;
    //在中序中找到后序遍历的顶点,划分左右子树
    for(k=inL;k<=inR;k++)
        if(inOrder[k] == postOrder[postR])
            break;
    int numLeft = k - inL;
    //左子树
    root->lchild = createByPost(postL,postL+numLeft-1,inL,k-1);
    //右子树
    root->rchild = createByPost(postL+numLeft,postR-1,k+1,inR);
    return root;
}

二叉树遍历

//前序遍历
void preTravel(node* root)
{
    if(root == NULL)
        return;
    printf(" %d",root->data);
    preTravel(root->lchild);
    preTravel(root->rchild);
}

//中序遍历
void inTravel(node* root)
{
    if(root == NULL)
        return;
    inTravel(root->lchild);
    printf(" %d",root->data);
    inTravel(root->rchild);
}

//后序遍历
void postTravel(node* root)
{
    if(root == NULL)
        return;
    postTravel(root->lchild);
    postTravel(root->rchild);
    printf(" %d",root->data);
}

//层次遍历
void layerTravel(node* root)
{
    queue<node*> treeQ;
    treeQ.push(root);
    bool first = true;
    while(!treeQ.empty())
    {
        //取出来访问
        node* now = treeQ.front();
        if(!first)
            printf(" %d",now->data);
        else{
            printf("%d",now->data);
            first =false;
        }
        treeQ.pop();
        if(now->lchild!=NULL)
            treeQ.push(now->lchild);
        if(now->rchild!=NULL)
            treeQ.push(now->rchild);
    }
    printf("\n");
}

AVL树(平衡二叉树)

//定义结点
struct node{
    int data;
    int height;
    node* lchild;
    node* rchild;
};

//获取以root为根结点的子树的当前height
int getHeight(node* root){
    if(root == NULL)
        return 0;  //空结点高度为0
    return root->height;
}

//计算结点root的平衡因子
int getBalanceFactor(node* root){
    //左子树高度减右子树高度
    return getHeight(root->lchild) - getHeight(root->rchild);
}

//更新结点root的height
void updateHeight(node* root){
    root->height = max(getHeight(root->lchild),
                   getHeight(root->rchild))+1;
}

//左旋
void L(node* &root){
    node* temp = root->rchild;
    root->rchild = temp->lchild;
    temp->lchild = root;
    updateHeight(root);
    updateHeight(temp);
    root = temp;
}

//右旋
void R(node* &root){
    node* temp = root->lchild;
    root->lchild = temp->rchild;
    temp->rchild = root;
    updateHeight(root);
    updateHeight(temp);
    root = temp;
}

//往AVL树中插入一个结点
void insertNode(node* &root,int data){
        if(root == NULL){
            root = new node;
            root->data = data;
            root->lchild = root->rchild = NULL;
            root->height = 1;//结点高度初始为1
            return;
        }
        if(data < root->data){// data比根结点权值小
            insertNode(root->lchild,data);//递归往左子树插入
            updateHeight(root);//更新树高
            if(getBalanceFactor(root) == 2){//失衡
                if(getBalanceFactor(root->lchild) == 1){//LL型
                    R(root);
                }else if(getBalanceFactor(root->lchild) == -1){//LR型
                    L(root->lchild);
                    R(root);
                }
            }
        }else{//data大于等于根结点权值
            insertNode(root->rchild,data);//递归往右子树插入
            updateHeight(root);//更新树高
            if(getBalanceFactor(root) == -2){//失衡
                if(getBalanceFactor(root->rchild) == -1){//RR型
                    L(root);
                }else if(getBalanceFactor(root->rchild) == 1){//Rl型
                    R(root->rchild);
                    L(root);
                }
            }
        }
}

//创建AVL平衡二叉树
node* createAVL(int insertSeq[],int n){
    node* root = NULL;
    for(int i=0;i<n;i++){
        insertNode(root,insertSeq[i]);
    }
    return root;
}

堆(大小顶堆,堆排序)

堆是完全二叉树

  • 大顶堆 - 堆序性:每个结点的权值均不小于其孩子结点
  • 小顶堆 - 堆序性:每个结点的权值均不大于其孩子结点

由于堆是完全二叉树,可以完全用数组来存储堆结构,比用指针更方便.

如果根结点的下标从1开始. 那么对任一结点 i ,其左孩子就为 2i , 右孩子为 2i + 1

举例分析 - 大顶堆

对一个原始序列: 3 1 2 8 7 5 9 4 6 0

初始的结构如下图所示,相当于按层次遍历的顺序摆放元素

graph TD C[3] C-->D[1] C -->E[2] D-->F[8] D-->G[7] E-->E1[9] E-->E2[4] F-->F1[6] F-->F2[0]

显然,这样一个结构并不是堆,因为不满足堆的定义,它既不是大顶堆也不是小顶堆,这时候就需要我们对这个结构进行调整,让它满足堆的定义成为一个真正的堆.

调整的核心 - 自下而上的下滤

下面模拟一下调整堆的过程:

从原始数组的最后一个元素开始,也就是上述二叉树的最后一个叶子结点开始,叶子结点没有孩子结点,所以叶子结点不需要调整(它与空孩子结点构成最大的元素).

所以其实是从最后一个非叶子结点开始,也是是数据为 8的结点 , 将 8 与它的两个孩子结点 6 和 0 比较,8比 6和0都大,所以8 不需要调整.

然后再到数据为2的结点, 将2与( 9,4)比较,9是3者中最大的,将9和2进行交换,得到下图

graph TD C[3] C-->D[1] C -->E[9] D-->F[8] D-->G[7] E-->E1[2] E-->E2[4] F-->F1[6] F-->F2[0]

然后到 数据为1的结点,将1 和( 8,7)比较,8是三者中最大的,将8与1交换,得到下图

graph TD C[3] C-->D[8] C -->E[9] D-->F[1] D-->G[7] E-->E1[2] E-->E2[4] F-->F1[6] F-->F2[0]

注意,8和1交换之后, 1还有孩子结点,1还要与其现在的孩子结点(6,0)比较,经比较得6最大,将6和1交换得下图

graph TD C[3] C-->D[8] C -->E[9] D-->F[6] D-->G[7] E-->E1[2] E-->E2[4] F-->F1[1] F-->F2[0]

然后到数据为3的根结点,将3与(8,9)比较,9是三者中最大的,将9与3交换,得到下图

graph TD C[9] C-->D[8] C -->E[3] D-->F[6] D-->G[7] E-->E1[2] E-->E2[4] F-->F1[1] F-->F2[0]

此时3还有孩子结点,3还要继续比较,3和其孩子结点(2,4)比较后,4是最大的,交换3和4,得到下图

graph TD C[9] C-->D[8] C -->E[4] D-->F[6] D-->G[7] E-->E1[2] E-->E2[3] F-->F1[1] F-->F2[0]

这样就完成了一个大顶堆的调整了,可以仔细检查每一个结点,确实都满足该结点不小于其孩子结点.

事实上,经过这样一次调整,原始数组中最大的元素此时就位于堆顶.

这样一个过程如我开头所说叫 自下而上的下滤.自上而下就是结点调整的顺序是从最后一个非叶子结点到根结点的结点调整顺序,这个从我们刚才调整的过程中树状图可以看出;

下滤就是对每一个结点进行调整时,都是将其与其孩子结点进行比较,如果它小于其中最大的一个,就将其与最大的孩子结点交换,交换之后该结点如果还有孩子结点,那么还要继续向下比较调整,直至在某一位置上,它不小于其孩子结点,很形象,所以叫下滤.

下面为对一个结点下滤操作的具体实现代码:

const int MAXN = 100;
int heap[MAXN]; //初始时为任意序列
int n; //n为实际heap中元素个数

/*
	对一个结点的下滤操作
	low是当前欲调整的结点的下标
	high是调整的最大范围,通常就是树中最后一个结点的下标n
	2*low <= high
*/
void downAdjust(int low,int high)
{
	int i = low;
	int j = 2*i; //j为i的左孩子
	
	while(j <= high)
	{
		if(j+1 <= high && heap[j+1] > heap [j]) //存在右孩子且右孩子比左孩子大
		{
			j = j+1;//此时下标 j 存放的是左右孩子中权值较大的那个 
		}
		if( heap[i] < heap [j])//孩子结点比父结点要大
		{
            swap(heap[i],heap[j]);//交换孩子结点和父结点
            i = j;// i的值重新成为欲调整的结点,继续向下调整
            j = 2*i;
		}
		else
		{
			break;//欲调整的结点比其孩子结点都大,调整结束
		}
	}	
}

要完成整个大顶堆的建立,就需要对非叶子结点都要进行下滤操作,在完全二叉树中,叶子结点的个数为 ( n/2 向上取整),所以非叶子结点的个数不少于(n/2 向下取整),前面已经说了,结点之间的调整顺序是自下而上的,所以我们从下标为 n/2的结点开始,倒着分别做下滤操作,直至根结点,就可以得到一个完整的建堆过程.

建堆过程代码如下:

/*
	建堆
	对结点 n/2 到 1 , 分别调用下滤操作,完成一个完整的建堆过程
*/
void createHeap()
{
	for(int i = n/2; i >= 1; i--)
	{
		downAdjust(i,n);
	}
}

posted @ 2019-09-20 15:14  dekeshile  阅读(89)  评论(0编辑  收藏  举报