首页 > 其他分享 >20天 hot 100 速通计划-day14

20天 hot 100 速通计划-day14

时间:2023-08-22 20:12:13浏览次数:37  
标签:20 速通 nums int mid day14 数组 升序 left

二分查找

33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -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

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums 中的每个值都 独一无二
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

因为旋转位数 k 是不定的,而 mid 必定在中间位置

  1. 当左侧有序时,nums[left] 是左侧有序部分的最小值,nums[mid] 是左侧有序部分的最大值。如果从左侧有序部分的最小值 nums[left] 开始向右查找,一直到左侧有序部分的最大值 nums[mid],都不会大于 nums[left],因此取小于等于符号。
  2. 当右侧有序时,nums[mid] 是右侧有序部分的最小值,nums[right] 是右侧有序部分的最大值。如果从右侧有序部分的最小值 nums[mid] 开始向左查找,一直到右侧有序部分的最大值 nums[right],都不会小于 nums[mid],因此取小于符号。

设旋转前数组:[1,2,3,4,5,6], n = 6, left = 0, right = 6 - 1 = 5, mid = 6 / 2 = 3

  • k < n / 2 → [5,6,1,2,3,4] → nums[left] = 5 > nums[mid] = 2 mid 右侧升序
  • k = n / 2 → [4,5,6,1,2,3] → nums[left] = 4 > nums[mid] = 1 mid 左侧升序
  • k > n / 2 → [3,4,5,6,1,2] → nums[left] = 3 < nums[mid] = 6 mid 左侧升序

设旋转前数组:[1,2,3,4,5], n = 5, left = 0, right = 5 - 1 = 4, mid = 5 / 2 = 2

  • k < n / 2 → [5,1,2,3,4] → nums[left] = 5 > nums[mid] = 2 mid 右侧升序
  • k = n / 2 → [4,5,1,2,3] → nums[left] = 5 > nums[mid] = 1 mid 右侧升序
  • k > n / 2 → [3,4,5,1,2] → nums[left] = 3 < nums[mid] = 5 mid 左侧升序

设旋转前数组:[1, 2], n = 2, left = 0, right = 1, mid = 2 / 2 = 1

  • k < n / 2 → [1,2] → nums[left] = 1 < nums[mid] = 2 mid 左侧升序
  • k = n / 2 → [2,1] → nums[left] = 2 > nums[mid] = 1 mid 右侧升序
  • k > n / 2 → [1,2] → nums[left] = 1 < nums[mid] = 2 mid 左侧升序

设旋转前数组:[1], n = 1, left = 0, right = 0, mid = 1 / 2 = 0

  • [1], nums[left] = nums[mid] mid 左侧升序 / mid 右侧升序

总结:

  • 若 nums[left] <= nums[mid],则 mid 左边有序
  • 若 nums[left] > nums[mid],则 mid 右边有序
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = nums.size(); // 数组长度
        if (n == 0) return -1; // 特殊情况,数组为空,直接返回-1
        
      // 二分法固定结构,首尾双指针,左闭右闭
        int left = 0, right = n - 1; 
        
        while (left <= right) { // 当左指针小于等于右指针时,执行循环
            int mid = left + (right - left) / 2; // 计算中间指针的位置
            
            if (nums[mid] == target) { // 如果中间的数等于目标值,直接返回结果
                return mid;
            }
            
            // 判断 mid 左边是否有序
            if (nums[left] <= nums[mid]) {
                // 判断 target 是否在有序的一侧
                if (nums[left] <= target && target < nums[mid]) {
                    right = mid - 1; // 在有序一侧(左边),调整右指针
                } else {
                    left = mid + 1; // 在无序一侧,调整左指针
                }
            } else {    	
                // 判断 target 是否在有序的一侧
                if (nums[mid] < target && target <= nums[right]) {
                    left = mid + 1; // 在有序一侧(右边),调整左指针
                } else {
                    right = mid - 1; // 在无序一侧,调整右指针
                }
            }
        }
        return -1; // 循环结束后仍然没有找到目标值,返回-1
    }
};

153. 寻找旋转排序数组中的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0; // 左边界
        int right = nums.size() - 1; // 右边界

        while (left < right) {
            int mid = left + (right - left) / 2; // 中间元素的索引

            if (nums[mid] < nums[right]) { // 中间元素比右边界元素小,说明最小值在左半边
                right = mid;
            }
            else { // 中间元素比右边界元素大,说明最小值在右半边
                left = mid + 1;
            }
        }

        return nums[left];
    }
};

