专栏——LeetCode

文章目录

  • 专栏——LeetCode
  • 31. 下一个排列
  • 2. 最长有效括号
  • 34. 在排序数组中查找元素的第一个和最后一个位置
  • 35. 搜索插入位置
  • 36. 有效的数独
  • 37. 解数独
  • 38. 外观数列
  • 39. 组合总和
  • 40. 组合总和 II

31. 下一个排列

题目描述

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

题解:
找规律O(n):
我们可以这样来分析:
我们希望 下一个数比当前数大,这样才满足“下一个排列”的定义。因此只需要将后面的「大数」与前面的「小数」交换,就能得到一个更大的数。
比如 123456,将 56 交换就能得到一个更大的数 123465
我们还希望 下一个数增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
在尽可能靠右的低位进行交换,需要从后向前查找
将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 54 交换而不是把 64 交换
将「大数」换到前面后,需要将「大数」后面的所有数 重置为升序,升序排列就是最小的排列。以 123465 为例:首先按照上一步,交换 54
得到 123564;然后需要将 5 之后的数重置为升序,得到 123546。显然 123546123564 更小,123546 就是 123465 的下一个排列。

c++版

class Solution {public:void nextPermutation(vector<int>& nums) {int i = nums.size() - 1;while(i > 0 && nums[i - 1] >= nums[i])i--;if(i == 0)sort(nums.begin(),nums.end());else{int t = i;while(t < nums.size() && nums[t] > nums[i - 1])t++;swap(nums[t - 1], nums[i - 1]);reverse(nums.begin() + i, nums.end());}}
};

python版

class Solution:def nextPermutation(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""i = len(nums) - 1while i > 0 and nums[i - 1] >= nums[i]:i -= 1if i == 0:nums.sort()else:t = iwhile t < len(nums) and nums[t] > nums[i - 1]:t += 1nums[i - 1], nums[t - 1] = nums[t - 1], nums[i - 1]nums[i:len(nums) + 1] = nums[i:len(nums) + 1][::-1]

2. 最长有效括号

题目描述
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”

示例 2:

输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”

题解:
二分O(logn):
对于旋转数组 nums = [4,5,6,7,0,1,2]
首先根据 nums[0] 与 target 的关系判断 target 是在左段还是右段。
例如 target = 5, 目标值在左半段,因此在 [4, 5, 6, 7, inf, inf, inf] 这个有序数组里找就行了;
例如 target = 1, 目标值在右半段,因此在 [-inf, -inf, -inf, -inf, 0, 1, 2] 这个有序数组里找就行了。

c++版

class Solution {public:int longestValidParentheses(string s) {int arr[100010] = {0};int t = 0;int ans = 0;for(int i = 0; i < s.size(); i++){if(s[i] == '(')arr[++t] = i;else{if(t == 0)arr[++t] = i;else if(s[arr[t]] == '(')t--;else arr[++t] = i;}if(t == 0) ans = i + 1;else ans = max(ans, i - arr[t]);}return ans;}
};

python版

class Solution:def longestValidParentheses(self, s: str) -> int:l = [0 for _ in range(100010)]t = 0ans = 0for i in range(0, len(s)):if s[i] == '(':t += 1l[t] = ielse:if t > 0 and s[l[t]] == '(':t -= 1else:t += 1l[t] = iif t == 0:ans = i + 1else:ans = max(ans, i - l[t])return ans

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]

题解;
二分模板题O(logn)

c++版

class Solution {public:vector<int> searchRange(vector<int>& nums, int target) {vector<int> a, b;a.push_back(-1);a.push_back(-1);if(nums.size() == 0)return a;int left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] < target) left = mid + 1;else if (nums[mid] >= target) right = mid - 1;}// 最后要检查 left 越界的情况if (left >= nums.size() || nums[left] != target)return a;b.push_back(left);left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] <= target) left = mid + 1;else if (nums[mid] > target) right = mid - 1;}// 最后要检查 left 越界的情况if (right < 0 || nums[right] != target)return a;b.push_back(right);return b;}
};

python版

class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:if not nums:return [-1,-1]n = len(nums)l, r = 0, n - 1while l <= r:mid = (l + r) // 2if nums[mid] >= target:r = mid - 1else: l = mid + 1if l >= n or nums[l] != target:return [-1, -1]ans1 = ll, r = 0, n - 1while l <= r:mid = (l + r) // 2if nums[mid] <= target:l = mid + 1else: r = mid - 1if r < 0 or nums[r] != target:return [-1, -1]ans2 = rreturn [ans1, ans2]

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(logn)

