Daily LeetCode ---- 数组

Posted 唯有努力生存

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Daily LeetCode ---- 数组相关的知识,希望对你有一定的参考价值。

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

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值,返回 [-1, -1]。

注:你的算法时间复杂度必须是 O(log n) 级别。

// 2020.06.30 17:35// 算法思路:左端点二分查找 + 右端点二分查找// 时间复杂度 O(logn) 空间复杂度 O(1)class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { // 特例处理 if(nums.size() == 0) return {-1, -1}; vector<int> res; int l = 0, r = nums.size() - 1; int m = l + (r - l) / 2; // left while(l <= r){ m = l + (r - l) / 2; if(nums[m] > target) r = m - 1; else if(nums[m] < target) l = m + 1; else r = m - 1; // 查找左端点时 右侧逼近 } // 全部小于 target 则 l = nums.size() 全部大于 target 则 nums[l] = nums[0] != target // 终止循环的条件是 l == r + 1 if(l == nums.size() || nums[l] != target) // 如果左端点未找到 也无需找右端点 直接返回 return {-1, -1}; else res.push_back(l); // right l = 0;  r = nums.size() - 1; m = l + (r - l) / 2; while(l <= r){ m = l + (r - l) / 2; if(nums[m] > target) r = m - 1; else if(nums[m] < target) l = m + 1; else l = m + 1; // 查找右端点时 左侧逼近 } // 全部小于 target 则 r = -1 全部大于 target 则 nums[r] = nums[n-1] != target // 终止循环的条件是 l == r + 1 /*if(r < 0 || nums[r] != target) r = -1; else res.push_back(r); */ // 如果程序可以进行到此处 必然存在右端点 res.push_back(r); return res; }};

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

// 2020.06.30 17:41// 二分法// 时间复杂度 O(logn) 空间复杂度 O(1)class Solution {public: int searchInsert(vector<int>& nums, int target) { if(nums.size() == 0) return 0; int l = 0, r = nums.size() - 1; while(l <= r){ int m = l + (r - l) / 2; if(nums[m] == target) return m; else if(nums[m] > target) r = m - 1; else l = m + 1; } return l; }};

39. 组合总和

