LeetCode(六)二分与单调队列_栈专题

LeetCode 69. Sqrt(x)

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。

class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x;
        while(l < r)
        {
            long long mid = l + 1ll + r >> 1; 
            if(mid * mid <= x) l = mid;
            else r = mid - 1;
        }
        return l;
    }
};

LeetCode 34. Find First and Last Position of Element in Sorted Array

给定一个按照升序排列的整数数组 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:
    vector<int> searchRange(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1; //二分下界,左边
        while(l < r)
        {
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        
        if(nums.empty() || nums[r] != target)
        {
            vector<int> res;
            res.push_back(-1), res.push_back(-1);
            return res;
        }
        vector<int> res;
        res.push_back(l);
        
        l = 0, r = nums.size() - 1;//二分上界,右边
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(nums[mid] <= target) l = mid;
            else r = mid - 1; 
        }
        res.push_back(l);
        return res;
        
    }
};

LeetCode 74. Search a 2D Matrix

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

示例 1:

输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true

示例 2:

输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty()) return false;
        int n = matrix.size(), m = matrix[0].size();
        int l = 0, r = n * m - 1;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(matrix[mid / m][mid % m] >= target) r = mid;
            else l = mid + 1;
        }
        return matrix[r / m][r % m] == target;
    }
};

LeetCode 240. Search a 2D Matrix II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。

给定 target = 20,返回 false。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty()) return false;
        int i = 0, j = matrix[0].size() - 1;
        while(i < matrix.size() && j >= 0)
        {
            int t = matrix[i][j];
            if(t == target) return true;
            if(t > target) j--;
            else i++;
        }
        return false;
    }
};

LeetCode 153. Find Minimum in Rotated Sorted Array

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例 1:

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

示例 2:

输入: [4,5,6,7,0,1,2]
输出: 0

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums[0] < nums.back()) return nums[0];
        int l = 0, r = nums.size() - 1;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(nums[mid] >= nums[0]) l = mid + 1;
            else r = mid;
        }
        cout << l << ' ' << r << endl;
        return nums[l];
    }
};

LeetCode 162. Find Peak Element

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞。

示例 1:

输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。

示例 2:

输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。

说明:

你的解法应该是 O(logN) 时间复杂度的。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
       int l = 0, r = nums.size() - 1;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(nums[mid] > nums[mid - 1]) l = mid;
            else r = mid - 1;
        }
        // cout << l << ' ' << r << endl;
        return l;
    }
};

LeetCode 155. Min Stack

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> stackValue;
    stack<int> stackMin;
    MinStack() {
        
    }
    
    void push(int x) {
        stackValue.push(x);
        if(stackMin.empty() || x <= stackMin.top())
            stackMin.push(x);
    }
    
    void pop() {
        int x = stackValue.top();
        stackValue.pop();
        if(stackMin.top() == x) stackMin.pop();            
    }
    
    int top() {
        return stackValue.top();
    }
    
    int getMin() {
        return stackMin.top();
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

LeetCode 496. Next Greater Element I

给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。

示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于num1中的数字2,第二个数组中的下一个较大数字是3。
对于num1中的数字4,第二个数组中没有下一个更大的数字,因此输出 -1。

注意:
nums1和nums2中所有元素是唯一的。
nums1和nums2 的数组大小都不超过1000。

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> stk;//单调队列
        unordered_map<int, int>hash;//值 下标
        for(int i = nums2.size() - 1; ~i; i--) //~i 等价于i >=0  i 等价于 i > 0
        {
            int t = nums2[i];
            while(stk.size() && stk.top() <= t) stk.pop();//大到小排列
            hash[t] = stk.size() ? stk.top() : -1;
            stk.push(t);
        }
        vector<int> res;
        for(auto x : nums1) res.push_back(hash[x]);
        return res;
    } 
};

LeetCode 84. Largest Rectangle in Histogram

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

(图)

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

(图)

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

输入: [2,1,5,6,2,3]
输出: 10

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size(), ans = 0;
        heights.push_back(-1);//数组末尾添加高度 -1 可以使得所有数字最后出栈
        stack<int> stk;
        for(int i = 0; i <= n; i ++)
        {
            while(!stk.empty() && heights[i] < heights[stk.top()])//当前元素小于 栈顶元素
            {
                /*
                出栈后,cur右边第一个比它低的柱形条就是i,左边第一个比它低的柱形条是当前栈中的top。
                不断出栈直到栈为空或者柱形条i不再比top低。
                */
                int cur = stk.top();
                stk.pop();
                if(stk.empty()) //单调队列为空
                    ans = max(ans, heights[cur] * i);
                else //单调队列不为空
                    ans = max(ans, heights[cur] * (i - stk.top() - 1));
            }
            stk.push(i);
        }
        return ans;
    }
};

LeetCode 42. Trapping Rain Water

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

