19-23年 学习笔记

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

2021-02-23  本文已影响0人  Du1in9

思维导图

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

已知一个排序数组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里未出现,则返回应该插入的下标,使得数组仍有序。


#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]。


#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。


#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、解码
#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] 右侧比其小的元素个数。


#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;
}
上一篇下一篇

猜你喜欢

热点阅读