分门别类刷leetcode——二分查找与分治算法(C++实现)

目录

基础复习——二分查找

递归实现二分查找(递归和时间复杂度没有关系)

leetcode 35 搜索插入位置

leetcode 34 在排序数组中查找元素的第一个和最后一个位置

leetcode 33 搜索旋转排序数组

基础复习——分治法

归并排序

leetcode 315 计算右侧小于当前元素的个数

leetcode 23 合并K个排序链表


基础复习——二分查找

 

递归实现二分查找(递归和时间复杂度没有关系)

bool binary_search(vector<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(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;
}

 

 

 

leetcode 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

思路:

顺序查找,过了,时间复杂度是O(n),目测面试官不会满意。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if(nums.size()<1) return 0;
        
        for(int i=0; i<nums.size(); i++){
            if(nums[i]>=target)
                return i;
        }
        return nums.size();
    }
};

 

用二分查找吧。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int index=-1;
        int begin=0;
        int 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]){
                    index=mid;
                }
                end=mid-1;
            }else if(target>nums[mid]){
                if(mid==nums.size()-1 || target<nums[mid+1]){
                    index=mid+1;
                }
                begin=mid+1;
            }
        }
        return index;
    }
};

 

 

 

leetcode 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]

思路:

用顺序查找的话,这题不难,二分查找的话,分别找左端点和右端点。写一下二分查找吧。

class Solution {
public:
    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){
                    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)/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;
    }
    
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int>result;
        result.push_back(left_bound(nums, target));
        result.push_back(right_bound(nums, target));
        return result;
    }
};

 

 

 

leetcode 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

思路:

使用二分查找。将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。

因此查找过程分为两种情况

  1. target<nums[mid]
  2. target>nums[mid]

class Solution {
public:
    int search(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])
                return mid;
            //情况一:target<nums[mid]
            else if(target<nums[mid]){
                //说明nums[begin]到nums[mid]为有序递增区间。该区间单纯的二分查找即可
                if(nums[begin]<nums[mid]){
                    if(target>=nums[begin]){
                        end=mid-1;
                    }else{
                        begin=mid+1;
                    }
                //说明nums[mid+1]都numerical[nums.size()-1]为有序区间
                }else if(nums[begin]>nums[mid]){
                    end=mid-1;
                //说明此时数组中只有两个元素[6,1],target的值为1
                }else if(nums[begin]==nums[mid]){
                    begin=mid+1;
                }
            //情况二:target>nums[mid]
            }else if(target>nums[mid]){
                //mid左侧有序
                if(nums[begin]<nums[mid]){
                    begin=mid+1;
                }else if(nums[begin]>nums[mid]){
                    //说明mid到数组末尾为有序递增
                    if(target>=nums[begin]){
                        //说明目标区间在mid左侧,那段无序区间中
                        end=mid-1;
                    }else{
                        begin=mid+1;
                    }
                }else if(nums[begin]==nums[mid]){
                    begin=mid+1;
                }
            }
        }
        return -1;
    }
};

 

 

 

基础复习——分治法

 

归并排序

递归写法

class Solution {
public:
    //归并两个已排序数组
    void merge_sort_two_vec(vector<int>&sub_vec1, vector<int>&sun_vec2,
                            vector<int>&vec){
        int i=0, j=0;
        while(i<sub_vec1.size()&&j<sub_vec2.size()){
            if(sub_vec1[i]<sub_vec2[j]){
                vec.push_back(sub_vec1[i]);
                i++;
            }else{
                vec.push_back(sub_vec2[j]);
                j++;
            }
        }
        for(;i<sub_vec1.size();i++){
            vec.push_back(sub_vec1[i]);
        }
        for(;j<sub_vec2.size();j++){
            vec.push_back(sub_vec2[j]);
        }
    }
    
    void merge_sort(vector<int>&vec){
       if(vec.size()<2) return;
        
        int mid=vec.size()/2;
        vector<int>sub_vec1;
        vector<int>sub_vec2;
        for(int i=0; i<mid; i++){
            sub_vec1.push_back(vec[i]);
        }
        for(int i=mid; i<vec.size();i++){
            sub_vec2.push_back(vec[i]);
        }
        merge_sort(sub_vec1);
        merge_sort(sub_vec2);
        vec.clear();
        merge_sort_two_vec(sub_vec1, sub_vec2, vec);
    }
};

 

 

 

leetcode 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 个更小的元素.