4. 寻找两个正序数组的中位数

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

有点像归并排序 + 二分查找的混合物

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(); // 数组nums1的长度
        int n = nums2.size(); // 数组nums2的长度
        int total = m + n; // 合并后数组的长度
        vector<int> merged(total);  // 合并后的有序数组
        
        int p1 = 0, p2 = 0; // 用于标记当前指针的位置
        int p = 0;  // 合并后数组的指针

        while (p1 < m && p2 < n) { // 当两个数组都没有遍历完时
            if (nums1[p1] <= nums2[p2]) { // 如果nums1当前位置的元素小于等于nums2当前位置的元素
                merged[p++] = nums1[p1++]; // 将nums1当前位置的元素放入合并后的数组中,并将指针向后移动一位
            } else { // 如果nums1当前位置的元素大于nums2当前位置的元素
                merged[p++] = nums2[p2++]; // 将nums2当前位置的元素放入合并后的数组中,并将指针向后移动一位
            }
        }

        while (p1 < m) { // 如果nums1还有未遍历的元素
            merged[p++] = nums1[p1++]; // 将nums1剩余的元素放入合并后的数组中,并将指针向后移动一位
        }

        while (p2 < n) { // 如果nums2还有未遍历的元素
            merged[p++] = nums2[p2++]; // 将nums2剩余的元素放入合并后的数组中,并将指针向后移动一位
        }

        if (total % 2 != 0) {  // 如果合并后数组的长度为奇数
            return merged[total / 2]; // 返回合并后数组中间位置的元素
        } else {  // 如果合并后数组的长度为偶数
            return (merged[total / 2] + merged[total / 2 - 1]) * 0.5; // 返回合并后数组中间两个元素的平均值
        }
    }
};

20. 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成
class Node {
public:
	int data;
	Node* next;

	Node(int val) {
		data = val;
		next = nullptr;
	}
};
class Stack {
private:
	Node* topNode;

public:
	Stack() {
		topNode = nullptr;
	}

	bool isEmpty() {
		return topNode == nullptr;
	}

	void push(int val) {
		Node* newNode = new Node(val);
		if (isEmpty()) {
			topNode = newNode;
		}
		else {
			// topNode
			// ↓ 
			// 3 → 2 → 1

			//		topNode
			//			↓ 
			//newNode → 3 → 2 → 1

			//topNode
			//	↓ 
			//newNode → 3 → 2 → 1
			
			newNode->next = topNode;
			topNode = newNode;
		}
	}

	int pop() {
		if (isEmpty()) {
			cout << "Error:stack is empty!" << endl;
			return -1;
		}
		else {
			int val = topNode->data;
			Node* tmp = topNode;
			topNode = topNode->next;
			delete tmp;
			return val;
		}
	}
};
class Solution {
public:
    bool isValid(string s) {
      Stack stack;
      for (char c : s) {
        //遇见左括号,左括号入栈
        if (c == '(' || c == '[' || c == '{') {
          cout << c << " ";
          stack.push(c);//左括号入栈
        }
        //遇见右括号,已入栈的左括号出栈
        else if (c == ')' || c == ']' || c == '}') {
          cout << c << " ";
          //只有右括号,没有左括号 → 不匹配
          if (stack.isEmpty()) {
            return false;
          }
          char top = stack.pop();//左括号出栈
          //判断是否匹配
          if ((c == ')' && top != '(')
            || (c == ']' && top != '[')
            || (c == '}' && top != '{')) {
            return false;
          }
        }
        else {
          cout << "Error: invalid char";
          return false;
        }
      }
      //栈为空,所有括号都匹配
      return stack.isEmpty();
    }
};

155. 最小栈

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

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

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

提示:

  • -231 <= val <= 231 - 1
  • poptopgetMin 操作总是在 非空栈 上调用
  • push, pop, top, and getMin最多被调用 3 * 104

纯技巧题

class MinStack {
    stack<int> s; // 声明一个普通的栈,用于存储所有的元素
    stack<int> minStack; // 声明一个辅助栈,用于存储当前栈的最小值
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s.push(x); // 将元素x压入普通栈中
        if (minStack.empty() || x <= minStack.top()) { // 如果辅助栈为空或者x小于等于辅助栈的栈顶,则将x压入辅助栈
            minStack.push(x);
        }
    }
    
    void pop() {
        if (s.top() == minStack.top()) { // 如果普通栈的栈顶元素等于辅助栈的栈顶元素,则将辅助栈的栈顶元素弹出
            minStack.pop();
        }
        s.pop(); // 弹出普通栈的栈顶元素
    }
    
    int top() {
        return s.top(); // 返回普通栈的栈顶元素
    }
    
    int getMin() {
        return minStack.top(); // 返回辅助栈的栈顶元素,即当前栈的最小值
    }
};

