树和二叉树

树的双亲表示法结点结构定义

#define MAX_TREE_SIZE 100
typedef int ElemType;
typedef struct PTNode
{
	ElemType data;//结点数据
	int parent;//双亲位置
}PTNode;
typedef struct 
{
	PTNode nodes[MAX_TREE_SIZE];
	int r;//根的位置
	int n;//结点数目
}

双亲孩子表示法

#define MAX_TREE_SIZE 100
typedef char ElemType ;
//孩子结点
typedef struct CTNode
{
	int child;//孩子结点的下标
	struct CTNode *next;//指向下一个孩子结点的指针

}*ChildPtr;
//表头结构
typedef struct
{
	ElemType data;//存放在树中的结点的数据
	int parent;//存放双亲的下标
	ChildPtr firstchild;//指向第一个孩子的指针
}CTBox;
//树结构
typedef struct
{
	CTBox nodes[MAX_TREE_SIZE];//结点数据
	int r,n;
}

树的层序遍历  需要对结点的层号进行求解

struct node
{
	int layer;
	int data;
	vector<int > child;
}Node[maxn];
void LayerOrder(int root)
{
	queue<int > Q;
	Q.push(root);
	Node[root].layer=0;
	while(!Q.empty())
	{
		int front=Q.front();
		cout<<Node[front].data<<" ";
		Q.pop();
		for(int i=0;i<Node[front].child.size();i++)
		{
			int child=Node[front].child[i];//当前结点的第i个子结点的编号
			//子结点层号为当前结点层号+1
			Node[child].layer=Node[front].layer+1;
			Q.push(child);
		}
	}
}

 

 

二叉树

1.满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树

满二叉树:所有叶子在同一层,非叶子的度为2;在同样深度的二叉树中,满二叉树的结点数目一定最多,同时叶子也是最多。

完全二叉树:叶子结点只能出现在最下两层;最下层的叶子结点一定集中在左部连续位置;倒数第二层,若有叶子结点,一定都在右部连续位置;同样结点数的二叉树,完全二叉树的深度最小

完全二叉树:①对完全二叉树当中的任何一个结点,(设编号为x),其左孩子的编号一定是2x,而右孩子的编号一定是2x+1。

                     ②完全二叉树可以通过建立一个大小为2的k次方的数组来存放所有结点的信息,其中k为完全二叉树的最大高且1                              号位存放的必须是根结点。

                      ③判断某个结点是否为叶结点的标志为:该结点(记下标为root)的左子结点的编号为root*2大于结点总结点数n

                      ④判断某个结点是否为空结点的标志为:该结点下标root大于结点总个数n

2、二叉链表

typedef struct BiNode
{
	ElemType data;
	struct BiNode *lchild,*rchild;
}BiNode,*BiTree;

3、如果函数中需要新建结点,即对二叉树的结构做出修改,就需要加引用;如果只是修改当前已有结点的内容,或仅仅是遍历树,就不用加引用。

4、在递归时,如果子树是空树,那么root一定是NULL,表示这个结点不存在;而所谓的*root=NULL,含义是获取地址root指向的空间的内容。

用二叉链表求叶子结点(用递归方法和非递归方法)