c++版

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

python版

class Solution:def searchInsert(self, nums: List[int], target: int) -> int:n = len(nums)l, r = 0, n - 1while l <= r:mid = (l + r) // 2if nums[mid] == target:return midif nums[mid] > target:r = mid - 1else: l = mid + 1return r + 1

36. 有效的数独

题目描述

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。


上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:

输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true

示例 2:

输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
  • 给定数独永远是 9x9 形式的。

题解:
1.位运算判重
分别使用一个整型数组记录每行、每列和每个九宫格内数字的存在情况。
位运算可以极大的简化判断,提高效率,具体看代码。
2.二维布尔数组判断
原理跟位运算一样

c++版

class Solution {public:bool isValidSudoku(vector<vector<char>>& board) {int N = 9;int r[N], c[N], cell[3][3];for(int i = 0; i < N; i++) r[i] = c[i] = (1 << N) - 1;for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++)cell[i][j] = (1 << N) - 1;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(board[i][j] != '.'){int a = i / 3;int b = j / 3;int t = board[i][j] - '1';if(!(r[i] & 1 << t) || !(c[j] & 1 << t) || !(cell[a][b] & 1 << t))return false;r[i] -= 1 << t;c[j] -= 1 << t;cell[a][b] -= 1 << t;}}}return true;}
};

python版

class Solution:def isValidSudoku(self, board):""":type board: List[List[str]]:rtype: bool"""# init dataN = 9row = [{} for _ in range(N)]col = [{} for _ in range(N)]cell = [{} for _ in range(N)]for i in range(N):for j in range(N):ce_index = i // 3 * 3 + j // 3if board[i][j] != '.':t = int(board[i][j])if t in row[i] or t in col[j] or t in cell[ce_index]:return Falserow[i][t] = 1col[j][t] = 1cell[ce_index][t] = 1return True

37. 解数独

题目描述

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
    空白格用 ‘.’ 表示。

一个数独。

提示

  • 给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

题解
(递归回溯):
1.首先预处理出 col、row 和 squ 数组。
2.从 (0,0) 位置开始尝试并递归。遇到 . 时,枚举可以填充的数字,然后判重并加入 col、row 和 squ 数组中。
3.如果成功到达结尾,则返回 true,告知递归可以终止。

c++版

class Solution {public:bool row[9][9], col[9][9], cell[3][3][9];void solveSudoku(vector<vector<char>>& board) {memset(row, false, sizeof row);memset(col, false, sizeof col);memset(cell, false, sizeof cell);for(int i = 0; i < 9; i++)for(int j = 0; j < 9; j++)if(board[i][j] != '.'){int t = board[i][j] - '1';row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;}dfs(board, 0, 0);}bool dfs(vector<vector<char>>& board, int x, int y){if(y == 9) x += 1, y = 0;if(x == 9) return true;if(board[x][y] != '.') return dfs(board, x, y + 1);for(int i = 0; i < 9; i++){if(!row[x][i] && !col[y][i] && !cell[x / 3][y / 3][i]){board[x][y] = i + '1';row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = true;if(dfs(board, x, y + 1)) return true;board[x][y] = '.';row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = false;}}return false;}
};

python版

