二分、快排、堆排与双指针

二分

cpp 复制代码
int Binary_Search(vector<int> A,int key){
	int n=A.size();
	int low=0,high=n-1,mid;
	while(low<=high){
		mid=(low+high)/2;
		if(A[mid]==key)
			return mid;
		else if(A[mid]>key)
			high=mid-1;
		else
			low=mid+1;	
	}
	return -1;
}

折半插入排序

------找到第一个 ≥ \ge ≥tem的元素

cpp 复制代码
void InsertSort(vector<int> A){
	int n=A.size();
	int low,high,mid;
	for(int i=1;i<=n;i++){
		int tem=A[i];
		low=1;high=i-1;
		while(low<=high){
			mid=(low+high)/2;
			if(A[mid]>tem)	//只要A[high]>tem,就不断往后移
				high=mid-1;	
			else	//先往右移,后续往做移
				low=mid+1;
		}
		for(int j=i-1;j>=high+1;j--)
			A[j+1]=A[j];
		A[high+1]=tem;
	}

在非递减数组中找到比x小的最后一个元素和比x大的第一个元素

  • 每次有要处理的就if-else
  • 为了避免无限循环>>[begin,mid-1] U mid U [mid+1,end]
  • 为了产生mid+1对nums[mid+1]进行讨论
cpp 复制代码
class Solution {
public:
    int search_left(vector<int> nums,int begin,int end,int target){
        if(begin>end)   return -1;
        if(nums[end]<target)
            return end;
        if(nums[begin]>=target)
            return begin-1;
        else {
            int mid=(begin+end)/2;
            if(nums[mid]>=target)
                return search_left(nums,begin,mid-1,target);
            else {
                if(mid==nums.size()-1)
                    return -1;
                else if(nums[mid+1]>=target)
                    return mid;
                else  
                    return search_left(nums,mid+1,end,target);
            }
        }
    }

    int search_right(vector<int> nums,int begin,int end,int target){
        if(begin>end)   return nums.size();
        if(nums[begin]>target){
            return begin;
        }  
        if(nums[end]<=target){
            return end+1;
        }
        else {
            int mid=(begin+end)/2;
            if(nums[mid]<=target)
                return search_right(nums,mid+1,end,target);
            else{
                if(mid==0)  return nums.size();
                if(nums[mid-1]<=target) return mid;
                else    return search_right(nums,begin,mid-1,target);
            }
        }
    }

    vector<int> searchRange(vector<int>& nums, int target) {
        int N=nums.size();
        int left=search_left(nums,0,N-1,target);
        int right=search_right(nums,0,N-1,target);
        vector<int> ans(2);
        if(left>=right-1){
            ans[0]=-1;  
            ans[1]=-1;
        }
        else {
            ans[0]=left+1;···
            ans[1]=right-1;
        }        
        return ans; 
    }
};

三数之和

  1. 三数之和首先把nums[0]~nums[n-1]排序
  2. 在a固定的情形下,条件: 每次c及其右侧所有可能都被确定,
    利用贪心性质:if b+c+a<0, b左侧的在c往左移的过程中更不可能,因此b++
    else if b+c+a>0,c及其右侧在b右移的过程中更不可能,因此c--,
    b+c+a=0,当前的b及其左侧已不可能,不妨b++,
cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};`

四数相加

  • 要返回解,只能2层循环+双指针,2个数组的双指针,不能输出全部解
cpp 复制代码
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>hash;
        int res = 0;
        int n =nums1.size();
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                hash[nums1[i]+nums2[j]]++;
            }
        } for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                auto it = hash.find(-(nums3[i]+nums4[j]));
                if(it!=hash.end()){
                    res += it->second;
                }
            }
        }
    return res;
    }
};

滑动窗口最大值

mean1: priority_queue

cpp 复制代码
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        auto cmp = [](const pair<int, int>& a, const pair<int, int>& b) -> bool { return a.first < b.first; };
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
        int n=nums.size();
        vector<int> ans;
        for(int i=0;i<n;i++){
            pq.push({nums[i],i});
            while(pq.top().second<i-k+1){
                pq.pop();
            }
            if(i>=k-1)
                ans.push_back(pq.top().first);
        }
        return ans;
    }
};

mean2: 单调队列

deque=双端队列,有clear()

cpp 复制代码
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        deque<int> q;
        for (int i = 0; i < k; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
        }

        vector<int> ans = {nums[q.front()]};
        for (int i = k; i < n; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
            while (q.front() <= i - k) {
                q.pop_front();
            }
            ans.push_back(nums[q.front()]);
        }
        return ans;
    }
};

Exp++

  1. 本质是用堆化简排序

双指针

cpp 复制代码
for (int i = 0, j = 0; i < n; i ++ )  // j从某一位置开始,不一定是0
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
}
常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间,比如快排的划分过程
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
  1. 双指针算法的核心思想(作用):优化.
  2. 在利用双指针算法解题时,考虑原问题如何用暴力算法解出,观察是否可构成单调性,若可以,就可采用双指针算法对暴力算法进行优化.
  3. 当我们采用朴素的方法即暴力枚举每一种可能的情况,时间复杂度为O(n*n),双指针降为O(n).
相关推荐
胜天半子_王二_王半仙1 小时前
c++源码阅读__smart_ptr__正文阅读
开发语言·c++·开源
程序猿阿伟1 小时前
《C++智能合约与区块链底层交互全解析:构建坚实的去中心化应用桥梁》
c++·区块链·智能合约
沐泽Mu1 小时前
嵌入式学习-C嘎嘎-Day08
开发语言·c++·算法
黑不溜秋的2 小时前
C++ 编程指南04 - 尽量编写静态类型安全的程序
开发语言·c++·安全
努力学习的饼干3 小时前
C++模版特化和偏特化
开发语言·c++
明月*清风4 小时前
【数据结构专栏】二叉搜索树(Binary Search Tree)的剖析?
开发语言·数据结构·c++·visualstudio
qiaoqiaohonghu4 小时前
c/c++ 用easyx图形库写一个射击游戏
c语言·c++·游戏
Beau_Will4 小时前
数据结构-树状数组专题(2)
数据结构·c++·算法
济南信息学奥赛刘老师5 小时前
GESP考试大纲
开发语言·c++·算法·青少年编程
~yY…s<#>5 小时前
【刷题21】BFS解决FloodFill算法专题
数据结构·c++·算法·leetcode·宽度优先