(图)

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size(), ans = 0;
        stack<int> stk;
        for(int i = 0; i < n; i ++)
        {
            while(!stk.empty() && height[i] > height[stk.top()])
            {
                int top = stk.top();
                stk.pop();
                if(stk.empty()) break;
                ans += (i - stk.top() - 1) * (min (height[stk.top()], height[i]) - height[top]);
            }
            stk.push(i);
        }
        return ans;
    }
};

LeetCode 475. Heaters

冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。

现在,给出位于一条水平线上的房屋和供暖器的位置,找到可以覆盖所有房屋的最小加热半径。

所以,你的输入将会是房屋和供暖器的位置。你将输出供暖器的最小加热半径。

说明:

给出的房屋和供暖器的数目是非负数且不会超过 25000。
给出的房屋和供暖器的位置均是非负数且不会超过10^9。
只要房屋位于供暖器的半径内(包括在边缘上),它就可以得到供暖。
所有供暖器都遵循你的半径标准,加热的半径也一样。

示例 1:

输入: [1,2,3],[2]
输出: 1
解释: 仅在位置2上有一个供暖器。如果我们将加热半径设为1,那么所有房屋就都能得到供暖。

示例 2:

输入: [1,2,3,4],[1,4]
输出: 1
解释: 在位置1, 4上有两个供暖器。我们需要将加热半径设为1,这样所有房屋就都能得到供暖。

class Solution {
public:
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        heaters.push_back(INT_MIN), heaters.push_back(INT_MAX);//哨兵 边界
        sort(heaters.begin(), heaters.end());
        int res = 0;
        for(auto &x : houses) //枚举每个房屋求最小半径
        {
            int l = 0, r = heaters.size() - 1;
            while(l < r)
            {
                int mid = l + r >> 1;
                if(heaters[mid] >= x) r = mid;
                else l = mid + 1;
            }
            // 房屋与左边 和右边的 最小值
            res = max(res, (int)min(x - 0ll - heaters[r - 1], heaters[r] - 0ll - x));
        }
        return res;
    }
};

LeetCode 239. Sliding Window Maximum

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。

返回滑动窗口最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

注意:

你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。

进阶:

你能在线性时间复杂度内解决此题吗?

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;//队尾队头 都要插入删除 所以用双向队列
        vector<int> res;
        for(int i = 0, j = 0; i < nums.size(); i ++)
        {
            while(!dq.empty() && nums[i] >= nums[dq.front()]) dq.pop_front();
            dq.push_front(i);//在头插入新元素 队尾是最大
            if(i - j + 1 > k) //判断队头是否出列
            {
                if(j >= dq.back()) dq.pop_back();
                j ++;
            }
            if(i - j + 1 == k)
                res.push_back(nums[dq.back()]);
        }
        return res;
    }
};

LeetCode 456. 132 Pattern

给定一个整数序列:a1, a2, …, an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。

注意:n 的值小于15000。

示例1:

输入: [1, 2, 3, 4]

输出: False

解释: 序列中不存在132模式的子序列。

示例 2:

输入: [3, 1, 4, 2]

输出: True

解释: 序列中有 1 个132模式的子序列: [1, 4, 2].

示例 3:

输入: [-1, 3, 2, 0]

输出: True

解释: 序列中有 3 个132模式的的子序列: [-1, 3, 2], [-1, 3, 0] 和 [-1, 2, 0].

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int s3 = INT_MIN;
        stack<int> stk;
        for(int i = nums.size() - 1; ~i; i--)
        {
            if(nums[i] < s3) return true; //i < j < k
            while(stk.size() && nums[i] > stk.top())
            {
                s3 = stk.top();//j 的值
                stk.pop();
            }
            stk.push(nums[i]); //k 的值
        }
        return false;          
    }
};

LeetCode 4. Median of Two Sorted Arrays

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total = nums1.size() + nums2.size();
        if(total % 2 == 0) //偶数
        {
            int left = findKthNumber(nums1, 0, nums2, 0, total / 2);
            int right = findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
            return (left + right) / 2.0;
        }
        else //奇数
        {
            return findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
        }        
    }
    //寻找第k大的数
    int findKthNumber(vector<int>&nums1, int i, vector<int>&nums2, int j, int k)
    {
        //边界1
        if(nums1.size() - i > nums2.size() - j) return findKthNumber(nums2, j, nums1, i, k);
        if(nums1.size() == i) return nums2[j + k - 1];
        if(k == 1) return min(nums1[i], nums2[j]);
        int si = min(i + k / 2, int(nums1.size())), sj = j + k / 2;
        if(nums1[si - 1] > nums2[sj - 1])
            return findKthNumber(nums1, i, nums2, j + k / 2, k - k / 2);
        else
            return findKthNumber(nums1, si, nums2, j, k - (si - i));
        
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值