思路:

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<int>count;
        if(nums.empty()) return count;
        
        vector<pair<int, int>>vec;
        //把每个值以及他在数组中的位置作为pair
        for(int i=0; i<nums.size(); i++){
            vec.push_back(make_pair(nums[i],i));
            count.push_back(0);
        }
        merge_sort(vec, count);
        return count;
    }
private:
    //归并
    void merge_sort_two_vec(vector<pair<int, int>>&sub_vec1,
                            vector<pair<int, int>>&sub_vec2,
                            vector<pair<int, int>>&vec,
                            vector<int>&count){
        int i=0, j=0;
        while(i<sub_vec1.size()&&j<sub_vec2.size()){
            if(sub_vec1[i].first<=sub_vec2[j].first){
                count[sub_vec1[i].second]+=j;
                vec.push_back(sub_vec1[i]);
                i++;
            }else{
                vec.push_back(sub_vec2[j]);
                j++;
            }
        }
        for(; i<sub_vec1.size(); i++){
            count[sub_vec1[i].second]+=j;
            vec.push_back(sub_vec1[i]);
        }
        for(; j<sub_vec2.size(); j++){
            vec.push_back(sub_vec2[j]);
        }
    }
    
    //拆分
    void merge_sort(vector<pair<int, int>>&vec, vector<int>&count){
        if(vec.size()<2)
            return;
        int mid=vec.size()/2;
        vector<pair<int, int>>sub_vec1;
        vector<pair<int, int>>sub_vec2;
        for(int i=0; i<mid; i++){
            sub_vec1.push_back(vec[i]);
        }
        for(int i=mid; i<vec.size();i++){
            sub_vec2.push_back(vec[i]);
        }
        merge_sort(sub_vec1,count);
        merge_sort(sub_vec2,count);
        vec.clear();
        merge_sort_two_vec(sub_vec1, sub_vec2, vec, count);
    }
};

 

 

 

leetcode 23 合并K个排序链表

合并 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

方法一:把所有节点都存入数组中,然后对数组进行排序。再将排序好的各个值存入一个新链表中——目测这种方法面试官不喜欢

bool cmp(const ListNode *a, const ListNode *b){
    return a->val<b->val;
}

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        vector<ListNode*>node_vec;
        for(int i=0; i<lists.size();i++){
            ListNode *head=lists[i];
            while(head){
                node_vec.push_back(head);
                head=head->next;
            }
        }
        if(node_vec.size()==0){
            return NULL;
        }
        sort(node_vec.begin(),node_vec.end(), cmp);
        for(int i=1; i<node_vec.size();i++){
            node_vec[i-1]->next=node_vec[i];
        }
        node_vec[node_vec.size()-1]->next=NULL;
        return node_vec[0];
    }
};

 

方法二:分治归并

合并两个单链表的步骤:

  1. 先检查两个链表是否都非空,如果存在空链接,则返回另一个链表的头结点
  2. 然后设置一个空指针,使其指向两个链表中头结点值较小的节点,将该指针作为结果链表的头结点
  3. 之后进入循环体,不断比较,直到有一方的链表到达了尾节点为止
  4. 最后连接剩余的未参与比较的链表段
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        //1
        if((!l2)||(!l1)){ //如果存在空链表,则返回另一个链表的头结点
            if(l1){
                return l1;
            }else{
                return l2;
            }
        }
        
        //2
        ListNode *result=(l1->val>l2->val)?l2:l1;//结果的头结点
        if(result==l1){
            l1=l1->next;
        }else if(result==l2){
            l2=l2->next;
        }
        
        //3
        ListNode *helper=result;
        while(l1&&l2){
            if(l1->val>l2->val){
                helper->next=l2;
                l2=l2->next;
            }else{
                helper->next=l1;
                l1=l1->next;
            }
            helper=helper->next;
        }
        
        //4
        //连接l1或l2的剩余节点
        if(l1){
            helper->next=l1;
        }else if(l2){
            helper->next=l2;
        }        
        return result;
    }

       
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size()==0) return NULL;
        if(lists.size()==1) return lists[0];
        if(lists.size()==2) return mergeTwoLists(lists[0], lists[1]);
        
        int mid=lists.size()/2;
        vector<ListNode*>sub1_lists;
        vector<ListNode*>sub2_lists;
        for(int i=0; i<mid; i++){
            sub1_lists.push_back(lists[i]);
        }
        for(int i=mid; i<lists.size(); i++){
            sub2_lists.push_back(lists[i]);
        }
        ListNode *l1=mergeKLists(sub1_lists);
        ListNode *l2=mergeKLists(sub2_lists);
        return mergeTwoLists(l1, l2);
    }
};

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值