c++简单实现二叉树、AVL、最大最小堆、霍夫曼、集合(问题:malloc,typedef,指针)

前言

顺序查找
二分查找
树 n个结点构成集合 儿子兄弟表示法 所有结点结构相同省时省空间
二叉树
二叉树遍历 先序遍历 中序遍历 后序遍历 层序遍历
顺序存储结构 数组等实现的完全二叉树 他们的标号连续 结点父亲为i 左孩子为2i 右孩子为2i+1
一般的二叉树也可以补全结点为完全二叉树 但浪费空间
链表存储

typedef的使用

typedef的有无在c/c++ 中的区别 转载https://blog.csdn.net/rjw9999/article/details/6655821

在C中定义一个结构体类型要用typedef:

	typedef struct Student        
	{
	    int a;
	}Stu;

于是在声明变量的时候就可:Stu stu1;如果没有typedef就必须用struct Student stu1;来声明
但在c++里很简单,直接

	struct Student
	{
	int a;
	};

于是就定义了结构体类型Student,声明变量时直接Student stu2;

在c++中如果用typedef的话,又会造成区别:

	struct Student   
	{   
	    int a;   
	}stu1;      //stu1是一个变量   
	typedef struct Student2   
	{   
	    int a;   
	}stu2;      //stu2是一个结构体类型  typedef相当于定义了一个别名 
	*/

stack的使用

转载https://blog.csdn.net/zichen_ziqi/article/details/80807989

队列的使用

转自:http://www.169it.com/article/2718050585107790752.html

malloc/new 的使用方法与区别

本文链接:https://blog.csdn.net/qq_26816591/article/details/52214313

c++ NULL/nullptr 的区别

转载https://www.cnblogs.com/Philip-Tell-Truth/p/6594632.html
NULL 相当于0会当作int不会当作void*来看待

二叉树

#include <iostream>
#include <string>
#include <algorithm>
#include <queue> //顺序队列
#include <stack> //先进后出
using namespace std;
struct Node
{
    int value;
    Node *left = NULL;
    Node *right = NULL;
};
Node a;

二叉树遍历

二叉树遍历 先序遍历 先访问根结点 先序遍历左子树 先序遍历其右子树 根左右
void pre_traversal(Node *root)
{
    if (root)
    {
        cout << root->value;
        pre_traversal(root->left);
        pre_traversal(root->right);
    }
}
二叉树遍历 中序遍历 中序遍历左子树 访问根结点 中序遍历其右子树 左根右
void mid_traversal(Node *root)
{
    if (root)
    {
        mid_traversal(root->left);
        cout << root->value;
        mid_traversal(root->right);
    }
}
二叉树遍历 后序遍历 后序遍历左子树 后序遍历其右子树 访问根结点 左右根
void back_traversal(Node *root)
{
    if (root)
    {
        back_traversal(root->left);
        back_traversal(root->right);
        cout << root->value;
    }
}
二叉树非递归遍历 堆栈 中序遍历
void inorder_traversal(Node *root)
{
    Node *temp = root;
    stack<Node> s;               // 创建队列
    while (temp || !s.empty()) //栈不为空或当前结点不为空
    {
        while (temp)
        {
            s.push(*temp);
            temp = temp->left;
        } //当结点不为空时一直将左叶子结点插入
        if (!s.empty())
        {
          	temp = s.pop(); 
            cout << temp->value;
            temp = temp->right; //左根右
            //当前节点没有右右节点则弹出栈顶元素父节点
        }
    }
}
层序遍历 遍历的本质就是如何将树的二维结构变为一维的线性结构

假如我访问了左儿子怎么才能访问到右儿子
需要一个存储结构存放暂时不访问的结点
依靠队列 将结点放入 之后进行抛出并将结点的左右儿子放进去一步步操作

void level_traversal(Node *root)
{
    queue<Node> temp;
    if (!root)
        return; // 如果树为空返回
    temp.push(*root);
    while (!temp.empty())
    {
        Node *t = temp.pop;
        cout << t->value;
        if (t->left)
        {
            temp.push(*t->left);
        }
        if (t->right)
        {
            temp.push(*t->right);
        }
    }
}

二叉树结点/高度

输出二叉树的所有叶子结点
void child_traversal(Node *root)
{
    if (root)
    {
        if (!root->left && !root->right)
            cout << root->value;
        pre_traversal(root->left);
        pre_traversal(root->right);
    }
}
求二叉树的高度

递归 max(Hl,Hr)+1 先求左子树的最大高度 再求右子树的最大高度 采用后序遍历

int max_height(Node *root)
{
    int Hl, Hr, MaxH;
    if (root)
    {
        Hl = max_height(root->left);
        Hr = max_height(root->right);
        MaxH = (Hl > Hr) ? Hl : Hr;
        return (MaxH + 1);
    }
    else
        return 0;
}

二元运算的树表示及遍历 (中缀表达式不准当没括号时)
两个遍历序列 前中 或 中后 可确定唯一二元式序列 但是前后不行

同构

