数组排序(快排简版)
js
var sortArray = function (nums) {
if (nums.length <= 1) return nums;
let minList = [];
let maxList = [];
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[0]) maxList.push(nums[i]);
else minList.push(nums[i]);
}
return [...sortArray(minList), nums[0], ...sortArray(maxList)];
};
var sortArray = function (nums) {
if (nums.length <= 1) return nums;
let minList = [];
let maxList = [];
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[0]) maxList.push(nums[i]);
else minList.push(nums[i]);
}
return [...sortArray(minList), nums[0], ...sortArray(maxList)];
};
数组排序(快排标准版)
js
var sortArray = function (nums) {
// 分治递归
function loop(arr, left, right) {
if (left < right) {
const middle = compare(arr, left, right); // 寻找中心并排序大小区间
loop(arr, left, middle - 1); // 排序小区间
loop(arr, middle + 1, right); // 排序大区间
}
}
// 快慢指针区间排序
function compare(arr, left, right) {
let base = left; // 基准中心
let slow = left + 1; // 慢指针
for (let fast = slow; fast <= right; fast++) {
// 快指针
if (arr[fast] < arr[base]) {
// 升序排列
swap(arr, slow, fast);
slow++;
}
}
swap(arr, base, slow - 1);
return slow - 1;
}
// 数组项交换
function swap(arr, a, b) {
[arr[a], arr[b]] = [arr[b], arr[a]];
}
loop(nums, 0, nums.length - 1);
return nums;
};
var sortArray = function (nums) {
// 分治递归
function loop(arr, left, right) {
if (left < right) {
const middle = compare(arr, left, right); // 寻找中心并排序大小区间
loop(arr, left, middle - 1); // 排序小区间
loop(arr, middle + 1, right); // 排序大区间
}
}
// 快慢指针区间排序
function compare(arr, left, right) {
let base = left; // 基准中心
let slow = left + 1; // 慢指针
for (let fast = slow; fast <= right; fast++) {
// 快指针
if (arr[fast] < arr[base]) {
// 升序排列
swap(arr, slow, fast);
slow++;
}
}
swap(arr, base, slow - 1);
return slow - 1;
}
// 数组项交换
function swap(arr, a, b) {
[arr[a], arr[b]] = [arr[b], arr[a]];
}
loop(nums, 0, nums.length - 1);
return nums;
};
数组排序(冒泡)
js
var sortArray = function (nums) {
let len = nums.length;
for (let i = 0; i < len - 1; i++) {
let hasSorted = false;
for (let j = 0; j < len - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
hasSorted = true;
}
}
if (!hasSorted) {
break;
}
}
return nums;
};
var sortArray = function (nums) {
let len = nums.length;
for (let i = 0; i < len - 1; i++) {
let hasSorted = false;
for (let j = 0; j < len - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
hasSorted = true;
}
}
if (!hasSorted) {
break;
}
}
return nums;
};
数组删除
js
function arrayDelete(arr) {
let slow = -1; // 慢指针
let len = arr.length; // 数组长度
for (let i = 0; i < arr.length; i++) {
if (arr[i] === value) {
// 查找到删除项
arr[i] = undefined;
slow = slow === -1 ? i : slow; // 慢指针记录空项索引
len--;
} else if (arr[i] !== value && slow !== -1) {
// 慢指针有值且非删除项
arr[slow] = arr[i];
arr[i] = undefined;
slow++; // 慢指针记录空项索引
}
}
arr.length = len; // 变更新长度
return arr;
}
function arrayDelete(arr) {
let slow = -1; // 慢指针
let len = arr.length; // 数组长度
for (let i = 0; i < arr.length; i++) {
if (arr[i] === value) {
// 查找到删除项
arr[i] = undefined;
slow = slow === -1 ? i : slow; // 慢指针记录空项索引
len--;
} else if (arr[i] !== value && slow !== -1) {
// 慢指针有值且非删除项
arr[slow] = arr[i];
arr[i] = undefined;
slow++; // 慢指针记录空项索引
}
}
arr.length = len; // 变更新长度
return arr;
}
接雨水
js
/**
* 前缀(含自身)最大值 l_max
* 后缀(含自身)最大值 r_max
* 存水条件:Math.min(l_max, r_max) 是否大于 自身
*/
var trap = function (height = []) {
let sum = 0;
let l_max = height[0];
let r_max = height[height.length - 1];
let left = 0;
let right = height.length - 1;
while (left <= right) {
l_max = Math.max(height[left], l_max);
r_max = Math.max(height[right], r_max);
if (l_max < r_max) {
sum += l_max - height[left++];
} else {
sum += r_max - height[right--];
}
}
return sum;
};
/**
* 前缀(含自身)最大值 l_max
* 后缀(含自身)最大值 r_max
* 存水条件:Math.min(l_max, r_max) 是否大于 自身
*/
var trap = function (height = []) {
let sum = 0;
let l_max = height[0];
let r_max = height[height.length - 1];
let left = 0;
let right = height.length - 1;
while (left <= right) {
l_max = Math.max(height[left], l_max);
r_max = Math.max(height[right], r_max);
if (l_max < r_max) {
sum += l_max - height[left++];
} else {
sum += r_max - height[right--];
}
}
return sum;
};
三数之和
js
/**
* 先排序得到非递减数组
* 再遍历,每次固定一个数组,余下两个数字做相向指针运动
* 优化1: arr[i] + arr[n-1] + arr[n-2] 小于 sum,本次无解跳过
* 优化2: arr[i] + arr[i+1] + arr[i+2] 大于 sum,本题无解跳出
* 优化3: i>0 && nums[i]===nums[i - 1], 相同值,已计算,本次跳过
* 查找三数之和
*/
var threeSum = function (nums) {
let target = 0;
// 结果
let res = [];
// 非递减
nums.sort((a, b) => a - b);
// 数组项个数
let n = nums.length;
// 遍历
for (let i = 0; i < n - 2; i++) {
// 相同值跳过 i
if (i > 0 && nums[i] === nums[i - 1]) continue;
// 首位的和小于目标值,则当前项全部情况都小于目标值,跳过 i
if (nums[i] + nums[n - 1] + nums[n - 2] < target) continue;
// 连续的和大于目标值,则所有情况也必然大于目标值,关闭 for
if (nums[i] + nums[i + 1] + nums[i + 2] > target) break;
// 借用两数之和的相向指针思想
const x = nums[i];
let j = i + 1;
let k = n - 1;
// 相向指针运动条件
while (j < k) {
let sum = x + nums[j] + nums[k];
if (sum > target) {
k--;
} else if (sum < target) {
j++;
} else {
res.push([x, nums[j], nums[k]]);
for (j++; j < k && nums[j] === nums[j - 1]; j++);
for (k--; j < k && nums[k] === nums[k + 1]; k--);
}
}
}
return res;
};
/**
* 先排序得到非递减数组
* 再遍历,每次固定一个数组,余下两个数字做相向指针运动
* 优化1: arr[i] + arr[n-1] + arr[n-2] 小于 sum,本次无解跳过
* 优化2: arr[i] + arr[i+1] + arr[i+2] 大于 sum,本题无解跳出
* 优化3: i>0 && nums[i]===nums[i - 1], 相同值,已计算,本次跳过
* 查找三数之和
*/
var threeSum = function (nums) {
let target = 0;
// 结果
let res = [];
// 非递减
nums.sort((a, b) => a - b);
// 数组项个数
let n = nums.length;
// 遍历
for (let i = 0; i < n - 2; i++) {
// 相同值跳过 i
if (i > 0 && nums[i] === nums[i - 1]) continue;
// 首位的和小于目标值,则当前项全部情况都小于目标值,跳过 i
if (nums[i] + nums[n - 1] + nums[n - 2] < target) continue;
// 连续的和大于目标值,则所有情况也必然大于目标值,关闭 for
if (nums[i] + nums[i + 1] + nums[i + 2] > target) break;
// 借用两数之和的相向指针思想
const x = nums[i];
let j = i + 1;
let k = n - 1;
// 相向指针运动条件
while (j < k) {
let sum = x + nums[j] + nums[k];
if (sum > target) {
k--;
} else if (sum < target) {
j++;
} else {
res.push([x, nums[j], nums[k]]);
for (j++; j < k && nums[j] === nums[j - 1]; j++);
for (k--; j < k && nums[k] === nums[k + 1]; k--);
}
}
}
return res;
};
两数之和
js
var twoSum = function (nums, target) {
let obj = { [target - nums[0]]: 0 };
for (let i = 1; i < nums.length; i++) {
if (obj[nums[i]] !== undefined) {
return [obj[nums[i]], i];
}
obj[target - nums[i]] = i;
}
return [];
};
var twoSum = function (nums, target) {
let obj = { [target - nums[0]]: 0 };
for (let i = 1; i < nums.length; i++) {
if (obj[nums[i]] !== undefined) {
return [obj[nums[i]], i];
}
obj[target - nums[i]] = i;
}
return [];
};
买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
js
var maxProfit = function (prices) {
let max_profit = 0; // 历史最大收益
let min_prices = prices[0]; // 历史最小价格
for (let i = 1; i < prices.length; i++) {
let profit = prices[i] - min_prices;
max_profit = Math.max(max_profit, profit);
min_prices = Math.min(min_prices, prices[i]);
}
return max_profit;
};
var maxProfit = function (prices) {
let max_profit = 0; // 历史最大收益
let min_prices = prices[0]; // 历史最小价格
for (let i = 1; i < prices.length; i++) {
let profit = prices[i] - min_prices;
max_profit = Math.max(max_profit, profit);
min_prices = Math.min(min_prices, prices[i]);
}
return max_profit;
};
买卖股票的最佳时机 II
js
var maxProfit = function (prices) {
// 阶段累加收益
let sum = 0;
// 同向双指针
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
if (prices[j] <= prices[i]) {
// 小于等于,原地卖出
break;
} else if (prices[j] > prices[i] && prices[j + 1] > prices[j]) {
// 大于且后序还有更大利润
continue;
} else {
// 见好就收
sum += prices[j] - prices[i];
i = j;
break;
}
}
}
return sum;
};
var maxProfit = function (prices) {
// 阶段累加收益
let sum = 0;
// 同向双指针
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
if (prices[j] <= prices[i]) {
// 小于等于,原地卖出
break;
} else if (prices[j] > prices[i] && prices[j + 1] > prices[j]) {
// 大于且后序还有更大利润
continue;
} else {
// 见好就收
sum += prices[j] - prices[i];
i = j;
break;
}
}
}
return sum;
};
洗牌
js
function shuffle(arr) {
for (let i = 0; i < arr.length; i++) {
// 随机位置
const j = Math.floor(Math.random() * arr.length);
// 元素交换
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
function shuffle(arr) {
for (let i = 0; i < arr.length; i++) {
// 随机位置
const j = Math.floor(Math.random() * arr.length);
// 元素交换
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
括号生成
正整数 n 代表生成括号的对数,请设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
js
// 假设 "("是 1, ")"是 -1,前缀和只能 >= 0
var generateParenthesis = function (n) {
let res = [];
function loop(n, sum, str) {
if (n === 0 || n < sum) {
sum === 0 && res.push(str);
return;
}
if (sum > 0) {
loop(n - 1, sum + 1, str + "(");
loop(n - 1, sum - 1, str + ")");
}
if (sum === 0) {
loop(n - 1, sum + 1, str + "(");
}
}
loop(n * 2, 0, "");
return res;
};
// 假设 "("是 1, ")"是 -1,前缀和只能 >= 0
var generateParenthesis = function (n) {
let res = [];
function loop(n, sum, str) {
if (n === 0 || n < sum) {
sum === 0 && res.push(str);
return;
}
if (sum > 0) {
loop(n - 1, sum + 1, str + "(");
loop(n - 1, sum - 1, str + ")");
}
if (sum === 0) {
loop(n - 1, sum + 1, str + "(");
}
}
loop(n * 2, 0, "");
return res;
};
有效的括号字符串 🌟
leetcode 给你一个只包含三种字符的字符串,支持的字符类型分别是 '('、')' 和 '*'。请你检验这个字符串是否为有效字符串,如果是有效字符串返回 true 。
- '*' 可以被视为单个右括号 ')' ,或单个左括号 '(' ,或一个空字符串。
- 一个空字符串也被视为有效字符串。
js
/**
* 贪心
* ( ( * * ) )
* min + + -/0 -/0 - - 最小待匹配左括号 ( 数量
* max + + + + - - 最大待匹配左括号 ( 数量
*/
var checkValidString = function (s) {
let min = 0; // 待匹配左括号 ( 数量
let max = 0; // 待匹配左括号 ( 数量
for (let i = 0; i < s.length; i++) {
if (s[i] === "(") {
min++;
max++;
} else if (s[i] === ")") {
// 当最小值为 0 时,不应将最小值继续减少,以确保最小值非负。
min = Math.max(min - 1, 0);
max--;
if (max < 0) {
// 任何情况下,未匹配的左括号数量必须非负,因此当最大值变成负数时,
// 说明没有左括号可以和右括号匹配,返回 false
return false;
}
} else {
// 当最小值为 0 时,不应将最小值继续减少,以确保最小值非负。
min = Math.max(min - 1, 0);
max++;
}
}
// 遍历结束时,所有的左括号都应和右括号匹配,
// 因此只有当最小值为 0 时,字符串 s 才是有效的括号字符串。
return min === 0;
};
/**
* 贪心
* ( ( * * ) )
* min + + -/0 -/0 - - 最小待匹配左括号 ( 数量
* max + + + + - - 最大待匹配左括号 ( 数量
*/
var checkValidString = function (s) {
let min = 0; // 待匹配左括号 ( 数量
let max = 0; // 待匹配左括号 ( 数量
for (let i = 0; i < s.length; i++) {
if (s[i] === "(") {
min++;
max++;
} else if (s[i] === ")") {
// 当最小值为 0 时,不应将最小值继续减少,以确保最小值非负。
min = Math.max(min - 1, 0);
max--;
if (max < 0) {
// 任何情况下,未匹配的左括号数量必须非负,因此当最大值变成负数时,
// 说明没有左括号可以和右括号匹配,返回 false
return false;
}
} else {
// 当最小值为 0 时,不应将最小值继续减少,以确保最小值非负。
min = Math.max(min - 1, 0);
max++;
}
}
// 遍历结束时,所有的左括号都应和右括号匹配,
// 因此只有当最小值为 0 时,字符串 s 才是有效的括号字符串。
return min === 0;
};
全排列
leetcode 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
js
var permute = function (nums) {
let res = [];
function loop(arr, used) {
if (arr.length && arr.length === nums.length) {
return res.push(arr);
}
for (let i = 0; i < nums.length; i++) {
const num = nums[i];
if (used[num]) continue;
loop(arr.concat([num]), Object.assign({ [num]: true }, used));
}
}
loop([], {});
return res;
};
var permute = function (nums) {
let res = [];
function loop(arr, used) {
if (arr.length && arr.length === nums.length) {
return res.push(arr);
}
for (let i = 0; i < nums.length; i++) {
const num = nums[i];
if (used[num]) continue;
loop(arr.concat([num]), Object.assign({ [num]: true }, used));
}
}
loop([], {});
return res;
};