探索部分 数组与字符串
数组
1 寻找数组的中心索引: 给定一个整数类型的数组 nums,请编写一个能够返回数组 “中心索引” 的方法。 我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,那么我们应该返回-1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
function sum (arr) {
var nsum = 0;
for (var i = 0; i < arr.length; i++) {
nsum += arr[i]
}
return nsum;
}
var pivotIndex = function (nums) {
if (nums instanceof Array && nums.length != 0) {
if (sum(nums.slice(1)) == 0)
return 0;
for (var i = 1; i < nums.length - 1; i++) {
var lsum = sum(nums.slice(0, i));
var rsum = sum(nums.slice(i + 1));
if (lsum == rsum)
return i;
}
if (sum(nums.slice(0, nums.length - 1)) == 0)
return nums.length - 1;
}
return -1;
}
2 搜索插入位置:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
var searchInsert = function (nums, target) {
var i = 0;
if (nums instanceof Array && nums.length === 0) {
return 0;
}
while (i < nums.length) {
if (nums[i] >= target) {
return i;
}
i++;
}
return nums.length
};
3 合并区间:给出一个区间的集合,请合并所有重叠的区间。
var merge = function (intervals) {
if (intervals.length <= 1) {
return intervals
}
// 先将数组按照区间最左边的大小顺序排序
let arr = intervals.sort((a, b) => a[0] - b[0]);
function unite (arr, i) {
if (i == arr.length - 1) {
return arr
}
// 如果下一个区间的左区间在本区间之间,则合并一次
if (arr[i][0] <= arr[i + 1][0] && arr[i + 1][0] <= arr[i][1]) {
arr[i] = [
Math.min(arr[i][0], arr[i + 1][0]),
Math.max(arr[i][1], arr[i + 1][1])
];
// 合并之后删除冗余区间
arr.splice(i + 1, 1);
} else {
// 如果没有合并,则找到下一个待合并区间
i++;
}
return unite(arr, i)
}
return unite(arr, 0)
};
先对数组进行排序,再考虑合并的判断,用到递归的思想。
7.22
二维数组
4 旋转矩阵:给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4字节。请你设计一种算法,将图像旋转 90 度。不占用额外内存空间能否做到?
var rotate = function (matrix) {
let dd = []
dd = matrix.map((val, matrixIndex) => {
let arr = []
for (let i = val.length - 1; i >= 0; i--) {
// 第一行等于第一列,以此类推
arr.push(matrix[i][matrixIndex])
}
return arr
})
console.log(dd)
matrix.forEach((val, index) => {
matrix[index] = dd[index]
})
};
map方法会遍历第二个参数,返回结果拼接为一个数组;forEach方法会改变传入参数的值
5 零矩阵:编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
var setZeroes = function (matrix) {
let col = []
for (let i = 0; i < matrix.length; i++) {
let row = null
for (let j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 0) {
col.push(j);
row = i;
}
}
if (row != null) {
for (let j = 0; j < matrix[i].length; j++) {
matrix[i][j] = 0
}
}
}
console.log(col)
for (let i = 0; i < matrix.length; i++) {
for (let j of col) {
matrix[i][j] = 0;
}
}
};
双重循环遍历
7.23
6 对角线遍历:给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素。
var findDiagonalOrder = function (matrix) {
if (matrix.length < 1) {
return []
}
var m = matrix.length;
var n = matrix[0].length;
var arr = []
for (var x = 0; x < n; x++) {
sum = x;
var subarr = []
for (var y = x; y > -1; y--) {
subarr.push(matrix[sum - y][y])
if (sum - y === m - 1) break;
}
if (x % 2 === 0) {
subarr = subarr.reverse()
}
arr.push(subarr)
}
for (var y = 1; y < m; y++) {
sum = y + n - 1;
var subarr = [];
for (var x = y; x < m; x++) {
subarr.push(matrix[x][sum - x])
if (x === m - 1|| sum - x === 0) break;
}
if ((n % 2 === 0 && y % 2 === 1) || (n % 2 === 1 && y % 2 === 0)) {
subarr = subarr.reverse()
}
arr.push(subarr)
}
arr = arr.flat(Infinity)
return arr
}
字符串
7 最长公共前缀:编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。
var longestCommonPrefix = function (strs) {
var comstr = ''
if (strs.length >= 1) {
comstr = strs[0]
for (var i = 1; i < strs.length; i++) {
if (strs[i] === '')
return ''
for (var j = 0; j < strs[i].length; j++) {
if (strs[i].length < comstr.length) {
comstr = comstr.slice(0, strs[i].length) // 保证公共前缀长度最短
}
if (comstr[j] != strs[i][j]) { //遇到不相等的字母,截掉后面的部分
comstr = comstr.slice(0, j)
break
}
}
}
}
return comstr
};
8 翻转字符串里的单词:给定一个字符串,逐个翻转字符串中的每个单词。
var reverseWords = function (s) {
if (s.length <= 1) {
return s.trim()
}
var arr = s.trim().split(/\s+/) //用正则表达式匹配一到多个空格 trim()去掉首尾空格
var arr1 = []
var arr2 = ''
for (var i = arr.length - 1; i > -1; i--) { //从后往前遍历,逐个添加到新的数组中
arr1.push(arr[i])
}
arr2 = arr1.join(" ") //join()将数组内的字符串用空格连接起来
return arr2
};
双指针技巧
9 反转字符串:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
var reverseString = function (s) {
let i = 0
let j = s.length - 1 //双指针的方法 交换两个字符
while (i < j) {
var temp = s[j]
s[j] = s[i]
s[i] = temp
i++
j--
}
return s
}
10 数组拆分 I:给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。
var arrayPairSum = function (nums) {
if (nums.length === 0) {
return 0
}
nums = nums.sort((a, b) => a - b) //给数组原地排序
let sum = 0
for (let i = 0; i < nums.length; i++) { //所有下标为偶数的项的和即为所求
if (i % 2 === 0)
sum += nums[i]
}
return sum
};
11 两数之和 II - 输入有序数组:给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
var twoSum = function (numbers, target) {
var map = new Map() //用到map对象
let result = []
for (let i = 0, len = numbers.length; i < len; i++) {
if (map.has(numbers[i])) { //如果找到了差值,就找到需要这个差值的那一项的值,即index1 当前下标加一是index2
result.push(map.get(numbers[i]))
result.push(i + 1)
}
map.set(target - numbers[i], i + 1) //把这一项的与目标的差作为键,下标加1作为值
}
return result
};
map.set(key,value) 设置map中的键值对
map.get(key) 返回键为key的value值
map.has(key),boolean值,判断map中是否有键为key的键值对
7.27
12 移除元素:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
var removeElement = function (nums, val) {
if (nums.length < 1) {
return 0
}
let j = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] != val) {
nums[j] = nums[i];
j++;
console.log(j)
}
}
return j;
};
13 最大连续1的个数:给定一个二进制数组, 计算其中最大连续1的个数。
var findMaxConsecutiveOnes = function (nums) {
if (nums.length === 0) {
return 0;
}
let max = 0;
let j = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 1) {
j++;
}
max = max > j ? max : j
if (nums[i] != 1) {
j = 0
}
}
return max
};
14 长度最小的子数组:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
(1)
var minSubArrayLen = function (s, nums) {
if (nums.length < 1) {
return 0;
}
let acc = 0;
acc = nums.reduce((a, b) => { a + b }, 0)
if (acc < nums) return 0
let sublen = nums.length;
for (let i = 0; i < nums.length; i++) {
let count = 0;
let sum = 0;
for (j = i; j < nums.length; j++) {
sum += nums[j];
count++;
if (sum >= s) {
sublen = sublen < count ? sublen : count;
break;
}
}
}
return sublen;
};
(2)
var minSubArrayLen = function (s, nums) {
if (nums.length === 0) {
return 0;
}
let acc = 0;
let min = null;
let slideWindow = [];
for (let i = 0; i < nums.length + 1; i++) {
const num = nums[i];
while (acc >= s) {
if (min === null || min > slideWindow.length) {
min = slideWindow.length;
}
acc = acc - slideWindow.shift();
}
slideWindow.push(num);
acc = slideWindow.reduce((a, b) => a + b, 0)
}
return min || 0
};
第二种方法是滑动窗口的方法,适合求连续字串的问题。reduce()方法可以用来求数组的和。
小结
14 杨辉三角:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
var generate = function (numRows) {
let arr = [];
if (numRows) { //如果numsRows不等于0的时候,把第一行添加进去,否则就是空数组
arr.push([1]);
}
for (let i = 1; i < numRows; i++) { //从第二行开始计算
let item = [];
let lastitem = arr[arr.length - 1]; //每次取上一个数组项
for (let j = 0; j < lastitem.length - 1; j++) {
item.push(lastitem[j] + lastitem[j + 1]) //计算和,添加到新的数组中
}
item.unshift(1);
item.push(1);
arr.push(item);
}
return arr
};
15 杨辉三角2:给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
var getRow = function (rowIndex) {
let arr = [];
arr.push([1]);
for (let i = 1; i < rowIndex + 1; i++) { //最后一次循环计算rowIndex行
let item = [];
let lastitem = arr[arr.length - 1]; //每次取上一个数组项
for (let j = 0; j < lastitem.length - 1; j++) {
item.push(lastitem[j] + lastitem[j + 1]) //计算和,添加到新的数组中
}
item.unshift(1);
item.push(1);
arr.push(item);
}
return arr[rowIndex] //返回rowIndex行
};
优化到O(k)空间
var getRow = function (rowIndex) {
let item = [1];
for (let i = 0; i < rowIndex; i++) { //最后一次循环计算rowIndex行
for (let j = 0; j < item.length - 1; j++) {
item[j] = item[j] + item[j + 1]
}
item.unshift(1)
}
return item //返回行
};
16 反转字符串中的单词 III:给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
var reverseWords = function (s) {
let arr = s.split(/\s+/); //先将字符串按单词分割成数组
for (let i = 0; i < arr.length; i++) {
let arr2 = arr[i].split("") //将每个单词分割成数组
arr2.reverse() //翻转
arr[i] = arr2.join("") //再拼接成一个单词
}
s = arr.join(" ")
return s
};
17 寻找旋转排序数组中的最小值:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。你可以假设数组中不存在重复元素。
var findMin = function (nums) {
if (nums.length === 0) {
return []
}
for (let i = 0; i < nums.length - 1; i++) {
if (nums[i] > nums[i + 1]) { //如果后一个比当前值小,就找到了
return nums[i + 1]
}
}
return nums[0]
};
18 删除排序数组中的重复项:给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
var removeDuplicates = function (nums) {
let arr = nums.reduce((init, current) => {
if (init.length === 0 || init[init.length - 1] != current) {
init.push(current)
}
return init
}, [])
arr.forEach((val, index) => {
nums[index] = arr[index];
});
return arr.length
};
19 移动零:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
var moveZeroes = function (nums) {
//快慢指针
let count = 0
for (let i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
if (count != i) {
nums[count] = nums[i];
nums[i] = 0;
}
count++;
}
}
};
20 :最长回文子串(动态规划)