左右子树交换后可以得到相同结构 数组结构表示二叉树 静态链表递归每层对比

struct Tree
{
    int value;
    int L;
    int right;
} t1[size], t2[size];
void build()
{
    Tree R1, R2;
    R1 = build(); //首先找到根结点没有结点之乡的点为根结点
    R2 = build();
    if (check(R1, R2))
    {
        ...
    }
}
int check(Tree a, Tree b)
{
    if ((a == NULL) && (b == NULL))
        return 1;
    if ((a == NULL) && (b != NULL))
        || ((a != NULL) && (b == NULL)) return 0;
    if (/*判断ab两个树根结点是否相同*/)
        return 0;
    if (/*两个根结点左右位置不交换*/)
    { /*递归*/
    }
    else
    { /*递归*/
    }
}

binary search tree 二叉搜索树 左小右大 递归查找 / 循环查找

//二叉搜索树 递归查找 NULL=0FALSE=0TRUE记得是非零值
Node* binary_find(int value, Node *root)
{
    if (!root)
        return NULL;
    if (value > root->value)
    {
        binary_find(value, root->right);
    }
    else if (value < root->value)
    {
        binary_find(value, root->left);
    }
    else
        return root;
}

Node* binary_find2(int value, Node *root)
{
    while (root)
    {
        if (value > root->value)
        {
            root = root->right;
        }
        else if (value < root->value)
        {
            root = root->left;
        }
        else
            return root;
    }
    return NULL;
}
//为了防止二叉搜索树一边倒 查找效率取决于树的高度

查找二叉搜索树的最大 最小值

//查找二叉搜索树的最大 最小值
Node* FindMin(Node *root)
{
    if(!root) return NULL;
    else if (!root->left) return root;
    else return FindMin(root->left);
}
Node* FindMax(Node *root)
{
    if(root)
    {
        while(root->right) root = root->right;
    }
    return root;
}

二叉搜索树的插入删除

 //二叉搜索树的插入删除 
void insert(int a,Node* root)//返回插入结点
{
    if(!root)
    {
        root = (Node*)malloc(sizeof(Node));
        root->value = a;
        root->left = NULL;
        root->right = NULL;
    }
    else
    {
        if(a<root->value)
        {
            return insert(a,root->left);
        }
        else if(a>root->value)
        {
            return insert(a,root->right);
        }
    }
}
//二叉树的使用 例如字典序
/*二叉树删除结点 用另一节点代替套被删除的结点 右子树的最小元素或者是
左子树的最大元素 这样的好处是那么这个节点一定不会有两个孩子*/
void delete_binary(int x,Node* root)
{
    Node* temp;//用来存放替换的
    if(!root) cout<<"所要删除的元素不存在";
    else if(x<root->value) delete_binary(x, root->left);
    else if(x>root->value) delete_binary(x, root->right);
    else
    {
        if(root->left&&root->right) //左右孩子节点都存在时找右子树的最小节点
        {
            temp = FindMin(root->right);
            root->value = temp->value;
            delete_binary(temp->value,root->right); // 递归删除找到的替换的结点并找右子树最小的元素代替
        }
        else //只有一个孩子结点或者没有
        {
            temp = root;
            if(!left)//有右孩子或无子结点
            {
                root=root->right;
            }
            else if(!right)//有左孩子或无子结点
            {
                root=root->left;
            }
            free(temp);
        }
    }
}

平衡二叉树

平衡二叉树 任意一个结点左右两个子树的高度差不超过1
当插入的结点在发现者右子树的右边因而叫RR插入需要RR旋转(右单旋)
当插入的结点在发现者左子树的左边因而叫LL插入需要LL旋转(左单旋)
当插入的结点在发现者左子树的右子树因而叫LR插入需要LR旋转(左右旋转)
当插入的结点在发现者右子树的左子树因而叫RL插入需要RL旋转(右左旋转)

AVL 树的实现

typedef struct AVLNode *Position;
typedef Position AVLTree; /* AVL树类型 */
struct AVLNode{
    ElementType Data; /* 结点数据 */
    AVLTree Left;     /* 指向左子树 */
    AVLTree Right;    /* 指向右子树 */
    int Height;       /* 树高 */
};
 
int Max ( int a, int b )
{
    return a > b ? a : b;
}
 
AVLTree SingleLeftRotation ( AVLTree A )
{ /* 注意:A必须有一个左子结点B */
  /* 将A与B做左单旋,更新A与B的高度,返回新的根结点B */     
 
    AVLTree B = A->Left;
    A->Left = B->Right;
    B->Right = A;
    A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
    B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
  
    return B;
}
 
AVLTree DoubleLeftRightRotation ( AVLTree A )
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
  /* 将A、B与C做两次单旋,返回新的根结点C */
     
    /* 将B与C做右单旋,C被返回 */
    A->Left = SingleRightRotation(A->Left);
    /* 将A与C做左单旋,C被返回 */
    return SingleLeftRotation(A);
}
 
/*************************************/
/* 对称的右单旋与右-左双旋请自己实现 */
/*************************************/
 