#include<bits/stdc++.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef char ElemType;
typedef struct BiTNode
{

    char data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//创建一颗二叉树
void CreateBiTree(BiTree &T)
{
    char c;
    cin>>c;
    if(c=='#')
    {
        T=NULL;
    }
    else
    {
        T=(BiTNode *)malloc(sizeof(BiTNode));
        T->data=c;
        CreateBiTree(T->lchild);
        CreateBiTree(T->rchild);
    }
}
//求二叉树叶子结点的递归算法
int leaf1(BiTree &T)
{
    if(T==NULL)
    {
        return 0;
    }
    else if(T->lchild==NULL&&T->rchild==NULL)
    {
        return 1;
    }
    else
    {
        return (leaf1(T->lchild)+leaf1(T->rchild));
    }
}
//求二叉树叶子结点的非递归算法
int leaf2(BiTree &T)
{
    if(T==NULL)
        return 0;
    else if(T->lchild==NULL&&T->rchild==NULL)
        return 1;
    else
    {
        queue< BiTree >q;
        q.push(T);
        int a=0;
        while(!q.empty())
        {
            BiTNode* t=q.front();
            q.pop();
            if(!t->lchild&&!t->rchild)
                a++;
            if(t->lchild)
                q.push(t->lchild);
            if(t->rchild)
                q.push(t->rchild);
        }
        return a;
    }

}
int main()
{

    BiTree T=NULL;
    CreateBiTree(T);
    cout<<"用递归求得的二叉树叶子节点数目:  "<<leaf1(T)<<endl;
    cout<<"用非递归求得的二叉树叶子节点数目:"<<leaf2(T)<<endl;
    return 0;

}

3、二叉树的遍历方法

(1)前序遍历(根左右):若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树

(2)中序遍历(左根右):若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左                                              子树,然后 是访问根结点,最后中序遍历右子树。

(3)后序遍历(左右根):若树为空,则空操作返回。否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点

前序遍历的应用

#include<bits/stdc++.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef char ElemType;
typedef struct BiTNode
{

    char data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//创建一颗二叉树
void CreateBiTree(BiTree &T)
{
    char c;
    scanf("%c",&c);
    if(c=='#')
    {
        T=NULL;
    }
    else
    {
        T=(BiTNode *)malloc(sizeof(BiTNode));
        T->data=c;
        CreateBiTree(T->lchild);
        CreateBiTree(T->rchild);
    }
}
//访问二叉树结点的具体操作
void visit(char c,int level)
{
    printf("%c 位于第%d 层\n",c,level);
}
//前序遍历二叉树
void PreOrderTraverse(BiTree T,int level)
{
    if(T)
    {
        visit(T->data,level);
        PreOrderTraverse(T->lchild,level+1);
        PreOrderTraverse(T->rchild,level+1);
    }
}
int main()
{
    int level=1;
    BiTree T=NULL;
    CreateBiTree(T);
    PreOrderTraverse(T,level);
    return 0;
}

二叉树到树、森林的转换

1、普通树转换为二叉树

 (1)加线:在所有兄弟结点之间加一条连线

  (2)去线:对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线

   (3)层次调整:以树的根结点为轴心,将整棵树顺时针旋转一定的角度,使之结构层次分明

2、森林转换为二叉树

(1)把每棵树转换为二叉树

(2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接   起来

3、二叉树转换为树、森林:逆过程

4、判断一棵二叉树能够转换成一棵树还是森林,标准很简单:看这棵二叉树的根结点有没有右孩子,有的话就是森林,没有的话就是一棵树。

5、二叉树的遍历方式

void PreorderTraversal(BinTree BT)
{
  if(BT==NULL)
    return;
  printf(" %c",BT->Data);
  PreorderTraversal(BT->Left);
  PreorderTraversal(BT->Right);
}
void InorderTraversal(BinTree BT)
{
  if(BT==NULL)
    return;
  InorderTraversal(BT->Left);
  printf(" %c",BT->Data);
  InorderTraversal(BT->Right);
}
void PostorderTraversal(BinTree BT)
{
  if(BT==NULL)
    return;
  PostorderTraversal(BT->Left);
  PostorderTraversal(BT->Right);
  printf(" %c",BT->Data);
}
void LevelorderTraversal(BinTree BT)
{
  int p=0,q=0;
  BinTree b[10000],c;
  if(BT==NULL)
    return ;
  b[q++]=BT;
  while(p!=q)
  {
    c=b[p++];
    printf(" %c",c->Data );
    if(c->Left) b[q++]=c->Left;
    if(c->Right) b[q++]=c->Right;
  }

(1)先序遍历:(根左右)

void preorder(node* root)//根左右
{
	if(!root)
		return;//到达空树,递归边界、
	//访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
	//访问左子树
	preorder(root->lchild);
	//访问右子树
	preorder(root->rchild);
}

(2)中序遍历:(左根右)

void inorder(node* root)//左根右
{
	if(!root)
		return;//到达空树,递归边界、
	//访问左子树
	inorder(root->lchild);
	//访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
	//访问右子树
	inorder(root->rchild);
}

(3)后序遍历:左右根

void postorder(node* root)//左右根
{
	if(!root)
		return;//到达空树,递归边界、
	//访问左子树
	postorder(root->lchild);
	//访问右子树
	postorder(root->rchild);
	//访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
	
}

(4)层序遍历:指按照层次的顺序从根结点向下逐层进行遍历,且对同一层的结点为从左到右遍历

        ①这里使用node*,因为这样可以通过访问地址去修改原元素

void LayerOrder(node* root)
{
	queue<node*> q;//注意队列里是存地址
	q.push(root);//将根结点地址入队
	while(!q.empty())
	{
		node* now=q.front();//取出队首元素
		q.pop();
		printf("%d",now->data);//访问队首元素
		if(now->lchild!=NULL)
			q.push(now->lchild);//左子树非空
		if(now->rchild!=NULL)
			q.push(now->rchild);//右子树非空
	}
}

②很多题目中要求计算每个结点的层次,这时就需要在二叉树结点的中添加一个记录层次layer的变量

struct node
{
	int data;
	int layer;
	node* lchild;
	node* rchild;
};

需要在根结点入队前就先令根结点的layer为1来表示根结点是第一层(也可以是0,由题意决定),之后在now->lchild和now->rchild入队前,把它们的层号都记为当前结点now的层号+1

struct node
{
	int data;
	int layer;
	node* lchild;
	node* rchild;
};

void LayerOrder(node* root)
{
	queue<node*> q;//注意队列里是存地址
	root->layer=1;
	q.push(root);//将根结点地址入队
	while(!q.empty())
	{
		node* now=q.front();//取出队首元素
		q.pop();
		printf("%d",now->data);//访问队首元素
		if(now->lchild!=NULL)
		{
			now->lchild->layer=now->layer+1;//左孩子的层号为当前层号+1
			q.push(now->lchild);//左子树非空
		}

		if(now->rchild!=NULL)
		{
			now->rchild->layer=now->layer+1;//右孩子的层号为当前层号+1
			q.push(now->rchild);//右子树非空
		}
	}
}

55、给定一棵二叉树的中序遍历序列和后序遍历序列,重建这棵二叉树

      

#include <iostream>
#include<cstring>
#include<map>
using namespace std;
map<char,char> lch,rch;
char post[100],in[100],pre[100];//DEFBHGICA   DBEFAGHCI
int t=0;
char build(int L1,int R1,int L2,int R2)
{
    if(L1>R1)
        return '#';
    char root=post[R2];
    int p=L1;
    while(in[p]!=root)
        p++;
    int cnt=p-L1;
    lch[root]=build(L1,p-1,L2,cnt+L2-1);
    rch[root]=build(p+1,R1,L2+cnt,R2-1);
    return root;

}
void previs(char a)
{
    if(a=='#')
        return ;

    pre[t++]=a;
    previs(lch[a]);
    previs(rch[a]);
}
int main()
{
    cin>>post>>in;
    int s=strlen(post);
    build(0,s-1,0,s-1);
    previs(post[s-1]);
    pre[t]='\0';
    cout<<pre<<endl;
    for(map<char,char>::iterator i =lch.begin();i!=lch.end();i++)
    {
        cout<<(i->first)<<" "<<i->second<<endl;
    }
    cout<<endl;
    for(map<char,char>::iterator i =rch.begin();i!=rch.end();i++)
    {
        cout<<(i->first)<<" "<<i->second<<endl;
    }
    cout<<endl;

    return 0;
}

  56、根据中序遍历和前序遍历写层序遍历,该题注意:控制最后一个数后面的空格不应该被输出

/*Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, you are supposed to output the level order traversal sequence of the corresponding binary tree.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (<=30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print in one line the level order traversal sequence of the corresponding binary tree. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the end of the line.
Sample Input:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
Sample Output:
4 1 6 3 5 7 2*/
#include <iostream>
#include<cstring>
#include<queue>
using namespace std;
int post[100],in[100];
int lch[100],rch[100];
int t=0;
int n;
int build(int L1,int R1,int L2,int R2)
{
    if(L1>R1)
        return 0;
    int root=post[R2];
    int p=L1;
    while(in[p]!=root)
        p++;
    int cnt=p-L1;
    lch[root]=build(L1,p-1,L2,L2+cnt-1);
    rch[root]=build(p+1,R1,L2+cnt,R2-1);
    return root;
}
void layer(int root)
{
    queue<int > q;
    q.push(root);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        cout<<now;
        t++;
        if(t<n)
            cout<<" ";
        if(lch[now])
            q.push(lch[now]);
        if(rch[now])
            q.push(rch[now]);

    }

}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>post[i];
    for(int i=0;i<n;i++)
        cin>>in[i];
    build(0,n-1,0,n-1);
    layer(post[n-1]);

    return 0;
}

57、根据先序遍历和中序遍历求后序遍历

/*Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: "Push X" where X is the index of the node being pushed onto the stack; or "Pop" meaning to pop one node from the stack.
Output Specification:

For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.
Sample Input:

6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
Sample Output:

3 4 2 6 5 1*/

#include <iostream>
#include<stack>
#include<cstdio>
#include<cstring>
using namespace std;
int pre[35],in[35],post[35];
int lch[35],rch[35];
int t=0;
int N;
int build(int L1,int R1,int L2,int R2)//分别代表先和中,先:根左右;中:左根右
{
    if(L1>R1)
        return 0;
    int root=pre[L1];
    int p=L2;
    while(in[p]!=root)
        p++;
    int cnt=p-L2;//左子树个数
    lch[root]=build(L1+1,L1+cnt,L2,L2+cnt-1);
    rch[root]=build(L1+cnt+1,R1,L2+cnt+1,R2);
    return root;

}
void postvis(int root)
{
    if(root==0)
        return;
    postvis(lch[root]);
    postvis(rch[root]);
    post[t]=root;
    cout<<post[t];
    t++;
    if(t<N)
        cout<<" ";

}
int main()
{
    scanf("%d",&N);
    char str[5];
    stack<int> st;
    int x,preindex=0;
   int inindex=0;
    for(int i=0; i<2*N; i++)
    {
        scanf("%s",str);
        if(strcmp(str,"Push")==0)
        {
            scanf("%d",&x);
            pre[preindex++]=x;
            st.push(x);
        }
        else
        {
            in[inindex++]=st.top();
            st.pop();
        }


    }
    build(0,N-1,0,N-1);
    postvis(pre[0]);

    return 0;
}

6、森林的遍历:分为前序遍历和后序遍历,其实就是按照树的先根遍历和后根遍历依次访问森林的每一棵树

7、树、森林的前根/序遍历和二叉树的前序遍历结果相同

     树、森林的后根/序遍历和二叉树的中序遍历结果相同

赫夫曼树及其应用

赫夫曼树中没有度为1的结点,则一棵有n个叶子结点的赫夫曼树共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中

1、结点的路径长度:从根结点到该结点的路径上的连接数

2、树的路径长度:树中每个叶子结点的路径长度之和

3、结点带权路径长度:结点的路径长度与结点权值的乘积

4、树的带权路径长度(WPL):树中所有叶子结点的带权路径长度之和

5、WPL的值越小,说明构造出来的二叉树性能越优

6、定长编码:像ASCII编码

     变长编码:单个编码的长度不一样,可以根据整体出现频率来调节

     前缀码:所谓的前缀码,就是没有任何码字是其他码字的前缀

typedef struct
{
	unsigned int weight;
	unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;//动态分配数组存储赫夫曼树
typedef char* *HuffmanCode;//动态分配数组存储赫夫曼树编码表,相当于一个指针数组,数组的每一个都是一个指针
//求赫夫曼树编码的算法
void HuffmanCoding(HuffmanTree &HT,HuffmanCode&HC,int *w,int n)
//w存放n个字符的权值(均大于0),构造赫夫曼树HT,并求出n个字符的赫夫曼树编码HC
{
	if(n<=1)
	{
		return;
	}
	int m=2*n-1;
	HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
	for(p=HT,i=1;i<=n;++i,++p,++w)
		*p={*w,0,0,0};
	for(;i<=m;++i,++p)
		*p={0,0,0,0};
	for(i=n+1;i<=m;++i)//建立赫夫曼树
	//在HT[1..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2
	{
		Select(HT,i-1,s1,s2);
		HT[s1].parent=i;
		HT[s2].parent=i;
		HT[i].lchild=s1;
		HT[i].rchild=s2;
		HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
	//从叶子到根逆向求每个字符的赫夫曼树编码
	HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//分配n个字符编码的头指针向量
	cd=(char *)malloc(n*sizeof(char ));//分配求编码的工作空间
	cd[n-1]="\0";//编码结束符
	for(i=1;i<=n;++i)//逐个字符求赫夫曼树编码
	{
		start=n-1;//编码结束符位置
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到根逆向求编码
			if(HT[f].lchild==c)
				cd[--start]="0";
			else
				cd[--start]="l";
			HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
			strcpy(HC[i],&cd[start]);//从cd复制编码到HC
	}
	free(cd);
}



}

二叉查找树(BST)

1、二叉树的递归定义:①要么二叉查找树是一颗空树②要么二叉查找树由根结点、左子树、右子树组成,,其中左子树和右子树都是二叉查找树,且左子树所有结点的数据域均小于或等于根结点的数据域,右子树所有结点的数据域均大于根结点的数据域

2、查找操作

、search函数查找二叉查找树中数据域为x的结点

//search函数查找二叉查找树中数据域为x的结点
void search(node* root,int x)
{
	if(root==NULL)//如果是空树,查找失败
	{
		printf("search failed\n");
		return;
	}
	if(x==root->data)
	{
		printf("%d\n",root->data);
	}
	else if(x<root->data)
	{
		search(root->lchild,x);
	}
	else
	{
		search(root->data,x);
	}
}

3、插入操作

insert 函数将在二叉树中插入一个数据域为x的新结点(注意参数root需要加引用&)

//insert 函数将在二叉树中插入一个数据域为x的新结点(注意参数root需要加引用&)
void insert(node* &root,int x)
{
	if(root==NULL)//空树,说明查找失败,也即插入位置
	{
		root=newNode(x);//新建结点
		return;
	}
	if(x==root->data)//查找成功,说明结点已存在,直接返回
	{
		return;
	}
	else if(x<root->data)
	{
		insert(root->lchild,x);
	}
	else
	{
		insert(root->rchild,x);
	}
}
node* newNode(int v)//生成一个新结点,v为结点权值
{
	node* Node=new node;
	Node->data=v;
	Node->lchild=Node->child=NULL;
	return Node;
}

4、二叉查找树的建立

//二叉查找树的建立
node* create(int data[],int n)
{
	node* root=NULL;//新建根结点root
	for(int i=0;i<n;i++)
	{
		insert(root,data[i]);
	}
	return root;
}

5、二叉查找树的删除:把以二叉查找树中比结点权值小的最大结点称为该结点的前驱,而把比结点权值大的最小结点称为该结点的后继(结点的前驱是该结点左子树中的最右结点,而结点的后继则是该结点右子树的最左结点)

//删除以root为根结点的树中权值为x的结点
void deleteNode(node* &root,int x)
{
	if(roo==NULL)
	{
		return;//不存在权值为x的结点
	}
	if(root->data==x)//找到欲删除结点
	{
		if(root->lchild==NULL&&root->rchild==NULL)//叶子结点直接删除
		{
			root=NULL;//把root地址设为NULL,父结点就引用不到它了
		}
		else if(root->lchild!=NULL)
		{
			//左子树不为空时
			node* pre=findMax(root->lchild);
			root->data=pre->data;//用前驱覆盖root
			deleteNode(root->lchild,pre->data);//在左子树中删除结点next
		}
		else//当右子树不为空时
		{
			node* next=findMin(root->rchild);//找root后继
			root->data=next->data;
			deleteNode(root->rchild,next->data);
		}
	}
	else if(root->data>x)
	{
		deleteNode(root->lchild,x);//在左子树中删除x
	}
	else
	{
		deleteNode(root->rchild,x);//在右子树中删除x
	}
}


//寻找以root为根结点的树中的最大权值结点
node* findMax(node* root)
{
	while(root->rchild!=NULL)
	{
		root=root->rchild;//不断往右,直到没有右孩子
	}
	return root;
}
//寻找以root为根结点的树中的最小权值结点
node* findMin(node* root)
{
	while(root->lchild!=NULL)
	{
		root=root->lchild;
        //不断往左,直到没有左孩子
	}
	return root;
}

并查集

1、①合并:合并两个集合  ② 查找:判断两个集合是否在一个集合

2、并查集的基本操作

①初始化:一开始,每个元素都是一个独立的集合,因此需要令所有father[i]=i

②查找: 实现方式为递推或者递归

递推:

int findFather(int x)
{
	while(x!=father[x])
		x=father[x];
	return x;
}

递归

int findFather(int x)
{
	if(x==father[x])
		return x;
	else
		return findFather(father[x]);

}

③合并:一般是先判断两个元素是否属于同一个集合,只有当两个元素属于不同集合时才合并,而合并的过程一般是把其中一个集合的根结点指向另一个集合的根结点

void Union(int a,int b)
{
	int faA=findFather(a);
	int faB=findFather(b);
	if(faA!=faB)//如果不属于同一个集合
		father[faA]=faB;//合并它们
}

3、路径压缩:相当于把当前查询结点的路径上的所有结点的父亲都指向根结点

int findFather(int x)
{
	//由于x在下面的while中会变成根结点,因此先把原先的x保存一下
	int a=x;
	while(x!=father[x])//寻找根结点
	{
		x=father[x];
	}
	//到这里,x存放的是根结点,下面把路径上的所有结点的father都改成根结点
	while(a!=father[a])
	{
		int z=a;//因为a要被father[a]覆盖,所以先保存a的值
		a=father[a];//a回溯父亲结点
		father[z]=x;
	}
	return x;//返回根结点
}

路径压缩的递归写法

int findFather(int v)
{
    if(v==father[v])
		return v;//找到根结点
	else
	{
		int F=findFather(father[v]);//递归寻找father[v]的根结点F
		father[v]=F;
		return F;
	}
}

1、向下调整:总是将当前结点V与它的左右孩子比较(如果有的话),加入孩子中存在权值比结点V的权值大的,就将其中权值最大的那个孩子结点与结点V交换;交换完毕后继续让结点V和孩子比较,直到结点V的孩子的权值都比结点V的权值小或是结点V不存在孩子结点

向下调整的代码

//对heap数组在[low,high]范围进行向下调整
//其中low为欲调整结点的数组下标,high一般为堆的最后一个元素的数组下标
void downAdjust(int low,int high)
{
	int i=low,j=i*2;//i为欲调整结点,j为其左孩子
	while(j<=high)//存在孩子结点
	{
		//如果右孩子存在且右孩子的值大于左孩子
		if(j+1<=high&&heap[j+1]>heap[j])
			j=j+1;
		//如果孩子中最大的权值比欲调整结点i大
		if(heap[i]<heap[j])
		{
			swap(heap[i],heap[j]);
			i=j;
			j=i*2;
		}
		else
			break;//孩子的权值均比欲调整结点i小,调整结束
	}
}

2、建堆

保证每个结点都是以其为根结点的子树中的权值最大的结点


//建堆
void createHeap()
{
	for(int i=n/2;i>=1;i--)
		downAdjust(i,n);
}

3、删除堆顶元素

//删除堆顶元素
void deleteTop()
{
	heap[1]=heap[n--];//用最后一个元素覆盖堆顶元素,并让元素个数减一
	downAdjust(1,n);//向下调整堆顶元素
}

4、向上调整,也就是我那个堆里添加一个元素

//对heap数组在[low,high]范围进行向上调整
//其中low一般设为1,high表示欲调整结点的数组下标
void upAdjust(int low,int high)
{
	int i=high,j=i/2;//i为欲调整结点,j为其父亲
	while(j>=low)//父亲在[low,high]范围内
	{
		
		//父亲权值小于欲调整结点i的权值
		if(heap[j]<heap[i])
		{
			swap(heap[i],heap[j]);
			i=j;
			j=i/2;
		}
		else
			break;//孩子的权值均比欲调整结点i小,调整结束
	}
}
//调加元素
void insert(int x)
{
	heap[++n]=x;
	upAdjust(1,n);
}

堆排序

void heapSort()
{
	createHeap();//建堆
	for(int i=n;i>1;i--)//倒着排序,直到堆中只有一个元素
	{
		swap(heap[i],heap[1]);//交换heap[i]与堆顶
		downAdjust(1,i-1);//调整堆顶
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值