C++ 非STL数据结构学习——1.1 二叉树与二叉搜索树

链表实现式

首先是二叉搜索树的链表模板,作为二叉树的特例,常用的操作有:

1. 节点定义
struct Node {
    int key;
    Node* left;
    Node* right;
    
    Node(int k) : key(k), left(nullptr), right(nullptr) {}
};
2. 节点的插入
Node* insert(Node* root, int key) {
    if (root == nullptr) {
        return new Node(key);
    }
    
    if (key < root->key) {
        root->left = insert(root->left, key);
    } else {
        root->right = insert(root->right, key);
    }
    
    return root;
}
3. 节点的搜索
Node* search(Node* root, int key) {
    if (root == nullptr || root->key == key) {
        return root;
    }
    
    if (key < root->key) {
        return search(root->left, key);
    }
    
    return search(root->right, key);
}
4. 返回最小节点
Node* minValueNode(Node* node) {
    Node* current = node;
    while (current && current->left != nullptr) {
        current = current->left;
    }
    return current;
}
5. 节点的删除
Node* deleteNode(Node* root, int key) {
    if (root == nullptr) {
        return root;
    }
    
    if (key < root->key) {
        root->left = deleteNode(root->left, key);
    } else if (key > root->key) {
        root->right = deleteNode(root->right, key);
    } else {
        if (root->left == nullptr) {
            Node* temp = root->right;
            delete root;
            return temp;
        } else if (root->right == nullptr) {
            Node* temp = root->left;
            delete root;
            return temp;
        }
        
        Node* temp = minValueNode(root->right);
        root->key = temp->key;
        root->right = deleteNode(root->right, temp->key);
    }
    
    return root;
}

这里说明一下:删除的节点若为左儿子为空,就直接把右子树平移覆盖上去;右儿子为空,直接把左子树平移覆盖上去;若两儿子都存在,则复制左子树最大节点或右子树最小节点覆盖 删除节点 位置,再清除原件。这样保证结果还是二叉搜索树。

6. 遍历(以中序遍历示范)
void inorderTraversal(Node* root) {
    if (root != nullptr) {
        inorderTraversal(root->left);
        cout << root->key << " ";
        inorderTraversal(root->right);
    }
}

整理一下,写成类的形式:

#include <iostream>
using namespace std;

// 二叉树节点定义
struct Node {
    int key;
    Node* left;
    Node* right;
    
    Node(int k) : key(k), left(nullptr), right(nullptr) {}
};

// 二叉搜索树类定义
class BinarySearchTree {
public://事实上,为了程序安全性,应该用private,然后提供接口便于数据向外输出。但用于做题可以不考虑
    Node* root;

    Node* insert(Node* node, int key) {
        if (node == nullptr) {
            return new Node(key);
        }
        
        if (key < node->key) {
            node->left = insert(node->left, key);
        } else {
            node->right = insert(node->right, key);
        }
        
        return node;
    }

    Node* minValueNode(Node* node) {
        Node* current = node;
        while (current && current->left != nullptr) {
            current = current->left;
        }
        return current;
    }

    Node* deleteNode(Node* node, int key) {
        if (node == nullptr) {
            return node;
        }
        
        if (key < node->key) {
            node->left = deleteNode(node->left, key);
        } else if (key > node->key) {
            node->right = deleteNode(node->right, key);
        } else {
            if (node->left == nullptr) {
                Node* temp = node->right;
                delete node;
                return temp;
            } else if (node->right == nullptr) {
                Node* temp = node->left;
                delete node;
                return temp;
            }
            
            Node* temp = minValueNode(node->right);
            node->key = temp->key;
            node->right = deleteNode(node->right, temp->key);
        }
        
        return node;
    }

    void inorderTraversal(Node* node) {
        if (node != nullptr) {
            inorderTraversal(node->left);
            cout << node->key << " ";
            inorderTraversal(node->right);
        }
    }

    BinarySearchTree() : root(nullptr) {}
};

其次是二叉树的链表实现,主要框架如下:

#include <iostream>
#include <queue>
using namespace std;

// 二叉树节点定义
struct Node {
    int data;
    Node* left;
    Node* right;

    Node(int value) : data(value), left(nullptr), right(nullptr) {}
};

// 二叉树类定义
class BinaryTree {
private:
    Node* root;

public:
    BinaryTree() : root(nullptr) {}

    // 插入节点,录入节点部分常借助队列实现,同时层次遍历也可以借助队列,更为自然
    void insert(int value) {
        Node* newNode = new Node(value);

        if (root == nullptr) {
            root = newNode;
            return;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if (current->left == nullptr) {
                current->left = newNode;
                return;
            } else {
                q.push(current->left);
            }

            if (current->right == nullptr) {
                current->right = newNode;
                return;
            } else {
                q.push(current->right);
            }
        }
    }

    // 删除节点
    void deleteNode(int value) {
        if (root == nullptr) {
            cout << "Tree is empty. Cannot delete." << endl;
            return;
        }

        // 使用层序遍历找到要删除的节点
        queue<Node*> q;
        q.push(root);
        Node* target = nullptr;
        Node* deepest = nullptr;

        while (!q.empty()) {
            deepest = q.front();
            q.pop();

            if (deepest->data == value) {
                target = deepest;
            }

            if (deepest->left) {
                q.push(deepest->left);
            }

            if (deepest->right) {
                q.push(deepest->right);
            }
        }

        if (target == nullptr) {
            cout << "Node not found." << endl;
            return;
        }

        target->data = deepest->data;
        deleteDeepestNode(deepest);
    }

