1.找到数组的中间位置
给你一个下标从 0 开始的整数数组 nums ,请你找到 最左边 的中间位置 middleIndex
(也就是所有可能中间位置下标最小的一个)。中间位置 middleIndex 是满足 nums[0] + nums[1] + … + nums[middleIndex-1] ==
nums[middleIndex+1] + nums[middleIndex+2] + … + nums[nums.length-1]
的数组下标。如果 middleIndex == 0 ,左边部分的和定义为 0 。类似的,如果 middleIndex == nums.length -
1 ,右边部分的和定义为 0 。请你返回满足上述条件 最左边 的 middleIndex ,如果不存在这样的中间位置,请你返回 -1 。
leetcode 出处和题解,我觉得这个题解非常好,就直接用了。
https://leetcode-cn.com/problems/find-the-middle-index-in-array/solution/zhao-dao-shu-zu-de-zhong-jian-wei-zhi-by-s8cy/
1.思想

2.JS代码
var findMiddleIndex = function(nums) {
let total = nums.reduce(a, b => a+b ,0)
let sum = 0;
for (i = 0; i < nums.length; i++) {
if (total == 2 * sum + nums[i])
{
return i
}
sum += nums[i]
}
return -1
};
2.用两个栈实现一个队列。
队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
2.1 思想
1 )掌握栈的基本概念, 栈:先进后出;队列:先进先出。
2 )掌握JS数组处理方法
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度
2.2 JS代码
// 栈:先进后出;队列:先进先出
//队列 unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
var CQueue = function() {
this.inStack = [];
this.outStack = [];
};
// push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度
CQueue.prototype.appendTail = function(value) {
this.inStack.push(value);
};
CQueue.prototype.deleteHead = function() {
if (!this.outStack.length) {
if (!this.inStack.length) {
return -1;
}
this.in2out();
}
return this.outStack.pop();
};
CQueue.prototype.in2out = function() {
while (this.inStack.length) {
this.outStack.push(this.inStack.pop());
}
};
3.给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
示例 1: 输入:s = “()” 输出:true
示例 2: 输入:s = “()[]{}” 输出:true
示例 3: 输入:s = “(]” 输出:false
3.1 思想
到有括号的题,第一反应就要是栈
- 先为所有类型的括号做匹配,如果出现’(’ , 就把一个 “)”放入栈。
- 如果栈的长度为0 则说明没有括号,返回false
- 括号都放进栈之后,把输入的值和栈进行匹配,不相等则返回false。
3.2 JS代码
var isValid = function(s) {
const stack = [];
for (let val of s){
if(val === '('){
stack.push(')')
}
else if(val === '[')
{
stack.push(']')
}
else if(val === '{')
{
stack.push('}')
}
else if(stack.length === 0 || val !== stack.pop() ){ return false }
}
return stack.length === 0 ;
};
4.将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
4.1 思想
递推的方式
- 定义一个空链表用来存储最后的结果。
- 先比较两个链表的第一个节点,把小的先存到空链表中,依次比较。
- 两个链表长度不一,当有一个链表为空时,直接将后面的数值存到存储列表中。
- 将拼接后的链表返回。
4.2 JS代码
var mergeTwoLists = function(list1, list2) {
// 空链表
const l6 = new ListNode();
let current = l6;
while(list1 && list2) {
// 将current.next 指向最小的一个链表
if(list1.val < list2.val ){
current.next = list1;
list1 = list1.next
}
else{
current.next = list2;
list2 = list2.next;
}
// 当前的链表向后位移
current = current.next;
}
// 处理剩下的
if(list1 === null){
current.next = list2;
}
if(list2 === null){
current.next = list1;
}
return l6.next;
};
5.数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3 输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]
示例 2:输入:n = 1 输出:[“()”]
5.1 思想
根据题目,想到了深度优先搜索(DFS),首先确定边界,
当 字符串长度为 2*n的时候,括号的数量为偶数,即匹配成功。就可以把匹配的的字符串返回。
尝试可能的状态,以左括号“( ” 判断是否有剩余,如果有则继续进行选择。如果左括号比右括号剩的多才能选右括号。这样一遍遍的查找下去,直到满足边界条件。
DFS算法:
function dfs(step)
{
if(边界成立)
{
走到最深处
......
return;
}
for(尝试每一种可能的状态)
{
if(如果这种状态可行){ //剪枝
把这种可能的状态标记,表示走过
继续下一步dfs(step+1) //状态转移
把这种标记去除 //回溯
}
}
}
5.2 JS代码
var generateParenthesis = function(n) {
const res = [];
const dfs = (lRemain, rRemain, str) =>{
if(str.length === 2 * n){
res.push(str)
return;
}
if(lRemain > 0){
dfs(lRemain-1, rRemain, str + '(')
}
// 右括号比左括号剩的多,才能选右括号
if(lRemain < rRemain){
dfs(lRemain, rRemain-1, str + ')')
}
};
dfs(n, n, '')
return res;
};
6.给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。
在构造过程中,请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。
示例 1:
输入:s = “abccccdd” 输出:7 解释: 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
示例 2:输入:s = “a” 输入:1
示例 3:输入:s = “bb” 输入: 2
6.1 思想
利用JS的中map方法,存储字符串分割后的数组。
遍历数组中的索引,用map的has 方法判断数组中是否有重复的字符。如果有删除此字符,计数加2.如果没有,就把这个字符放到map中。
最后,如果计数值小于字符串的长度,那么说明至少有一个字符串单个出现。而在回文字符串中,单个出现的字符只能有一个,所以利用长度对比来判断,count 是否应该+1.
6.2 JS代码
var longestPalindrome = function(s) {
let map = new Map();
let count = 0;
let arr = s.split('');
for (let val of arr){
if(map.has(val)){
map.delete(val);
count += 2;
}
else{
map.set(val)
}
}
return count < arr .length ? count + 1 : count;
};
7.在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例 1:
输入:s = “abaccdeff” 输出:‘b’ 示例 2:
输入:s = “” 输出:’ ’
7.1 思想
巧用JS的indexOf() 、 lastIndexOf() 判断字符串是否有重复的值出现。有就返回。没有就退出循环,返回‘ ’ .
7.2 JS代码
var firstUniqChar = function(s) {
for (let i of s){
if(s.indexOf(i)===s.lastIndexOf(i)) return i;
}
return ' '
};
8.给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2
示例 2:输入: nums = [1,3,5,6], target = 2 输出: 1
示例 3:输入: nums = [1,3,5,6], target = 7 输出: 4
8.1 思想
二分法的思想,其中二分法有两种形式:
①左闭右开[ left , right )
当左闭右开时, left = right 无意义,所以条件是while(left < right)
查找的区间确定了值的范围,如果值不在范围内,就返回left,因为是左闭右开的,以为基准 left开始查找的。
while(left < right)的终止条件是left == right,写成区间的形式就是[left, right],或者带个具体的数字进去[2, 2],这时候区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间[2, 2]被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。
②左闭右闭 [ left , right ]
左闭右闭, left = right 有意义,所以条件是while(left <= right) ,判断值是否在查找区间里,如果在就返回。如果不在,就以right为基准判断是小于范围内的值还是大于范围内的值,返回的是right + 1是处理插入值的四种情况:
目标值在数组所有元素之前 [0, -1]
目标值等于数组中某一个元素 return middle;
目标值插入数组中的位置 [left, right],return right + 1
目标值在数组所有元素之后的情况 [left, right], return right + 1
while(left <= right)的终止条件是left == right + 1,写成区间的形式就是[right + 1, right],或者带个具体的数字进去[3, 2],可见这时候区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。
8.2 JS代码
左闭右开的二分法:
找到 target 的最左侧索引,要收紧右侧边界以锁定左侧边界。
var searchInsert = function(nums, target) {
let right = nums.length ;
let left = 0;
while(left < right){
let middle = left +((right - left) >> 1);
if(nums[middle] > target){
right = middle;
} else if (nums[middle] < target) {
left = middle + 1;
} else { return middle; }
}
// 收左边界
return left;
};
左闭右闭的二分法:
var searchInsert = function(nums, target) {
let right = nums.length - 1;
let left = 0;
while(left <= right){
let middle = left +((right - left) >> 1);
if(nums[middle] > target){
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else { return middle; }
}
return right + 1;
};
9. 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4
示例 2:输入: nums = [-1,0,3,5,9,12], target = 2 输出: -1 解释: 2 不存在 nums 中因此返回 -1
9.1 思想
二分法的要点:① 判断执行条件,②判断区间取值范围, ③判断可能取值的结果
9.2 JS代码
①左闭右开:
var search = function(nums, target) {
let left = 0;
let right = nums.length;
// ① 判断执行条件
while (left < right){
let middle = left + ((right - left) >> 1);
// ②判断区间取值范围
if (nums[middle] > target) {
right = middle;
} else if (nums[middle] < target) {
left = middle + 1;
} else {
return middle;
}
}
// ③判断可能取值的结果
return -1;
};
②左闭右闭
var search = function(nums, target) {
let left = 0;
let right = nums.length - 1;
// ① 判断执行条件
while (left <= right){
let middle = left + ((right - left) >> 1);
// ②判断区间取值范围
if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else {
return middle;
}
}
// ③判断可能取值的结果
return -1;
};
10.在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4] 示例 2:
输入:nums = [5,7,7,8,8,10], target = 6 输出:[-1,-1] 示例 3:
输入:nums = [], target = 0 输出:[-1,-1]
10.1 思想
二分法,经过了上面两题,我们可以大概知道
①左闭右闭:若初始化 right = nums.length - 1,则「搜索区间」是 [left, right]
则 while (left <= right),且 left = mid+1 和 right = mid-1 。
只需找到一个 target 的索引,当 nums[mid] == target 时可以立即返回 return middle;
②左闭右开:若初始化 right = nums.length,则「搜索区间」是 [left, right),则 while (left < right),且 left = mid + 1 和 right = mid
10.2 JS代码
var searchRange = function(nums, target) {
const getRightBorder = function(nums, target) {
let rightBorder = -2;
let left = 0;
let right = nums.length - 1;
// while(left <= right)的终止条件是left == right + 1,
//写成区间的形式就是[right + 1, right]
while (left <= right) {
let middle = left + ((right - left) >> 1);
// 如果 目标 大于范围 收右边界 [right - 1, right]
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界,nums[middle] == target的时候更新left
// [right + 1, right]
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
};
const getLeftBorder = function(nums, target) {
let leftBorder = -2;
let left = 0;
let right = nums.length - 1;
while (left <= right) {
let middle = left + ((right - left) >> 1)
if(nums[middle] >= target){ // 寻找左边界,nums[middle] == target的时候更新right
// [right - 1, right]
right = middle - 1;
leftBorder = right;
} else {
// 收紧左侧边界时必须 left = mid + 1
// 所以最后无论返回 left 还是 right,必须减一
left = middle + 1;
}
}
return leftBorder;
}
let rightBorder = getRightBorder (nums, target);
let leftBorder = getLeftBorder (nums, target);
if (leftBorder === -2 || rightBorder === -2) return [-1, -1];
if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];
return [-1, -1];
};
简写形式:
var searchRange = function(nums, target) {
let left = 0, right = nums.length - 1, mid;
while (left <= right) {//二分查找target
mid = (left + right) >> 1;
if (nums[mid] === target) break;
if (nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
if(left > right) return [-1, -1];
let i = mid, j = mid;
while(nums[i] === nums[i - 1]) i--;//向左尝试找相同的元素
while(nums[j] === nums[j + 1]) j++;//向右尝试找相同的元素
return [i, j];
};
本文介绍了9个使用JavaScript实现的算法问题,包括找到数组中间位置、用栈实现队列、括号的有效性检查、合并升序链表、生成有效括号、最长回文子串、查找首次出现的字符、二分查找、目标值在排序数组中的位置以及查找目标值的开始和结束位置。每个问题都包含了思路和JS代码实现。
466

被折叠的 条评论
为什么被折叠?