AVLTree Insert( AVLTree T, ElementType X )
{ /* 将X插入AVL树T中,并且返回调整后的AVL树 */
    if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
        T = (AVLTree)malloc(sizeof(struct AVLNode));
        T->Data = X;
        T->Height = 0;
        T->Left = T->Right = NULL;
    } /* if (插入空树) 结束 */
 
    else if ( X < T->Data ) {
        /* 插入T的左子树 */
        T->Left = Insert( T->Left, X);
        /* 如果需要左旋 */
        if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
            if ( X < T->Left->Data ) 
               T = SingleLeftRotation(T);      /* 左单旋 */
            else 
               T = DoubleLeftRightRotation(T); /* 左-右双旋 */
    } /* else if (插入左子树) 结束 */
     
    else if ( X > T->Data ) {
        /* 插入T的右子树 */
        T->Right = Insert( T->Right, X );
        /* 如果需要右旋 */
        if ( GetHeight(T->Left)-GetHeight(T->Right) == -2 )
            if ( X > T->Right->Data ) 
               T = SingleRightRotation(T);     /* 右单旋 */
            else 
               T = DoubleRightLeftRotation(T); /* 右-左双旋 */
    } /* else if (插入右子树) 结束 */
 
    /* else X == T->Data,无须插入 */
 
    /* 别忘了更新树高 */
    T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
     
    return T;
}

堆heap 优先队列的实现

堆heap 优先队列的实现
数组实现 插入-元素总是插入到尾部 删除-查找最大胡哦最小关键字
数组中删去需要移动的元素 复杂度O(n)因为删去一个元素时需要将元素向前移
链表的实现 相对于数组来讲 删除结点 复杂度O(1) 指针指向下一个元素即可
有序数组 logn
二叉树叶子结点为最大值
堆 完全二叉树进行存储 结点为最大值/最小值 最大堆/最小堆

堆的构造

typedef struct HNode *Heap; /* 堆的类型定义 */
struct HNode {
    int *Data; /* 存储元素的数组 */
    int Size;          /* 堆中当前元素个数 */
    int Capacity;      /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */
 
#define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */
 
MaxHeap CreateHeap( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */
 
    MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
    H->Data = (int *)malloc((MaxSize+1)*sizeof(int));
    H->Size = 0;
    H->Capacity = MaxSize;
    H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
 
    return H;
}
bool IsFull( MaxHeap H )
{
    return (H->Size == H->Capacity);
}

元素插入

  /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
bool Insert( MaxHeap H, int X )
{
    int i;
    if ( IsFull(H) ) { 
        printf("最大堆已满");
        return false;
    }
    i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
    for (i; H->Data[i/2] < X; i/=2 )
        H->Data[i] = H->Data[i/2]; /* 上滤X */
    H->Data[i] = X; /* 将X插入 */
    return true; //插入到最后位置再与父元素减缓
}
 
#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */
 
bool IsEmpty( MaxHeap H )
{
    return (H->Size == 0);
}

删除元素

 /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
int DeleteMax( MaxHeap H )
{ 
    int Parent, Child;
    int MaxItem, X;
 
    if ( IsEmpty(H) ) {
        printf("最大堆已为空");
        return ERROR;
    }
 
    MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 取出左后一个数 并且size变小 */
    for( Parent=1; Parent*2<=H->Size; Parent=Child ) // 与子结点比较 并交换下滤
    {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
 
    return MaxItem;
} 

建造最大堆

/*----------- 建造最大堆 -----------
可以考虑每次选出数组中最大的元素进行插入  
也可以将整个数组构造好之后再重新整理顺序
*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    int X;
 
    X = H->Data[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
}
 
void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */
 
    int i;

    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- )
        PercDown( H, i );
}

哈夫曼树 依赖最小堆

哈夫曼树 依靠每个元素的概率来建树 带权路径最小
哈夫曼的构造 每次把权值最小的两棵二叉树合并
利用最小堆来实现选择数组中的最小元素效率要比排序算法要好

struct HuffmanTree
{
    int weight;
    HuffmanTree* Left; 
    HuffmanTree* Right;
};
HuffmanTree* huffman(MinHeap H)
{
    int i;
    HuffmanTree* temp;
    BuildMinHeap(H);//构建最小堆
    for(i=1;i<H->Size;i++)
    {
        temp = (HuffmanTree*)malloc(sizeof(HuffmanTree));
        temp->Left = DeleteMin(H);
        temp->Right = DeleteMin(H);
        temp->weight = temp->Left->weight+ temp->Right->weight;
        insert(H,temp);
    }
    temp = pop(H);//将最后值弹出
    return temp;
}

集合相交

/*
集合相交 或者线路连通 那么若各结点相连 
根节点相同则说明是交集或者是连通
数组中每个元素的类型描述
*/
typedef struct {
    ElementType  Data;
    int parent;
}SetType;
/*循环遍历每个元素的parent 一层层向上遍历 若最后根结点相同 那么集合相同*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值