二分搜索与二叉排序树
文章目录
二分搜索基础知识
适用于有序数组
二分查找(递归)
bool binary_search(vector<int> &sort_array, int begin, int end, int target){
if (begin > end) return false;
int mid = begin + (end - begin)/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(vector<int> &sort_array, int target){
int begin = 0;
int end = sort_array.size() - 1;
while (begin <= end){
int mid = begin + (end - begin)/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;
}
//或者
bool binary_search(vector<int> &sort_array, int target){
int begin = 0;
int end = sort_array.size();
while (begin < end){
int mid = begin + (end - begin)/2;
if (target == sort_array[mid]) return true;
else if (target < sort_array[mid])
end = mid;
else if (target > sort_array[mid])
begin = mid + 1;
}
return false;
}
代码细究:
二分循环一般有两种写法,左闭右闭区间 和 左闭右开区间,如上;很多同学在写二分时一下就陷入了死循环,想解决这个问题,我们需要细究以下问题:
问题1:int mid = begin + (end - begin)/2;
int mid = begin + (end - begin)/2 :这么写主要是为了防止溢出,如果你确保不会溢出的话,可以用直接写为(begin+end)/2的方法。
当这么写时,如果begin与end间数量为偶数时,mid更偏向begin,比如begin=1,end=2 => mid=1
问题2:左闭右闭时 begin <= end;
之所以要用=,是因为begin=end时仍然需要判断(有点像废话),同样来看,begin=1,end=2 => mid=1,这里我们一般会判断mid=1时条件是否成立,如果不成立:1)end=mid-1,退出;2)begin=mid+1,这时我们发现如果此时退出的话,mid=2 这个条件就不会被判断了,但是事实上,mid=2这个条件是没有被判断过的。这样我们就发现了=的意义。
问题3:begin=mid+1、end=mid-1;
其实这个就比较简单了,因为我们进循环的第一步就是判断mid时是否成立,因为mid点已经判断过,所以就没有必要再继续加入探索序列。
左闭右开与之相似,这里留给读者自己。
二叉排序树基础知识
具有二叉树性质,且具有以下性质:
若左子树非空,则左子树上所有节点的值均小于或等于根节点值
若右子树非空,则右子树上所有节点的值均大于根节点值(等号也可置于右子树,但只能在一侧)
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;
}
}
}
二叉排序树查找数值
bool BST_search (TreeNode* node, int value){
if (node->val == value) return true;
if (node->val > value) {
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;
}
}
1.插入位置(easy)
题目说明:
给定一个无重复元素有序数组,判断target是否在nums中,是则返回元素下标;否则返回应该插入的位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int begin = 0;
int end = nums.size() - 1;
while (begin <= end){
int mid = begin + (end - begin)/2;
if (target == nums[mid]) return mid;
else if (target < nums[mid]){
end = mid - 1;
}
else if (target > nums[mid]){
begin = mid + 1;
}
}
if (begin < nums.size() && nums[begin] < target)
return begin + 1;
else
return begin;
}
};
2.区间查找(medium)
题目说明:
给定一个有重复数字排序数组,判断target是否在nums中,是则返回左右端点下标;否则返回[-1,-1]
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
return vector<int>{left_bound(nums, target), right_bound(nums, target)};
}
int left_bound(vector<int>& nums, int target){
int begin = 0;
int end = nums.size() - 1;
while (begin <= end){
int mid = begin + (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 right_bound(vector<int>& nums, int target){
int begin = 0;
int end = nums.size() - 1;
while (begin <= end){
int mid = begin + (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;
}
};
3.旋转数组查找(medium)
题目说明:
给定一个无重复元素排序数组nums,且nums可能以某个未知下标旋转,给定target,判断在nums中,是则返回下标;否则返回-1
mid将数组分为两部分,必然有一部分是有序的,判断target是否在有序数组内
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
if(n < 2)
return (target == nums[0]) - 1;
int first = 0, end = n - 1;
while(first <= end){
int mid = (first + end) / 2;
if(nums[mid] == target){
return mid;
}
if(nums[mid] >= nums[0]){ //左边是有序的
if(target >= nums[first] && target < nums[mid])
end = mid - 1;
else
first = mid + 1;
}
else{
if(target <= nums[end] && target > nums[mid])
first = mid + 1;
else
end = mid - 1;
}
}
return -1;
}
};
4.二叉排序树编码与解码(medium)
题目说明:
给定一个二叉排序树,实现对其编码与解码。编码即将该树转化为字符串,解码即将字符串转化为二叉排序树。
代码解析:
编码:我们使用前序遍历将二叉树中的元素依序加入到字符串中,并以‘#’为分隔符,为了方便后续解码。这里需要将数字转化为字符串,我们使用自己编写的函数。
解码:解码第一步我们要先将字符串中的数字先提取出来,然后再插入到二叉树中(这里为了能够还原,我们前边就必须使用前序遍历,这样根节点是固定的)
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode (int x) : val(x), left(NULL), right(NULL) {}
};
class Codec {
public:
string serialize(TreeNode* root){
string data;
BST_preorder(root, data);
return data;
}
TreeNode* deserialize(string data){
if (data.size() == 0) return NULL;
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];
}
private:
void change_int_to_string(int val, string& str_val){
while (val){
char temp = val % 10 + '0';
str_val = temp + str_val;
val = val / 10;
}
}
void BST_preorder(TreeNode* node, string& data){
if (!node) return;
string str_val;
change_int_to_string(node->val, str_val);
data += str_val + '#';
BST_preorder(node->left, data);
BST_preorder(node->right, data);
}
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;
}
}
}
};
5.逆序数(hard)
题目说明:
已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比nums[i]小的元素个数。
(这道题之前用分治算法做过)
思考:
将元素按照原数组逆置后的顺序插入到二叉排序树中,如何在元素插入时计算已有多少个元素比当前插入元素小?
struct BSTNode {
int val;
int count;
BSTNode* left;
BSTNode* right;
BSTNode (int x) : val(x), count(0), left(NULL), right(NULL) {};
};
class Solution {
public:
vector<int> countSmaller(vector<int>& nums){
vector<int> result;
vector<BSTNode* > node_vec;
vector<int> count;
for (int i = nums.size()-1; i >= 0; i--){
node_vec.push_back(new BSTNode(nums[i]));
}
count.push_back(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.push_back(count_small);
}
for (int i = node_vec.size() - 1; i >= 0; i--){
delete node_vec[i];
result.push_back(count[i]);
}
return result;
}
void BST_insert (BSTNode* node, BSTNode* insert_node, int& count_small){
if (insert_node->val <= node->val){
node->count ++;
if (node->left){
BST_insert(node->left, insert_node, count_small);
}
else{
node->left = insert_node;
}
}
else{
count_small += node->count + 1;
if (node->right){
BST_insert(node->right, insert_node, count_small);
}
else{
node->right = insert_node;
}
}
}
};