class Solution:def solveSudoku(self, board: List[List[str]]) -> None:"""Do not return anything, modify board in-place instead."""row = [[False for _ in range(9)] for _ in range(9)]col = [[False for _ in range(9)] for _ in range(9)]cell = [[False for _ in range(9)] for _ in range(9)]for i in range(9):for j in range(9):if board[i][j] != '.':t = int(board[i][j]) - 1row[i][t] = col[j][t] = cell[i // 3 * 3 + j // 3][t] = True;def dfs(board, x, y):if y == 9:x += 1; y = 0if x == 9: return Trueif board[x][y] != '.': return dfs(board, x, y + 1)for i in range(9):if not row[x][i] and not col[y][i] and not cell[x // 3 * 3 + y // 3][i]:board[x][y] = str(i + 1)row[x][i] = col[y][i] = cell[x // 3 * 3 + y // 3][i] = Trueif(dfs(board, x, y + 1)): return Trueboard[x][y] = '.'row[x][i] = col[y][i] = cell[x // 3 * 3 + y // 3][i] = Falsereturn Falsedfs(board, 0, 0)

38. 外观数列

题目描述

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221

第一项是数字 1

描述前一项,这个数是 1 即 “一个 1 ”,记作 11

描述前一项,这个数是 11 即 “两个 1 ” ,记作 21

描述前一项,这个数是 21 即 “一个 2 一个 1 ” ,记作 1211

描述前一项,这个数是 1211 即 “一个 1 一个 2 两个 1 ” ,记作 111221

示例 1:

输入: 1
输出: “1”
解释:这是一个基本样例。

示例 2:

输入: 4
输出: “1211”
解释:当 n = 3 时,序列是 “21”,其中我们有 “2” 和 “1” 两组,“2” 可以读作 “12”,也就是出现频次 = 1 而 值 = 2;类似 “1” 可以读作 “11”。所以答案是 “12” 和 “11” 组合在一起,也就是 “1211”。

题解:
(模拟) O(n2)
直接按照从 2 到 n 的顺序生成字符串,即每次找连续相同的数字段,合并。

c++版

class Solution {public:string countAndSay(int n) {string s = "1";for (int i = 0; i < n - 1; i ++ ) {string t;for (int j = 0; j < s.size();) {int k = j + 1;while (k < s.size() && s[k] == s[j]) k ++ ;t += to_string(k - j) + s[j];j = k;}s = t;}return s;}
};

python版

class Solution:def countAndSay(self, n: int) -> str:s = '1'for i in range(n - 1):j = 0t = ""while j < len(s):k = j + 1while k < len(s) and s[k] == s[j]: k += 1t += str(k - j) + s[j]j = ks = treturn s

39. 组合总和

题目描述

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

题解:
(递归枚举)
在每一层搜索中,枚举这个数字添加几次。
搜索的终止条件是层数超过的数组的长度或者当前数字组合等于目标值。
剪枝:可以先将数组从小到大排序,搜索中如果 sum != target 并且 sum+candidates[i] > target,
则可以直接终止之后的递归,因为之后的数字都会比 candidates[i] 大,不会再产生答案。

c++版

class Solution {public:vector<vector<int>> ans;vector<int> path;vector<vector<int>> combinationSum(vector<int>& candidates, int target) {dfs(candidates, 0, target);return ans;}void dfs(vector<int>& candidates, int cur, int target){if(target == 0){ans.push_back(path);return ;}if(target < 0) return ;if(cur == candidates.size()) return ;for(int i = 0; i * candidates[cur] <= target; i++){dfs(candidates, cur + 1, target - i * candidates[cur]);path.push_back(candidates[cur]);}for(int i = 0; i * candidates[cur] <= target; i++)path.pop_back();}
};

python版

class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:ans = []path = []def dfs(c, cur, target):if target == 0:ans.append(path[:])returnif target < 0: return if cur == len(c): return i = 0while(i * c[cur] <= target):dfs(c, cur + 1, target - i * c[cur])path.append(c[cur])i += 1i = 0while(i * c[cur] <= target):path.pop()i += 1dfs(candidates, 0, target)return ans

40. 组合总和 II

题目描述

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。
**
**说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]

题解:
递归搜索:
排序,对于每层搜索,判断当前数字重复的次数,保证搜索下一层的数字和当前数字不一样

c++版

class Solution {public:vector<vector<int>> ans;vector<int> path;vector<vector<int>> combinationSum2(vector<int>& c, int target) {sort(c.begin(), c.end());dfs(c, 0, target);return ans;}void dfs(vector<int>& c, int u, int target) {if (target == 0) {ans.push_back(path);return;}if (u == c.size()) return;int k = u + 1;while (k < c.size() && c[k] == c[u]) k ++ ;int cnt = k - u;for (int i = 0; c[u] * i <= target && i <= cnt; i ++ ) {dfs(c, k, target - c[u] * i);path.push_back(c[u]);}for (int i = 0; c[u] * i <= target && i <= cnt; i ++ ) {path.pop_back();}}
};

python

class Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:ans = []path = []candidates.sort()def dfs(c, cur, target):if target == 0:ans.append(path[:])return if cur == len(c): returnif target < 0: return k = cur + 1while k < len(c) and c[k] == c[cur]: k += 1cnt = k - curi = 0while i <= cnt and i * c[cur] <= target: dfs(c, k, target - i * c[cur])path.append(c[cur])i += 1i = 0while i <= cnt and i * c[cur] <= target:path.pop()i += 1dfs(candidates, 0, target)return ans