    // 删除最深的节点
    void deleteDeepestNode(Node* node) {
        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if (current == node) {
                current = nullptr;
                delete node;
                return;
            }

            if (current->right) {
                if (current->right == node) {
                    current->right = nullptr;
                    delete node;
                    return;
                } else {
                    q.push(current->right);
                }
            }

            if (current->left) {
                if (current->left == node) {
                    current->left = nullptr;
                    delete node;
                    return;
                } else {
                    q.push(current->left);
                }
            }
        }
    }

    // 查找节点
    Node* search(int value) {
        if (root == nullptr) {
            return nullptr;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if (current->data == value) {
                return current;
            }

            if (current->left) {
                q.push(current->left);
            }

            if (current->right) {
                q.push(current->right);
            }
        }

        return nullptr;
    }

    // 层序遍历
    void levelOrderTraversal() {
        if (root == nullptr) {
            return;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            cout << current->data << " ";

            if (current->left) {
                q.push(current->left);
            }

            if (current->right) {
                q.push(current->right);
            }
        }

        cout << endl;
    }
};

主要说明一下删除部分的原理,使用层次遍历方式可以找到最深的节点,其删除会更加简单。于是再找到需要删除的节点target和最深处节点deepest后,将原deepest覆盖到target处,接着删除原deepest节点即可。

而具体情境中,很多时候二叉树的输入不一定按照满二叉树的方式行进。可以基于模板灵活变化,比如输入节点有三个指标,值,左儿子,右儿子(以输入顺序记录第几号,例如P2).

void insert(int value,int leftserial,int rightserial) {
        Node* newNode = new Node(value,leftserial,rightserial);

        if (root == nullptr) {
            root = newNode;
            quantity++;
            return;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if (current->leftseiral == quantity) {
                current->left = newNode;
                quantity++;
                return;
            } else {
                if(current->left) q.push(current->left);
            }

            if (current->rightserial == quantity) {
                current->right = newNode;
                quantity++;
                return;
            } else {
                if(current->right ) q.push(current->right);
            }
        }
    }

数组实现式

在一个数组表示的二叉树中,如果根节点的索引为 1,那么对于任意一个节点的索引 i,其左子节点的索引为 2i,右子节点的索引为 2i + 1,父节点的索引为 i/2(向下取整)。

对于完全二叉树,叶节点的下标通常从 n/2 + 1 开始,其中 n 是数组中存储节点的数量。举个例子,如果一个数组表示的完全二叉树有 7 个节点,那么叶节点的索引从 4 开始(7/2 + 1 = 4)。

根据以上性质,很容易灵活使用一个数组实现二叉树与二叉搜索树。


P1. 洛谷p4715淘汰赛

采用数组存储这棵二叉树。

#include<iostream>
#include<cmath>
using namespace std;

struct country
{
    int serial;
    int strength;
};

int main()
{
    int n;cin>>n;
    int nums=pow(2,n);
    country* tree=new country[2*nums+5];
    for(int i=(2*nums-1)/2+1;i<=2*nums-1;i++) 
    {
        tree[i].serial=i-(2*nums-1)/2;
        cin>>tree[i].strength;
    }

    //for(int i=1;i<=2*nums-1;i++) cout<<tree[i]<<" ";
    for(int i=2*nums-1;i>=1;i=i-2)
    {
        tree[i/2].strength=tree[i].strength>tree[i-1].strength?tree[i].strength:tree[i-1].strength;
        tree[i/2].serial=tree[i].strength>tree[i-1].strength?tree[i].serial:tree[i-1].serial;
    }
    if(tree[2].strength<tree[3].strength) cout<<tree[2].serial;
    else cout<<tree[3].serial;

    delete[] tree;
    return 0;
}

P2. 洛谷p4913二叉树深度

尝试以链表实现。

#include <iostream>
#include <queue>
using namespace std;
int maxdepth=0;

struct Node {
    int depth=0;
    Node* left;
    Node* right;
    int leftserial;
    int rightserial;

    Node(int a,int b) : leftserial(a), rightserial(b) {left=nullptr;right=nullptr;}
};

class BinaryTree {
private:
    Node* root;
    int quantity=1;

public:
    BinaryTree() : root(nullptr) {}

    void insert(int leftserial,int rightserial) {
        Node* newNode = new Node(leftserial,rightserial);

        if (root == nullptr) {
            root = newNode;
            quantity++;
            root->depth=1;
            return;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if (current->leftserial == quantity) {
                current->left = newNode;
                newNode->depth = current->depth+1;
                quantity++;
                return;
            } else {
                if(current->left) q.push(current->left);
            }

            if (current->rightserial == quantity) {
                current->right = newNode;
                newNode->depth = current->depth+1;
                quantity++;
                return;
            } else {
                if(current->right ) q.push(current->right);
            }
        }
    }
    void levelOrderTraversal() {
        if (root == nullptr) {
            return;
        }

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            Node* current = q.front();
            q.pop();

            if(current->depth>maxdepth) maxdepth=current->depth;
            //cout<<current->depth<<" ";

            if (current->left) {
                q.push(current->left);
            }

            if (current->right) {
                q.push(current->right);
            }
        }
        //cout<<endl;
    }

};

int main()
{
    int n;cin>>n;
    BinaryTree a;
    for(int i=1;i<=n;i++)
    {
        int l,r;
        cin>>l>>r;
        a.insert(l,r);
    }
    a.levelOrderTraversal();
    cout<<maxdepth;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值