数据结构---二叉查找数与堆

本文详细介绍了二叉查找树的插入、删除、查找操作,以及如何找到节点的后继和前驱。此外,还讲解了二叉树的前、中、后序遍历和按层遍历。接着,文章探讨了堆的概念,包括小顶堆、大顶堆和优先级队列,以及堆排序的实现。最后,提到了与这些主题相关的LeetCode练习题,如翻转二叉树、计算二叉树的最大深度、验证二叉查找树和寻找路径总和。
摘要由CSDN通过智能技术生成

一、二叉树

1.1 实现一个二叉查找树,并且支持插入、删除、查找操作

二叉查找数:
二叉查找树(Binary Search Tree),又被称为二叉搜索树。
它是特殊的二叉树:对于二叉树,假设x为二叉树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。那么,这棵树就是二叉查找树。

//节点定义
typedef struct BSTreeNode{
    int data;                     
    struct BSTreeNode *left;
    struct BSTreeNode *right;
    struct BSTreeNode *parent;
}Node,*BSTree;
 

//插入
BSTree* insertTree(BSTree *t,int data)
{
    if(t==NULL)
    {
        t=(BSTree *)malloc(sizeof(BSTree));
        t->data=data;
        t->right=NULL;
        t->left=NULL;
        return t;
    }
    if(t->data>data)
    {
        t->left=insertTree(t->left,data);
    }
    else if(t->data < data)
    {
        t->right=insertTree(t->right,data);
    }
    else{
        printf("%d已经在树中!\n",data);
    }
}

//删除
BSTree* deleteTree(BSTree *T,int data)
{
    if(T==NULL)
    {
        return T;
    }
    if(T->data>data){
        T->left=deleteTree(T->left,data);
    }
    else if(T->data<data)
    {
        T->right=deleteTree(T->right,data);
    }
    else if(T->left != NULL && T->right != NULL)
    {
        T->data=findMin(T->right)->data;
        T->right=deleteTree(T->right,data);
    }
    else
    {
        T=(T->left != NULL)?T->left:T->right;
    }
    return T;
}

//查找
BSTree* search(BSTree *T,int data)
{
    if(T==NULL)
    {
        printf("Tree is NUll\n");
    }
    else if(T->data>data)
    {
        return search(T->left,data);
    }
    else if(T->data<data)
    {
        return search(T->right,data);
    }
    else 
    {
        return T;
    }
}
1.2 实现查找二叉查找树中某个节点的后继、前驱节点
//节点的前驱是该节点的左子树的最大节点
//节点的后继是该节点的右子树中的最小节点

Node* bstree_predecessor(Node *x)
{
    // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
    if (x->left != NULL)
        return bstree_maximum(x->left);

    // 如果x没有左孩子。则x有以下两种可能:
    // 1. 若x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
    // 2. x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
    Node* y = x->parent;
    while ((y!=NULL) && (x==y->left))
    {
        x = y;
        y = y->parent;
    }

    return y;
}

Node* bstree_successor(Node *x)
{
    // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
    if (x->right != NULL)
        return bstree_minimum(x->right);

    // 如果x没有右孩子。则x有以下两种可能:
    // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
    // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
    Node* y = x->parent;
    while ((y!=NULL) && (x==y->right))
    {
        x = y;
        y = y->parent;
    }

    return y;
}
1.3 实现二叉树前、中、后序以及按层遍历
//前序遍历 递归实现
void preorder(BSTree tree){
    if(tree != NULL){
        printf("%d ",tree->key);
        preorder(tree->left);
        preorder(tree->right);
    }
}

//中序遍历
void inorder(BSTree tree){
    if(tree != NULL){
        inorder(tree->left);
        printf("%d ",tree->key);
        inorder(tree->right);
    }
}

//后序遍历
void postorder(BSTree tree){
    if(tree != NULL){
        postorder(tree->left);
        postorder(tree->right);
        printf("%d ",tree->key);
    }
}

二、堆

2.1 小顶堆、大顶堆、优先级队列

堆:堆实际上是一棵完全二叉树,任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。分为大顶堆和小顶堆。

大顶堆:所有节点的子节点比其自身小的堆。Key[i]>=Key[2i+1]&&key>=key[2i+2]

小顶堆:所有节点的子节点比其自身大的堆。Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]

优先级队列:优先级队列是带有优先级的队列,普通的队列是从队尾进数据,从队头出数据,而优先级队列入数据一样,不过出数据的时候是根据数据的优先级从高到低进行出数据,这种数据类型适用于一些文件管理的软件,优先级队列的使用也会加快工作效率,相比于一般队列,优先级队列也不会出现假溢出的问题。

// 初始化大顶堆
void initHeapMax(int a[], int len) {
 // 从完全二叉树最后一个非子节点开始
 // 在数组中第一个元素的索引是0
 // 第n个元素的左孩子为2n+1,右孩子为2n+2,
 // 最后一个非子节点位置在(n - 1) / 2
 for (int i = (len - 1) / 2; i >= 0; --i) {
  adjustMaxHeap(a, len, i);
 }
}
  