LeetCode Week 4:第 31 ~ 40 题相关推荐

  1. 【剑指Offer】俯视50题之31 - 40题

    [剑指Offer]俯视50题之31 - 40题 面试题31连续子数组的最大和 面试题32从1到n整数中1出现的次数 面试题33把数组排成最小的数 面试题34丑数 面试题35第一个仅仅出现一次的字符 面 ...

  2. JavaScript刷LeetCode拿offer-经典高频40题

    工作太忙没有时间刷算法题,面试的时候好心虚.这里双手奉上40道LeetCode上经典面试算法题,整理的内容有点长,建议先收藏,慢慢消化,在来年顺利拿到满意的offer. 1.[LeetCode] 两数 ...

  3. 【人工智能 机器学习 深度学习】基础选择题 31~60题 练习(题目+答案),亦含 判断题

    目录 一.前情回顾 二.31~40题 2.1 题目 2.2 答案 三.41~50题 3.1 题目 3.2 答案 四.51~60题 4.1 题目 4.2 答案 一.前情回顾 [人工智能 & 机器 ...

  4. LeetCode《算法入门》刷题笔记(31 题全)

    LeetCode<算法入门>刷题笔记(31 题全) 二分查找 1. 二分查找 _解法1:二分搜索(迭代) 解法2:二分搜索(递归) 2. 第一个错误的版本 _解法1:二分 3. 搜索插入位 ...

  5. JAVA经典算法40题

    JAVA经典算法40题 [程序1]  题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析 ...

  6. java求公式例题_JAVA经典算法40题

    1: JAVA经典算法40题 2: [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 3 ...

  7. 十、分享一道LeetCode较为简单的单链表题,但是却能激发起练习算法的极大的兴趣

    实现一种算法,找出单向链表中倒数第 k 个节点.返回该节点的值 1.今天在LeetCode练习一道单链表的题(题目如上),虽然不难,但是却极大的触动了我学习算法的兴趣.因为当你看到大神的解法时,你真的 ...

  8. LeetCode 148. Sort List--面试算法题--C++,Python解法

    LeetCode 148. Sort List–面试算法题–C++,Python解法 LeetCode题解专栏:LeetCode题解 LeetCode 所有题目总结:LeetCode 所有题目总结 大 ...

  9. leetcode旋转数组 c语言,leetcode explore 初级算法第三题,旋转数组代码实现

    leetcode explore 初级算法第三题,旋转数组代码实现.原题链接: 题目分析 因为题目不是很长,这里把题目贴出来: 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. ...

最新文章

  1. 超越YOLOv5,1.3M超轻量,高效易用,这个目标检测开源项目太香了!
  2. 为什么数学家、统计学家和机器学习专家会用不同方式解决问题?
  3. angular监听图片加载完成_angular1.0 如何监听页面渲染完毕 (转)
  4. jquery 导航栏目
  5. 破解xp(sp2)密码
  6. 功能Java示例 第8部分–更多纯函数
  7. php 文件 后缀,php如何修改文件后缀名
  8. linux上部署javaWeb项目
  9. 少数人知道的公式+思路,财务报表自动生成,财务总监用了5年
  10. 假设检验——抽样调查的结论依赖于样本量的大小
  11. 查看显卡显存_选购显卡必须知道的五大参数及分类推荐购买显卡
  12. TLS协议、PKI、CA
  13. appium环境搭建python_appium环境搭建python
  14. distpicker实现省市级联动
  15. 20190303-AJAX教程
  16. 常见动词的过去式和过去分词
  17. Welcome to MySQL Workbench:MySQL 复制表
  18. 南佛罗里达大学计算机科学硕士,去南佛罗里达大学读硕士好吗
  19. 【第一组】第十一次例会纪要
  20. Not creating XLA devices, tf_xla_enable_xla_devices not set

热门文章

  1. 【动态规划】开心的小明
  2. 68.营救问题(广搜)
  3. C - 查找回文质数
  4. Robot Framework连接MySQL数据库
  5. Bootstrap-CL:按钮下拉菜单
  6. 【DotNet加密方式解析】-- 好文收藏
  7. 经典排序算法学习笔记七——堆排序
  8. 以生活例子说明单线程与多线程
  9. 【恋上数据结构】基数排序、桶排序、休眠排序
  10. Python3 Time 模块详解 import time