给定一个 无重复元素 的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
注1:candidates 中的数字可以无限制重复被选取。
注2:所有数字(包括 target)都是正整数。
注3:解集不能包含重复的组合。
// 2020.07.01 12:03// 递归:一种调用自己的编程技巧;回溯:用递归实现的一种算法思想,类似穷举法,有“剪枝功能”// 时间复杂度 O() 空间复杂度 O(n)// 减法 dfs target == 0class Solution {public: vector<vector<int>> res; vector<int> path;  void dfs(vector<int>& candidates, int begin, int target){ if(target == 0){ res.push_back(path); return; } for(int i = begin; i < candidates.size() && candidates[i] <= target; i++){ path.push_back(candidates[i]); dfs(candidates, i, target - candidates[i]); // 回溯 path.pop_back(); } }
vector<vector<int>> combinationSum(vector<int>& candidates, int target) { // 排序降低算法复杂度 sort(candidates.begin(), candidates.end()); dfs(candidates, 0, target); return res; }};
// 2020.07.01 12:24// 加法 dfs target == targetclass Solution {public: vector<vector<int>> res; vector<int> path; int sum; // 用于存储目标值  void dfs(vector<int>& candidates, int begin, int target){ if(target == sum){ res.push_back(path); return; } for(int i = begin; i < candidates.size() && target < sum; i++){ path.push_back(candidates[i]); dfs(candidates, i, target + candidates[i]); // target + candidates[i] path.pop_back(); } }
vector<vector<int>> combinationSum(vector<int>& candidates, int target) { sort(candidates.begin(), candidates.end()); sum = target; dfs(candidates, 0, 0); // 起初总和为 0 逼近 target return res; }};

40. 组合总和II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
注1:candidates 中的每个数字在每个组合中只能使用一次。
注2:所有数字(包括目标数)都是正整数。
注3:解集不能包含重复的组合。 
// 2020.07.01 12:47// 注意数组中可能有重复元素class Solution {public: vector<vector<int>> res; vector<int> path;
void dfs(vector<int>& candidates, int begin, int target){ if(target == 0){ res.push_back(path); return; } for(int i = begin; i < candidates.size() && target > 0; i++){ // 去重的核心在此 去掉同层重复 保留上下层的重复 if(i > begin && candidates[i] == candidates[i-1]) continue; path.push_back(candidates[i]); dfs(candidates, i+1, target - candidates[i]); path.pop_back(); } }
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { // 排序方便“剪枝” sort(candidates.begin(), candidates.end()); dfs(candidates, 0, target); return res; }};

☆41. 缺失的第一个正数

给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。注:你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。

同类问题:442. 数组中重复的数据 以及 448. 找到所有数组中消失的数字

// 2020.07.02 10:38// 如果不考虑时间复杂度的要求 排序后一层循环即可// 时间复杂度 O(nlogn) 空间复杂度 O(logn)// 其他思路:哈希表 依次从1开始判断各数是否在数组中 // 时间复杂度 O(n) 空间复杂度 O(n)class Solution {public: int firstMissingPositive(vector<int>& nums) { sort(nums.begin(), nums.end()); // 考虑到 1 为最小正整数 res 初始化为 1 int res = 1; // 注意 for 循环里的额外限制条件 res >= nums[i] for(int i = 0; i < nums.size() && res >= nums[i]; i++){ if(nums[i] <= 0) continue; // 比 nums[i] 大一个即可 res = nums[i] + 1; } return res; }};
// 2020.07.02 11:26// 原地哈希:把数组看作哈希表// 算法思路:数组长度为N 要找的数一定在[1,N+1] 把1放在下标0的位置 以此类推// 遍历数组时 第一个遇到的值不等于下标的那个数 就是所求结果// 时间复杂度 O(n) -- 均摊复杂度分析 空间复杂度 O(1)class Solution {public: void swapArray(vector<int>& nums, int i, int j){ int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
int firstMissingPositive(vector<int>& nums) { int length = nums.size(); for(int i = 0; i < length; i++){ // 注意这里是 while 需要一直判断互换位置后是否满足条件 while(nums[i] != i + 1 && nums[i] > 0 && nums[i] <= length && nums[i] != nums[nums[i] - 1]) swapArray(nums, i, nums[i]-1); } for(int i = 0; i < length; i++){ if(nums[i] != i + 1) return i + 1; } return length + 1; }};

442. 数组中重复的数据

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。

注:你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

// 2020.07.02 13:27// 原地哈希// 时间复杂度 O(n) 空间复杂度 O(1)class Solution {public: void swapArray(vector<int>& nums, int i, int j){ int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
vector<int> findDuplicates(vector<int>& nums) { vector<int> res; int length = nums.size(); for(int i = 0; i < length; i++){ while(nums[i] != i + 1 && nums[i] != nums[nums[i] - 1]) swapArray(nums, i, nums[i] - 1); } for(int i = 0; i < length; i++){ if(nums[i] != i + 1) res.push_back(nums[i]); } return res; }};

448. 找到所有数组中消失的数字

给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。找到所有在 [1, n] 范围之间没有出现在数组中的数字。

注:您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

// 2020.07.03 11:01// 原地哈希// 时间复杂度 O(n) 空间复杂度 O(1)class Solution {public: void swapArray(vector<int>& nums, int i, int j){ int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> res; int length = nums.size(); for(int i = 0; i < length; i++){ while(nums[i] != i + 1 && nums[i] != nums[nums[i] - 1]) swapArray(nums, i, nums[i] - 1); } for(int i = 0; i < length; i++){ if(nums[i] != i + 1) res.push_back(i + 1); } return res; }};

48. 旋转图像

给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。

注:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

// 2020.07.03 11:45// 找到旋转对应规律 结合哈希表 递归// 时间复杂度 O(n2) 空间复杂度 O(1)class Solution {public: map<vector<int>,int> hash;
void swapMatrix(vector<vector<int>>& matrix, int i, int j, int value){ if(hash.count({i,j})) return; int n = matrix.size(); int temp = matrix[i][j]; matrix[i][j] = value; hash.insert(pair<vector<int>,int>({i,j},1)); // 注意是 matrix[j][n-1-i] swapMatrix(matrix, j, n-1-i, temp); }
void rotate(vector<vector<int>>& matrix) { // (i,j) --> (j,n-1-i) int n = matrix.size(); for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ // 注意是 matrix[n-1-j][i] swapMatrix(matrix, i, j, matrix[n-1-j][i]); } } }};
// 2020.07.03 12:34// 本质上是 [i,j] --> [j,n-1-i] // 算法思路1:先转置[i,j] = [j,i] 再翻转 [i,j] = [i,n-j-1]// 算法思路2:转化为移动4个位置元素的临时列表 外循环找大框 内循环找4个点// 时间复杂度 O(n2) 空间复杂度 O(1)class Solution {public: void rotate(vector<vector<int>>& matrix) { int n = matrix.size(); for(int start = 0, end = n - 1; start < end; start++, end--){ // 大框逐步缩小为小框 for(int i = start, j = end; i < end; i++, j--){ // 同一层框内点的移动 int temp = matrix[start][i]; matrix[start][i] = matrix[j][start]; matrix[j][start] = matrix[end][j]; matrix[end][j] = matrix[i][end]; matrix[i][end] = temp; } } }};

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

// 2020.07.04 10:20// 核心在于:如果累加值小于 nums[i] 则以 nums[i] 作为新起点// 时间复杂度 O(n) 空间复杂度 O(1)class Solution {public: int maxSubArray(vector<int>& nums) { if(nums.size() == 0) return 0; int res = nums[0]; int sum = 0; for(int i = 0; i < nums.size(); i++){ sum += nums[i]; if(sum < nums[i]) sum = nums[i]; if(sum > res) res = sum; } return res; }};
// 2020.07.04 10:51// 动态规划:f(i) = max{ f(i-1) + nums[i], nums[i] }// 所以本质上上面我写的思路其实也是动态规划?// 时间复杂度 O(n) 空间复杂度 O(1)#define max(a,b) ((a) > (b) ? (a) : (b))
class Solution {public: int maxSubArray(vector<int>& nums) { if(nums.size() == 0) return 0; int pre = 0; // 滚动表示 前 i 个数组元素的最大子序和 int res = nums[0]; for(int i = 0; i < nums.size(); i++){ pre = max(pre + nums[i], nums[i]); // 更新最大自序和 res = max(res, pre); // 更新 f(i) 数组中的最大值 } return res; }};
// 2020.07.04 11:52// 分治策略:将(l,r)区间 转化为 (l,m) 和 (m+1,r) 两个区间// 时间复杂度 O(n)  空间复杂度 O(1)#define max(a,b) ((a) > (b) ? (a) : (b))
class Solution {public: struct Status{ // lsum 以左端点开始的最大子序和 rsum 以右端点结束的最大子序和 // msum 最大子序和 isum 整个区间的元素和 int lsum, rsum, msum, isum; };
Status pushUp(Status l, Status r){ // 整合两个子区间 int isum = l.isum + r.isum; int lsum = max(l.lsum, l.isum + r.lsum); int rsum = max(r.rsum, r.isum + l.rsum); int msum = max(max(l.msum, r.msum), l.rsum + r.lsum); return (Status){lsum, rsum, msum, isum}; }
Status get(vector<int>& nums, int l, int r){ if(l == r) return (Status){nums[l], nums[l], nums[l], nums[l]}; int m = (l + r) / 2; Status left = get(nums, l, m); Status right = get(nums, m + 1, r); return pushUp(left, right); }
int maxSubArray(vector<int>& nums) { return get(nums, 0, nums.size() - 1).msum; }};

55. 跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

// 2020.07.05 11:17// 贪心算法:依次遍历数组中的每一个位置,并实时维护“最远可以到达的位置”// 时间复杂度 O(n) 空间复杂度 O(1)class Solution {public: bool canJump(vector<int>& nums) { int res = 0; // 最远可以到达的位置 for(int i = 0; i < nums.size(); i++){ // 首先得是 i 位置可以到达 才可以在 nums[i] 基础上继续增加 if(i <= res){ res = max(i + nums[i], res); if(res >= nums.size() - 1) return true; } } return false; }};

以上是关于Daily LeetCode ---- 数组的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode] 739. Daily Temperatures

LeetCode 739. 每日温度 Daily Temperatures (Medium)

LeetCode 739. 每日温度 Daily Temperatures (Medium)

Leetcode daily 20/03/15 岛屿的最大面积

Leetcode(Daily 11-8 ~ 11-14)

[栈] leetcode 739 Daily Temperatures