标签:20,速通,nums,int,mid,day14,数组,升序,left
From: https://www.cnblogs.com/ba11ooner/p/17649579.html

相关文章

  • ACM MM 2023 | 腾讯优图实验室6篇论文入选,含视觉识别、半监督学习等研究方向
    前言 近日,腾讯优图实验室6篇论文被国际人工智能多媒体领域顶级会议ACMMM2023(ACMInternationalConferenceonMultimedia)所接收,涵盖视觉识别、神经绘画和风格化研究、半监督学习等多个研究方向,进一步展示了腾讯优图实验室在人工智能领域的技术能力和学术成果。ACMMM是计算机......
  • P9562 [SDCPC2023] G-Matching
    思路易发现,如果\(i\)和\(j\)可以连边,\(j\)和\(k\)可以连边,那\(i\)和\(k\)也可以连边,如果\(x\)不能和\(i\)连边,那\(x\)同样不能和\(j,k\)连边。所以我们可以考虑把所有可以连边的放在一起,这样就把所有点分成了若干部分,并且每个部分不可能连边,必然是分割开的。......
  • P9560 [SDCPC2023] E-Math Problem
    思路首先发现应该优先除,理由很简单,如果先乘以\(k\)再加上一个不超过\(k\)的值,那么除以\(k\)后,就除回去了,没有发生任何变化。所以我们可以先枚举除以多少次\(k\),得到除以这么多次\(k\)后的\(n\)。我们再进行若干次乘法,计算\(n\)的取值范围\([l,r]\),那么只要这个区间......
  • AT_codefestival_2016_qualB_c Gr-idian MST
    思路首先想到暴力建边跑最小生成树,但是显然会TLE。所以思考有没有时间复杂度更低的做法,考虑到最小生成树是每次取最短的边,所以我们也可以先考虑较短的边。首先最短的边一定是某一列或者某一行(或者若干列和行),所以我们取边,也应该是一行一行或者一列一列的取。但是有些时候这样......
  • P1612 [yLOI2018] 树上的链
    因为自己太憨了,所以交了好几次都没过,谢谢审核大大!!!思路因为这是一棵树,所以每个节点只有一个父亲,那么选定一个结点,它到根节点的路径唯一。所以第一个思路就是暴力,对于每一个节点,直接暴力向上枚举,找到第一个满足条件的节点,然后输出长度即可。但是显然,第一种方法很容易TLE,所以我......
  • 2023第八届世界机器人大会要点合集!
    原创|文BFT机器人01十大应用场景板块,展示“机器人+”行业应用8月16日下午,2023世界机器人大会在北京北人亦创国际会展中心开幕,本届大会以“开放创新,聚享未来”为主题,由北京市人民政府、工业和信息化部、中国科学技术协会主办、中国电子学会、北京市经济和信息化局、北京经济技术......
  • 在Docker上安装部署SQL Server2019 Express
    在Docker上安装部署SQLServer2019Express_docker安装sqlserver2019_梦想天空分外蓝的博客-CSDN博客  梦想天空分外蓝_-CSDN博客......
  • Python学习日记 2023年8月22日
    importglobimportargparseimportcv2importnumpyfromtqdmimporttqdmfromitertoolsimportproductdefparsArgs():parser=argparse.ArgumentParser('拼接马赛克图片')parser.add_argument('--targetpath',type=str,default='3.jp......
  • 2023年小程序游戏可以玩出哪些花样?
    疫情过后,一地鸡毛。游戏行业的日子也不好过。来看看移动游戏收入:2022年,移动游戏收入达到920亿美元,同比下降6.4%。这告诉我们,2022年对移动游戏市场来说是一个小挫折。但不管是下挫还是上升,移动游戏市场依然代表了大趋势,手机游戏在全球游戏市场中占据的份额也越来越大。据Newzoo的数......
  • 2023-08-22 uni-popup 默认弹出显示
    奇怪!!uni的弹窗组件明明是默认隐藏的,我又没有做弹出的业务,为什么就蹦出来了呢??重新编译也不行,又没有报错。。就像uni-popup完全没生效,直接显示了里面的内容===========================若干分钟后 ===========================啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊......