首先我们先来复习一下二分查找的算法 对于正向序列的二分查找
递归实现:
1 bool binary_search(vector<int> &sort_arry,int begin,int end,int target) 2 { 3 if(begin>end) 4 return false; 5 int mid = (begin + end) / 2; 6 if(sort_arry[mid] == target) 7 return true; 8 else if(sort_arry[mid] > target) 9 { 10 return binary_search(sort_arry,begin,mid-1,target); 11 } 12 else 13 { 14 return binary_search(sort_arry,mid+1,end,target); 15 } 16 }
循环实现:
1 bool binary_search(vector<int> &sort_arry,int target) 2 { 3 int begin = 0; 4 int end = sort_arry.size() - 1; 5 int mid = (begin + end) / 2; 6 while(begin <= end) 7 { 8 if(sort_arry[mid] == target) 9 return true; 10 else if(sort_arry[mid] > target) 11 { 12 end = mid - 1; 13 } 14 else 15 { 16 begin = mid + 1; 17 } 18 mid = (begin + end) / 2; 19 } 20 return false; 21 }
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-insert-position
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution { public: int searchInsert(vector<int>& nums, int target) { int index = -1; int begin =0; int end = nums.size(); 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]) index = mid; end = mid - 1; } else if(target > nums[mid]) { if(mid == nums.size()-1 || target < nums[mid+1]) return mid + 1; begin = mid + 1; } } return index; } };
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
int left_bond(vector<int> &nums,int target) { int begin; int end = nums.size()-1; while(begin <= end) { int mid = (begin + end)/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 { begin = mid + 1; } } return -1; } int right_bond(vector<int> &nums,int target) { int begin; 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) { return mid; } begin = mid + 1; } else if(target < nums[mid]) { end = mid - 1; } else { begin = mid + 1; } } return -1; } class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { vector<int> result; result.push_back(left_bond(nums,target)); result.push_back(right_bond(nums,target)); return result; } };
旋转数组:
33. 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:
·如果target < nums[mid]的情况:
1:如果nums[begin] < nums[end]:
有两种可能 要么没转过 要么 转过(nums[begin] >nums[end])
说明此时递增区间为begin~mid-1 旋转区间为mid+1~end
比如是[7 9 12 15 20 1 3 6]的target =12
因为12 >begin 所以就在递增区间找
否则如果target = 3 < begin 应该在旋转区间中查找
2:如果begin > mid 说明旋转区间在begin到mid-1之间,递增区间在mid+1到end之间
比如[20 1 3 6 7 8 12 15]
target = 3 (< mid) 说明递增区间已经不可能了,直接在旋转区间begin到mid-1中查找
3:如果begin == mid 说明目标只能在mid+1到end之间查找 比如target = 1的时候数组是[6,1]
·如果目标target > nums[mid]的情况:
1:当nums[begin] < nums[mid],说明递增区间是[begin,mid-1]旋转区间为[mid+1,end]
比如数组[7 9 12 15 20 1 3 6]
递增区间7~15旋转区间在20~6
因为target已经大于mid处的值了,因此直接在旋转区间查找。
例如查找target=20就在20~6里面找
2:如果是nums[begin] > nums[mid]的情况 (有可能存在begin>end)
说明递增区间在后半部分mid+1~end旋转区间在前半部分begin~mid-1
比如15 20 1 3 6 7 9 12
如果target=20>begin 那就在旋转区间begin~mid-1里找 ,否则在递增区间mid+1~end里找
3:如果是begin == mid的情况那么也只可能在mid+1~end之间
比如target = 7 数组是[6 7]
class Solution { public: int search(vector<int>& nums, int target) { int begin =0; int end = nums.size() - 1; if(nums.size()==1) { if(nums[0]==target) return 0; else return -1; } while(begin <= end)//递增区间在前半部分 { int mid = (begin + end) / 2; if(target == nums[mid]) { return mid; } else if(target < nums[mid]) { if(nums[begin] < nums[mid]) { if(target >= nums[begin])//说明应递增区间为begin~mid-1之间 旋转区间为mid+1~end之间 { end = mid -1; } else { begin = mid + 1; } } else if(nums[begin] > nums[mid])//说明递增区间在后面mid+1~end { end = mid -1; } else if(nums[begin] == nums[mid])//说明目标只可能在mid+1~end之间 { begin = mid + 1; } } else if(target > nums[mid]) { if(nums[begin] < nums[mid])//说明递增区间是begin~mid-1,旋转区间在后半部分 { begin = mid + 1; } else if(nums[begin] > nums[mid]) { if(target >= nums[begin])//说明递增区间在mid+1到end 旋转区间在前半部分。 { end = mid - 1; } else { begin = mid + 1; } } else if(nums[begin] == nums[mid])//说明只能在mid+1~end之间 { begin = mid + 1; } } } return -1; } };
这题的边界真的很恶心…
接下来介绍一个数据结构 二叉查找树
二叉查找树是具有以下性质的二叉树:
1:若左子树不为空,则左子树上所有节点的值均小于或等于他的根节点
2:若右子树不为空,则右子树上所有节点的值均大于或等于他的根节点
3:二叉查找树的左右子树也是二叉查找树
4:等于的情况只能出现在左子树或右子树的某一侧(不能同时出现)
由于二叉查找树的中序遍历是从小到大的,所以称之为二叉查找树。
二叉查找树的插入操作:
if insert_node节点的值小于当前node的节点值
{
if (node有左子树) 则递归的将该节点插入到左子树为根的二叉排序树中,
else (将node的left赋值为该节点的地址
}
else//(大于等于的情况下)
{
if(node有右子树)则递归的将该节点插入到右子树为根的二叉排序树中
else (将node->right赋值为该节点的地址)
}
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; } } }
二叉树的查找:
查找数组val是否在二叉树中出现:
如果val == 当前节点的值返回真,
如果val < 当前节点的值
{
如果当前节点有左子树,继续左子树中查找该值,否则返回false;
否则{
如果当前节点有右子树,就继续在右子树中查找,否则返回false;
}
}
void BST_search(TreeNode*node,int val) { if(node->val == val) return true; if(val < node->val) { if(node->left) { BST_search(node,val); } else { return false; } } else { if(node->right) { BST_search(node,val); } else { return false; } } }
449. 序列化和反序列化二叉搜索树
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
注意:不要使用类成员/全局/静态变量来存储状态。 你的序列化和反序列化算法应该是无状态的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/serialize-and-deserialize-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Codec { public: 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 val) { if(node->val == val) return true; if(val < node->val) { if(node->left) { BST_search(node,val); } else { return false; } } else { if(node->right) { BST_search(node,val); } else { return false; } } } void change_int_to_str(int val, string &str) { string temp; while(val) { temp += (val % 10) +'0'; val = val / 10; } for(int i = temp.length() - 1;i >= 0;i--)//逆序将字符串插入到str中 { str += temp[i]; } str += '#';//转换完以后用#分割 } void BST_preorder(TreeNode *node,string &data) { if(!node) { return; } string str;//前序遍历把每次val添加到str中。 change_int_to_str(node->val,str); data += str;//每次转换就添加,然后继续递归遍历 BST_preorder(node->left,data); BST_preorder(node->right,data); } // Encodes a tree to a single string. string serialize(TreeNode* root) { string data; BST_preorder(root,data); return data; } // Decodes your encoded data to tree. TreeNode* deserialize(string data) { if(data.length() == 0) { return NULL; } vectornode_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]; } }; // Your Codec object will be instantiated and called as such: // Codec codec; // codec.deserialize(codec.serialize(root));
315. 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析:
将元素安装原数组逆置后的顺序插入到二叉查找树中,设置变量count_small = 0记录在插入过程中,有多少个元素比插入节点值小,
若待插入的节点值<=node->val,那么node->count++,并且递归的将该节点插入到当前节点的左子树。
若当待插入节点值大于当前节点的node的值,count_small+=node->count+1(当前节点左子树量+1)
递归将该节点插入到当前节点的右子树。
struct BSTNode { int val; int icount; BSTNode* left; BSTNode* right; BSTNode(int val) { val = val; left = NULL; right = NULL; icount = 0; } }; class Solution { public: void BST_insert(BSTNode *node,BSTNode *insert_node,int &count_small) { if(insert_node->val <= node->val) { node->icount++; if(node->left) { BST_insert(node->left,insert_node,count_small); } else { node->left = insert_node; } } else { count_small += node->icount + 1; if(node->right) { BST_insert(node->right,insert_node,count_small); } else { node->right = insert_node; } } } vector<int> countSmaller(vector<int>& nums) { vector <int> result;//最终逆序数组 vectornode_vec;//创建二叉查找树节点池 vector<int> icount;//从后插入过程中比当前节点小的count_small数组。 for(int i = nums.size() - 1;i >= 0;i--) { node_vec.push_back(new BSTNode(nums[i])); } icount.push_back(0);//第一个节点count_small = 0 for(int i = 1; i < node_vec.size(); i++) { int count_small = 0; BST_insert(node_vec[0],node_vec[i],count_small); icount.push_back(count_small); } for(int i = node_vec.size() - 1; i >= 0; i--)//将countsmall倒push到result数组中 { delete node_vec[i]; result.push_back(icount[i]); } return result; } };