//调整大顶堆
void adjustMaxHeap(int a[], int len, int parentNodeIndex) {
 // 若只有一个元素,那么只能是堆顶元素,也没有必要再排序了
 if (len <= 1) {
  return;
 }
  
 // 记录比父节点大的左孩子或者右孩子的索引
 int targetIndex = -1;
  
 // 获取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;
  
 // 没有左孩子
 if (leftChildIndex >= len) {
  return;
 }
  
 // 有左孩子,但是没有右孩子
 if (rightChildIndex >= len) {
  targetIndex = leftChildIndex;
 }
 // 有左孩子和右孩子
 else {
  // 取左、右孩子两者中最大的一个
  targetIndex = a[leftChildIndex] > a[rightChildIndex] ? leftChildIndex : rightChildIndex;
 }
  
 // 只有孩子比父节点的值还要大,才需要交换
 if (a[targetIndex] > a[parentNodeIndex]) {
  int temp = a[targetIndex];
   
  a[targetIndex] = a[parentNodeIndex];
  a[parentNodeIndex] = temp;
   
   
  // 交换完成后,有可能会导致a[targetIndex]结点所形成的子树不满足堆的条件,
  // 若不满足堆的条件,则调整之使之也成为堆
  adjustMaxHeap(a, len, targetIndex);
 }
}

//小顶堆初始化
void initHeapMin(int a[], int len) {
 for (int i = (len - 1) / 2; i >= 0; --i) {
  adjustMinHeap(a, len, i);
 }
}
  
void adjustMinHeap(int a[], int len, int parentNodeIndex) {
 // 若只有一个元素,那么只能是堆顶元素,也没有必要再排序了
 if (len <= 1) {
  return;
 }
  
 // 记录比父节点大的左孩子或者右孩子的索引
 int targetIndex = -1;
  
 // 获取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;
  
 // 没有左孩子
 if (leftChildIndex >= len) {
  return;
 }
  
 // 有左孩子,但是没有右孩子
 if (rightChildIndex >= len) {
  targetIndex = leftChildIndex;
 }
 // 有左孩子和右孩子
 else {
  // 取左、右孩子两者中最上的一个
  targetIndex = a[leftChildIndex] < a[rightChildIndex] ? leftChildIndex : rightChildIndex;
 }
  
 // 只有孩子比父节点的值还要小,才需要交换
 if (a[targetIndex] < a[parentNodeIndex]) {
  int temp = a[targetIndex];
   
  a[targetIndex] = a[parentNodeIndex];
  a[parentNodeIndex] = temp;
   
   
  // 交换完成后,有可能会导致a[targetIndex]结点所形成的子树不满足堆的条件,
  // 若不满足堆的条件,则调整之使之也成为堆
  adjustMinHeap(a, len, targetIndex);
 }
}

2.2 实现堆排序
//用大顶堆实现
void heapSort(int a[], int len) {
        if (len <= 1) {
            return;
         }
  
 // 初始堆成无序最大堆
 initHeapMax(a, len);
  
 for (int i = len - 1; i > 0; --i) {
  // 将当前堆顶元素与最后一个元素交换,保证这一趟所查找到的堆顶元素与最后一个元素交换
  if (a[0] > a[i]) {
   int temp = a[0];
   a[0] = a[i];
   a[i] = temp;
  }
   
  adjustMaxHeap(a, i - 1, 0);
 }
}

三、对应的 LeetCode 练习题

3.1 Invert Binary Tree(翻转二叉树)

LeetCode 226

//递归  交换根节点的左右子树,对左右子树分别执行递归反转

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL)
            return NULL;
        TreeNode *pNode=root->left;
        root->left=invertTree(root->right);
        root->right=invertTree(pNode);
        return root;
        
    }
};
3.2 Maximum Depth of Binary Tree(二叉树的最大深度)

leetcode 104

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* root) {
        int l,r,m;
        if(root != NULL)
        {
            l=maxDepth(root->left);
            r=maxDepth(root->right);
            m=max(l,r);
            return m+1;
        }
        else 
            return 0;
    }
};
3.3 Validate Binary Search Tree(验证二叉查找树)

LeetCode 98

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        return isValidBST(root,LONG_MIN,LONG_MAX);
    }
    bool isValidBST(TreeNode *root,long min,long max){
        if(!=root) return true;
        if(root->val <= min || root->val >= max) return false;
        return isValidBST(root->left,min,root->val) && isValidBST(root->right,root->val,max);
    }
};
3.4 Path Sum(路径总和)

LeetCode 112

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool hasPathSum(TreeNode* root, int sum) {
        if(root == NULL)
            return false;
        if(root -> left == NULL && root->right == NULL && root->val==sum)
            retrun true;
        return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
    }
};

The end~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值