美文网首页19-23年 学习笔记
【 数据结构 & 算法 】—— 二分查找、二叉查找树

【 数据结构 & 算法 】—— 二分查找、二叉查找树

作者: Du1in9 | 来源:发表于2021-02-23 07:37 被阅读0次

    思维导图

    预备知识:二分查找基础知识

    已知一个排序数组A,如 A=[-1,2,5,20,90,100,207,800]
    另外一个乱序数组B,如 B=[50,90,3,-1,207,8]
    求B中的任意某个元素,是否在A中出现,结果存储在数组C中,出现用1代表,未出现用0代表,如 C=[0,1,0,1,1,0]

    表中元素升序排列,将表中间位置的关键字与查找关键字比较:
    1、如果两者相等,则查找成功;
    2、否则利用中间位置将表分成前、后两个子表:
    ① 如果中间位置的关键字大于查找关键字,则进一步查找前一子表
    ② 否则进一步查找后一子表
    重复以上过程,直到查找成功,或子表不存在为止。

    • 二分查找(递归实现)
    #include <vector>
    
    bool binary_search(std::veator<int>&sort_array,
                    int begin,int end,int target){
        if begin > end
            return false; 
        int mid=(begin + end)/2; 
        if(target == sort_array[mid])
            return true;
        else if (target < sort_array[mid])
            return binary_search(sort_array,begin,mid-1,target);
        else if (target > sort_array[mid])
            return binary_search(sort_array,mid+1,end,target);
    }
    
    • 二分查找(循环实现)
    bool binary_search (std:: vector<int> & sort_array, int target){
        int begin =0; 
        int end = sort_array.size()-1; 
        while(begin <= end){
            int mid = (begin + end) /2; 
            if (target == sort_array [mid])
                return true;
            else if (target < sort_array [mid]) 
                end = mid-1; 
            else if (target > sort_array [mid])
                begin = mid +1; 
        }
        return false;
    }
    

    例1:插入位置(★)(二分查找)

    给定一个排序数组nums(无重复元素)与目标值target,如果target在nums里出现,则返回下标;如果target在nums里未出现,则返回应该插入的下标,使得数组仍有序。


    • LeetCode 35.Search Insert Position
    #include <stdio.h>
    #include <vector>
    
    class Solution {
    public:
        int searchInsert(std::vector<int>& nums, int target) {
            int index = -1,begin = 0,end = nums.size() - 1;
            while (index == -1){
                int mid = (begin + end) / 2;
                if (target == nums[mid]){
                    index = mid;
                }
                else if (target < nums[mid]){
                    if (mid == 0 || target > nums[mid - 1]){ //数组中没有target 
                        index = mid;
                    }
                    end = mid - 1;
                }
                else if (target > nums[mid]){
                    if (mid == nums.size() - 1 || target < nums[mid + 1]){ //数组中没有target 
                        index = mid + 1;
                    }
                    begin = mid + 1;
                }
            }
            return index;
        }
    };
    
    int main(){
        int test[] = {1, 3, 5, 6};
        std::vector<int> nums;
        Solution solve;
        for (int i = 0; i < 4; i++){
            nums.push_back(test[i]);
        }
        for (int i = 0; i < 8; i++){
            printf("i = %d index = %d\n", i, solve.searchInsert(nums, i));
        }
        return 0;
    }
    

    例2:区间查找(★★)(二分查找)

    给定一个排序数组nums(有重复元素)与目标值target,如果target在nums里出现,则返回所在区间的左右端点下标;如果target在nums里未出现,则返回 [-1,-1]。


    • LeetCode 34.Search for a Range
    #include <stdio.h>
    #include <vector>
    
    int left_bound(std::vector<int>& nums, int target){
        int begin = 0;
        int end = nums.size() - 1;
        while(begin <= end){
            int mid = (begin + end) / 2;
            if (target == nums[mid]){
                if (mid == 0 || nums[mid -1] < target){ //target未在数组中 
                    return mid;  
                }
                end = mid - 1;
            }
            else if (target < nums[mid]){ //左边 
                end = mid - 1;
            }
            else if (target > nums[mid]){ //右边 
                begin = mid + 1;
            }
        }
        return -1;
    }
    
    int right_bound(std::vector<int>& nums, int target){
        int begin = 0;
        int end = nums.size() - 1;
        while(begin <= end){
            int mid = (begin + end) / 2;
            if (target == nums[mid]){
                if (mid == nums.size() - 1 || nums[mid + 1] > target){ //target未在数组中 
                    return mid;
                }
                begin = mid + 1;
            }
            else if (target < nums[mid]){ //左边 
                end = mid - 1;
            }
            else if (target > nums[mid]){ //右边 
                begin = mid + 1;
            }
        }
        return -1;
    }
    
    class Solution {
    public:
        std::vector<int> searchRange(std::vector<int>& nums, int target) {
            std::vector<int> result;
            result.push_back(left_bound(nums, target));
            result.push_back(right_bound(nums, target));
            return result;
        }
    };
    
    int main(){
        int test[] = {5, 7, 7, 8, 8, 8, 8, 10};
        std::vector<int> nums;
        Solution solve;
        for (int i = 0; i < 8; i++){
            nums.push_back(test[i]);
        }
        for (int i = 0; i < 12; i++){
            std::vector<int> result = solve.searchRange(nums, i);
            printf("%d : [%d, %d]\n",i , result[0], result[1]);
        }
        return 0;
    }
    

    例3:旋转数组查找(★★)(二分查找)

    给定一个排序数组nums(无重复元素),且nums可能以某个未知下标旋转。给定目标值target,求target是否在nums中出现,若出现返回所在下标,未出现返回-1。


    • LeetCode 33.Search in Rotated Sorted Array
    #include <stdio.h>
    #include <vector>
    
    class Solution {
    public:
        int search(std::vector<int>& nums, int target) {
            int begin = 0;
            int end = nums.size() - 1;
            while(begin <= end){
                int mid = (begin + end) / 2;
                if (target == nums[mid]){ //1、刚好在中间 
                    return mid;
                }
                else if (target < nums[mid]){ //2、小于中间元素 
                    if (nums[begin] < nums[mid]){ //如:[9,12,15,20,1,3,6,7] 
                        if (target >= nums[begin]){ //target在左边元素区间 
                            end = mid - 1;
                        }
                        else{ //target在右边元素区间 
                            begin = mid + 1;
                        }
                    }
                    else if (nums[begin] > nums[mid]){ //如:[20,1,3,6,7,9,12,15] 
                        end = mid -1;
                    }
                    else if (nums[begin] == nums[mid]){ //如:[3,1] 
                        begin = mid + 1;
                    }
                }
                else if (target > nums[mid]){ //3、同理 
                    if (nums[begin] < nums[mid]){
                        begin = mid + 1;
                    }
                    else if (nums[begin] > nums[mid]){
                        if (target >= nums[begin]){
                            end = mid - 1;
                        }
                        else{
                            begin = mid + 1;
                        }
                    }
                    else if (nums[begin] == nums[mid]){
                        begin = mid + 1;
                    }
                }
            }
            return -1;
        }
    };
    
    int main(){
        int test[] = {9, 12, 15, 20, 1, 3, 6, 7}; //旋转后的数组 
        std::vector<int> nums;
        Solution solve;
        for (int i = 0; i < 8; i++){
            nums.push_back(test[i]);
        }
        for (int i = 0; i < 22; i++){
            printf("%d : %d\n", i, solve.search(nums, i));
        }
        return 0;
    }
    

    预备知识:二叉查找树基础知识

    1)二叉查找树(Binary Search Tree),具有下列性质:
    1、若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
    2、若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
    3、左、右子树也分别为二叉排序树。
    4、等于的情况只能出现在左子树或右子树中的某一侧。


    2)二叉查找树插入节点
    • 二叉查找树的插入
    #include <stdio.h>
    #include <vector>
    
    struct TreeNode {
        int val;
        TreeNode *left;
        TreeNode *right;
        TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    };
    
    void BST_insert(TreeNode *node, TreeNode *insert_node){
        if (insert_node->val < node->val){ //1、小于当前节点 
            if (node->left){ //左孩子非空 
                BST_insert(node->left, insert_node);
            }
            else{ //左孩子空,直接插入 
                node->left = insert_node;
            }
        }
        else{ //2、同理
            if (node->right){
                BST_insert(node->right, insert_node);
            }
            else{
                node->right = insert_node;
            }
        }
    }
    
    void preorder_print(TreeNode *node,int layer){ //前序遍历 
        if (!node){
            return;
        }
        printf("[%d]\n", node->val);
        preorder_print(node->left, layer + 1);
        preorder_print(node->right, layer + 1);
    }
    
    int main(){
        TreeNode root(8);
        std::vector<TreeNode *> node_vec;
        int test[] = {3, 10, 1, 6, 15};
        for (int i = 0; i < 5; i++){ //构建链表 
            node_vec.push_back(new TreeNode(test[i]));
        }
        for (int i = 0; i < node_vec.size(); i++){ //构建二叉查找树 
            BST_insert(&root, node_vec[i]);
        }
        preorder_print(&root, 0);
        return 0;
    }
    
    • 二叉查找树的查找
    #include <stdio.h>
    #include <vector>
    
    struct TreeNode {
        int val;
        TreeNode *left;
        TreeNode *right;
        TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    };
    
    bool BST_search(TreeNode *node, int value){
        if (node->val == value){ //找到 
            return true;
        }
        if (value < node->val){ //小于该节点元素 
            if (node->left){ //左孩子非空,继续查找 
                return BST_search(node->left, value);
            }
            else{ //未找到 
                return false;
            }
        }
        else{ //大于该节点元素 
            if (node->right){ //右孩子非空,继续查找 
                return BST_search(node->right, value);
            }
            else{ //未找到 
                return false;
            }
        }
    }
    
    int main(){
        TreeNode a(8);
        TreeNode b(3);
        TreeNode c(10);
        TreeNode d(1);
        TreeNode e(6);
        TreeNode f(15);
        a.left = &b;
        a.right = &c;
        b.left = &d;
        b.right = &e;
        c.right = &f;
        for (int i = 0; i < 20; i++){ //查找元素 0~19
            if (BST_search(&a, i)){
                printf("%d (√)\n", i);
            }
            else{
                printf("%d (×)\n", i);
            }
        }
        return 0;
    }
    

    例4:二叉查找树编码与解码(★★)

    给定一个二叉查找树,实现对该二叉查找树编码与解码功能。编码即将该二叉查找树转为字符串,解码即将字符串转为二叉查找树。不限制使用何种编码算法。



    1、编码



    2、解码
    • LeetCode 449.Serialize and Deserialize BST
    #include <stdio.h>
    #include <string>
    #include <vector>
    
    struct TreeNode {
        int val;
        TreeNode *left;
        TreeNode *right;
        TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    };
    
    void BST_insert(TreeNode *node, TreeNode *insert_node){
        if (insert_node->val < node->val){
            if (node->left){
                BST_insert(node->left, insert_node);
            }
            else{
                node->left = insert_node;
            }
        }
        else{
            if (node->right){
                BST_insert(node->right, insert_node);
            }
            else{
                node->right = insert_node;
            }
        }
    }
    
    void change_int_to_string(int val, std::string &str_val){
        std::string tmp;
        while(val){
            tmp += val % 10 + '0';
            val /= 10;
        }
        for (int i = tmp.length()-1; i>=0; i--){
            str_val += tmp[i];
        }
        str_val += '#';
    }
    
    void BST_preorder(TreeNode *node, std::string &data){ //前序遍历并转字符 
        if (!node){
            return;
        }
        std::string str_val;
        change_int_to_string(node->val, str_val); //整型转字符 
        data += str_val;
        BST_preorder(node->left, data);
        BST_preorder(node->right, data);
    }
    
    class Codec {
    public:
        std::string serialize(TreeNode* root) {
            std::string data;
            BST_preorder(root, data);
            return data;
        }
        TreeNode *deserialize(std::string data) {
            if (data.length() == 0){
                return NULL;
            }
            std::vector<TreeNode *> node_vec;
            int val = 0;
            for (int i = 0; i < data.length(); i++){
                if (data[i] == '#'){
                    node_vec.push_back(new TreeNode(val));
                    val = 0;
                }
                else{
                    val = val * 10 + data[i] - '0';
                }
            }
            for (int i = 1; i < node_vec.size(); i++){
                BST_insert(node_vec[0], node_vec[i]);
            }
            return node_vec[0];
        }
    };
    
    void preorder_print(TreeNode *node,int layer){ //前序遍历并输出 
        if (!node){
            return;
        }
        printf("[%d] ", node->val);
        preorder_print(node->left, layer + 1);
        preorder_print(node->right, layer + 1);
    }
    
    int main(){
        TreeNode a(8);
        TreeNode b(3);
        TreeNode c(10);
        TreeNode d(1);
        TreeNode e(6);
        TreeNode f(15); 
        a.left = &b;
        a.right = &c;
        b.left = &d;
        b.right = &e;
        c.left = &f;    
        Codec solve;    
        std::string data = solve.serialize(&a); //编码 
        printf("编码后:%s\n", data.c_str());
        TreeNode *root = solve.deserialize(data); //解码 
        printf("解码后:");
        preorder_print(root, 0); //前序遍历 
        return 0;
    }
    

    例5:逆序数(★★★)(二叉查找树)

    已知数组nums,求新数组count,count[i] 代表了 nums[i] 右侧比其小的元素个数。


    • LeetCode 315.Count of Smaller Numbers After Self
    #include <stdio.h>
    #include <vector>
    
    
    struct BSTNode {  
        int val;
        int count; //记录左子树节点个数,为了计算 count_small 
        BSTNode *left;
        BSTNode *right;
        BSTNode(int x) : val(x), left(NULL), right(NULL), count(0) {}
    };
    
    void BST_insert(BSTNode *node, BSTNode *insert_node, int &count_small){ 
        if (insert_node->val <= node->val){ //插入节点小于当前节点 
            node->count++; //当前结点的 count
            if (node->left){ 
                BST_insert(node->left, insert_node, count_small);
            }
            else{ 
                node->left = insert_node;
            }
        }
        else{ //插入结点大于当前节点 
            count_small += node->count + 1; //插入结点的 count_small 
            if (node->right){
                BST_insert(node->right, insert_node, count_small);
            }
            else{
                node->right = insert_node;
            }
        }
    }
    
    class Solution {
    public:
        std::vector<int> countSmaller(std::vector<int>& nums) {
            std::vector<int> result;
            std::vector<BSTNode *> node_vec;
            std::vector<int> count; //存储比当前元素小的个数 
            for (int i = nums.size() - 1; i >= 0; i--){ //1、逆向创建链表 [1,-2,5,3,1,9,-7,5] 
                node_vec.push_back(new BSTNode(nums[i]));
            }
            count.push_back(0); //第一个节点count=0
            for (int i = 1; i < node_vec.size(); i++){ //构建 
                int count_small = 0; //临时变量 
                BST_insert(node_vec[0], node_vec[i], count_small); //得到插入结点的 count_small 
                count.push_back(count_small); //
            }
            for (int i = node_vec.size() - 1; i >= 0; i--){ //2、count逆向存入result 
                delete node_vec[i]; //顺便释放链表内存 
                result.push_back(count[i]);
            }
            return result;
        }
    };
    
    int main(){
        int test[] = {5, -7, 9, 1, 3, 5, -2, 1};
        std::vector<int> nums;
        for (int i = 0; i < 8; i++){
            nums.push_back(test[i]);
        }
        Solution solve;
        std::vector<int> result = solve.countSmaller(nums);
        for (int i = 0; i < result.size(); i++){
            printf("%d(%d)\n", test[i],result[i]);
        }
        printf("\n");
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:【 数据结构 & 算法 】—— 二分查找、二叉查找树

        本文链接:https://www.haomeiwen.com/subject/iagkfltx.html