算法基础 典型题(六)二分搜索 与 二分查找树

记录算法基础题思路:

step1

插入位置查找: https://leetcode-cn.com/problems/search-insert-position/submissions/

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例:
输入: [1,3,5,6], 5
输出: 2

    int searchInsert(vector& nums, int target) {
        /* 二分查找方法改造, 适用不存在场景 */
        /* 1 左插入场景:假设target < nums[mid] 但 > nums[mid - 1](边界0), 在mid插入 */
        /* 2 右插入场景:假设target > nums[mid] 但 < nums[mid + 1](边界size - 1), 在mid + 1 插入 */      
        int begin = 0;
        int end = nums.size() - 1;
        while (begin <= end) {
            int mid = (end + begin) / 2;
            if (target == nums[mid]) {
                return mid;
            } else if (target < nums[mid]) {
                if ((mid == 0) || target > nums[mid - 1]) {
                    return mid;
                }
                end = mid - 1;
            } else {
                if ((mid == nums.size() - 1) || target < nums[mid + 1]) {
                    return mid + 1;
                }
                begin = mid + 1;
            }
        }
        return 0; /* 实际不会进来 */
    }

区间查找:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/submissions/

给定一个按照升序排列的整数数组 nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

public:
    vector searchRange(vector& nums, int target) {
        /* 基于二分查找做区间改造,做二次查找分别找左右端点 */
        /* target == nums[mid] && target > nums[mid - 1] 左端点, 边界0*/
        /* target == nums[mid] && target < nums[mid + 1] 左端点, 边界size -1 */
        vector res;
        res.push_back(find_left_pos(nums, target));
        res.push_back(find_right_pos(nums, target));
        return res;
    }
private:
    int find_left_pos(vector& nums, int target) {
        int begin = 0;
        int end = nums.size() - 1;
        while (begin <= end) {
            int mid = (end + begin) / 2;
            if (target == nums[mid]) {
                if (mid == 0 || nums[mid - 1] < 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 find_right_pos(vector& nums, int target) {
        int begin = 0;
        int end = nums.size() - 1;
        while (begin <= end) {
            int mid = (end + begin) / 2;
            if (target == nums[mid]) {
                if ((mid == nums.size() - 1) || nums[mid + 1] > target) {
                    return mid;
                }
                begin = mid + 1;
            } else if (target < nums[mid]) {
                end = mid - 1;
            } else if (target > nums[mid]) {
                begin = mid + 1;
            }
        }
        return -1;
    }

旋转数组查找:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/submissions/

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

    int search(vector& nums, int target) {
        /* 二分查找改进补充落在旋转区间方式, 如nums[begin]>nums[end]为旋转区间,否则为递增区间*/
        int begin = 0;
        int end = nums.size() - 1;
        while (begin <= end) {
            int mid = (end + begin) / 2;
            if (target == nums[mid]) {
                return mid;
            } else if (target < nums[mid]) {
                /* 如果begin到mid为递增区间, mid到end为旋转区间*/
                if (nums[begin] < nums[mid]) {
                    if (target >= nums[begin]) { // 递增区间有
                        //end = mid - 1; //递增区间有(优化为下面一句, 避免反复空间判断)
                        return normal_binary_search(nums, begin, mid - 1, target);
                    } else { //递增区间没有,在旋转区间找 
                        begin = mid + 1;
                    }
                /* 如果begin到mid为旋转区间,mid到end为递增区间 */
                } else if (nums[begin] > nums[mid]) {
                    end = mid - 1; // 递增区间没,在旋转区间找
                } else { //nums[begin] == nums[mid],mid和begin值相同
                    begin = mid + 1;
                }
            } else if (target > nums[mid]) {
                /* 如果begin到mid为递增区间, mid到end为旋转区间*/
                if (nums[begin] < nums[mid]) {
                    begin = mid + 1; //递增区间没有,在旋转区间找 
                /* 如果begin到mid为旋转区间,mid到end为递增区间 */
                } else if (nums[begin] > nums[mid]) {
                    if (target <= nums[end]) {
                        //begin = mid + 1; //递增区间有(优化为下面一句, 避免反复空间判断)
                        return normal_binary_search(nums, mid + 1, end, target);
                    } else {
                        end = mid - 1; // 递增区间没有,在旋转区间找
                    }
                } else { //nums[begin] == nums[mid],mid和begin值相同
                    begin = mid + 1;
                }
            }
        }
        return -1;
    }

二叉查找树编码解码:https://leetcode-cn.com/problems/serialize-and-deserialize-bst/submissions/

序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。

public:
    // Encodes a tree to a single string.
    /* 对查找二叉树进行前序遍历,将遍历到的结果按顺序插入能得到相同的查找二叉树 */
    string serialize(TreeNode* root) {
        string res;
        bst_preorder(root, res);
        return res;
    }
    void bst_preorder(TreeNode* root, string &res) {
        if (root == NULL) {
            return;
        }
        int val = root->val;
        /* 前序遍历,注意把字符串反序,取值反算得到val更简单, 用#分割数据 */
        string tmp;
        while (val != 0) {
            tmp += val % 10 + '0';
            val /= 10;
        }
        for (int i = tmp.size() - 1; i >= 0; i--) {
            res += tmp[i];
        }
        res += '#'; 
        bst_preorder(root->left, res);
        bst_preorder(root->right, res);        
    }
    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if (data.size() == 0) {
            return NULL;
        }
        vector  node_vec;
        int val = 0;
        for (int i = 0; i < data.size(); 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 bst_insert(TreeNode* root, TreeNode* node) {
        if (node->val < root->val) {
            if (root->left == NULL) {
                root->left = node;
            } else {
                bst_insert(root->left, node);
            }
         } else {
            if (root->right == NULL) {
                root->right = node;
            } else {
                bst_insert(root->right, node);
            }
         }
     }

逆序数:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/submissions/

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0] 

    struct BstNode {
        int val;
        int count; //左子数中节点个数
        BstNode *left;
        BstNode *right;
        BstNode(int x) : val(x), left(NULL), right(NULL), count(0) {}
    };
    vector countSmaller(vector& nums) {
        /* 利用查找二叉树数据结构,逆序插入数组nums[i], 插入过程生成节点的count[i]  
            每个节点加入一个count来记录其左子树节点的个数,插入过程如果插入左子树就更新count,
            如果插入右子树,count[i] += node->count + 1(当前节点左子树数量+1); */
        vector  count;
        vector  res;
        if (nums.size() == 0) {
            return res;
        }
        BstNode *root = new BstNode(nums[nums.size() - 1]);
        count.push_back(0);
        for (int i = nums.size() - 2; i >= 0; i--) {
            int small_count = 0;
            bst_insert(root, new BstNode(nums[i]), small_count);
            count.push_back(small_count);
        }
        for (int i = count.size() - 1; i >= 0; i--) {
            res.push_back(count[i]);
        }
        return res;    
    }
private:
     void bst_insert(BstNode* root, BstNode* node, int &small_count) {
        if (node->val <= root->val) {
            root->count++;//往左子树插入,更新count
            if (root->left == NULL) {
                root->left = node;
            } else {
                bst_insert(root->left, node, small_count);
            }
         } else {
            small_count += root->count + 1;//往右子树插入,更新统计small_count
            if (root->right == NULL) {
                root->right = node;
            } else {
                bst_insert(root->right, node, small_count);
            }
         }
     }

算法基础 典型题(六)二分搜索 与 二分查找树_第1张图片

 

你可能感兴趣的:(数据结构与算法)