数组查找
解法一,暴力求解
function Find(target, array)
{
// write code here
let ishave = false
for(let i = 0; i<array.length; i++){
if(ishave) break
for(let j = 0; j<array[0].length; j++){
if(array[i][j] === target){
ishave = true
break
}
}
}
return ishave
}
module.exports = {
Find : Find
};
解法二,因为是有序的所以可以用双指针的思想,先判断第一行最后一个元素,如果大于目标值,则目标值位于左侧,所以令c--,如果小于目标值则i--,如果等于直接输出
function Find(target, array)
{
// write code here
let i = 0,c = array[0].length -1
let ishave = false
while(i < array.length && c >=0){
if(array[i][c] < target){
i++
}else if(array[i][c] === target){
return ishave = true
}else if(array[i][c] > target){
c--
}
}
return ishave
}
module.exports = {
Find : Find
};
斐波那契数列
定义
斐波那契数列指的是这样一个数列:
这个数列从第3项开始,每一项都等于前两项之和。
我写的解,递归
function Fibonacci(n)
{
// write code here
// 0 1 1 2 3
let arr = [0,1,1]
let i = 3
if(n <3){
return arr[n]
}
function num (i,n) {
arr[i] = arr[i-1] + arr[i-2]
if(i===n){
return arr[i]
}else{
i++
return num(i,n)
}
}
return num(i,n)
}
module.exports = {
Fibonacci : Fibonacci
};
法一、递归简化版,用动态规划节省空间,此处用单个变量的空间复杂度为O(1),时间复杂度为O(n),如果用数组空间复杂度则为O(3)或者O(n)
function Fibonacci(n)
{
// write code here
// 0 1 1 2 3
if(n === 0 || n === 1) return n
let dp0 = 0, dp1 = 1, dp2
for(let i = 2; i <= n; i++){
dp2 = dp0 + dp1
dp0 = dp1
dp1 = dp2
}
return dp2
}
module.exports = {
Fibonacci : Fibonacci
};
转圈圈
注意边界问题
function printMatrix(matrix)
{
// write code here
let J = 0
let arr = []
function sum (arr,matrix) {
let i = J
let length = matrix[0].length - 1 -J
let iLength = matrix.length - 1 - J
for(let j = J;j < length + 1; j++) {
arr.push(matrix[i][j])
}
for(i = i + 1 ;i < iLength + 1; i++) {
arr.push(matrix[i][length])
}
for(i = length - 1; i > J - 1 && iLength - 1 - J> -1; i--){
arr.push(matrix[iLength][i])
}
for(let j = iLength - 1 ; j > J && length - 1 - J > -1 ; j--) {
arr.push(matrix[j][J])
}
if( length >= iLength && J < matrix.length/2 - 1){
J++
return sum(arr,matrix)
}else if(length < iLength && J < matrix[0].length/2 - 1){
J++
return sum(arr,matrix)
}else{
return arr
}
}
return sum(arr,matrix)
}
module.exports = {
printMatrix : printMatrix
};
寻找众数
经典的统计个数问题,运行时间:73ms超过26.58%,占用内存:10360KB超过11.91%
function MoreThanHalfNum_Solution(numbers)
{
// write code here
let Max = 0
let numName = numbers[0]
let obj = {}
numbers.forEach(item => {
if(obj[item]){
obj[item] = obj[item] + 1
}else {
obj[item] = 1
}
if(Max < obj[item]){
Max = obj[item]
numName = item
}
})
return numName
}
module.exports = {
MoreThanHalfNum_Solution : MoreThanHalfNum_Solution
};
法二、排序版
function MoreThanHalfNum_Solution(numbers)
{
// write code here
numbers = numbers.sort((a,b) => a -b)
return numbers[Math.floor(numbers.length/2)]
}
module.exports = {
MoreThanHalfNum_Solution : MoreThanHalfNum_Solution
};
升序数组中次数
双指针思想
function GetNumberOfK(data, k)
{
// write code here
let count = 0
let i = 0
for( ; i < data.length; i++) {
if(data[i] === k) break
}
for(; i < data.length; i++) {
if(data[i] !== k) break
count++
}
return count
}
module.exports = {
GetNumberOfK : GetNumberOfK
};
升序数组两数之和
牛客链接
双指针
function FindNumbersWithSum(array, sum)
{
// write code here
let res = []
for(let i = 0; i < array.length; i++) {
for(let j = i + 1; j < array.length; j++){
if(array[i] + array[j] > sum) break
if(array[i] + array[j] === sum) {
if(res.length === 0) {
res[0] = array[i]
res[1] = array[j]
break
}else{
if(res[0]*res[1] > array[i]*array[j]){
res[0] = array[i]
res[1] = array[j]
break
}
}
}
}
}
return res
}
module.exports = {
FindNumbersWithSum : FindNumbersWithSum
};
重复数字
function duplicate( numbers ) {
// write code here
let num = -1
numbers = numbers.sort((a , b) => b - a)
for(let i = 0; i < numbers.length; i++) {
if(numbers[i] < 0 || numbers[i] > numbers.length - 1) return -1
if(num === numbers[i]) return num
num = numbers[i]
}
return -1
}
module.exports = {
duplicate : duplicate
};
构建乘积数组
function multiply(array)
{
// write code here
let arrayb = []
for(let i = 0; i < array.length; i++){
let tem = array[i]
let res = 1
array[i] = 1
for(let j = 0; j < array.length; j++) {
res = res * array[j]
}
arrayb[i] = res
array[i] = tem
}
return arrayb
}
module.exports = {
multiply : multiply
};
程序员面试宝典刷题序号
旋转矩阵
程序员面试宝典链接
思路:1.沿着行的中线交换每一行 2. 然后沿对角线交换每个数
重要思想: 只计算对角线一侧可利用双重for循环,令j < i即可
/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/
var rotate = function(matrix) {
let n = matrix.length
for(let i = 0; i < Math.floor(n/2); i++) {
for(let j = 0; j < n; j++) {
let tmp = matrix[i][j]
matrix[i][j] = matrix[n - i - 1][j]
matrix[n - i - 1][j] = tmp
}
}
for(let i = 0; i < n; i++) {
for(let j = 0 ; j < i; j++) {
let tmp = matrix[i][j]
matrix[i][j] = matrix[j][i]
matrix[j][i] = tmp
}
}
};
零矩阵
/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/
var setZeroes = function(matrix) {
let tmp =[]
for(let i = 0; i < matrix.length; i++) {
for(let j = 0; j < matrix[0].length; j++) {
if(matrix[i][j] === 0) {
tmp.push([i,j])
}
}
}
// console.log(tmp)
function clear(tmp) {
if(tmp.length === 0) return
let arr = tmp.pop()
for(let i = 0; i < matrix.length; i++) {
matrix[i][arr[1]] = 0
}
for(let j = 0; j < matrix[0].length; j++) {
matrix[arr[0]][j] = 0
}
if(tmp.length !== 0) {
clear(tmp)
}
}
clear(tmp)
};
两数之和
双指针
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for(let i = 0; i < nums.length; i++) {
for(let j = i + 1; j < nums.length; j++) {
if(nums[i] + nums[j] === target) return [i,j]
}
}
};
法二、利用map将对应的值存为key,将索引存为value,在遍历时判断map中有没有对应的目标值减去当前数组的差值
var twoSum = function(nums, target) {
let map = new Map()
for(let i = 0; i < nums.length; i++) {
if(map.has(target - nums[i])) {
return [i, map.get(target - nums[i])]
}else {
map.set(nums[i], i)
}
}
};
承最多的水
leetcode
给你 n 个非负整数 a1,a2,...,a``n,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明: 你不能倾斜容器。
示例 1:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
解释: 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
法一、遍历查找,时间复杂度为o(n^2),超出时间限制
var maxArea = function(height) {
let max = 0
for(let i = 0; i < height.length; i++ ) {
for(let j = i + 1; j < height.length; j++) {
let tmp = Math.min(height[i],height[j])
if(tmp*(j - i) > max) {
max = tmp*(j - i)
}
}
}
return max
};
法二、双指针思想,因为是两端中最短边*宽度,所以就算里面有长度非常高的边也不会影响结果,可以每次移动两个指针中,较短的一条边,直到左指针大于有指针
var maxArea = function(height) {
let L = 0
let R = height.length - 1
let max = 0
while(L < R) {
let tmp = Math.min(height[L],height[R])*(R - L)
if(tmp > max) {
max = tmp
}
if(height[L] >= height[R]) {
R--
}else {
L++
}
}
return max
};
接雨水 hard
解法一、双指针
- 左指针从左往右遍历,右指针从右往左遍历
- 当左指针指向的值大于右指针时,令计算右指针的值,并令右指针往左移动
var trap = function(height) {
let l = 0
let r = height.length - 1
let lmax = 0
let rmax = 0
let ans = 0
while(l < r) {
if(height[l] > height[r]) {
if(rmax < height[r]) {
rmax = height[r]
}else {
ans += rmax - height[r]
}
r--
}else {
if(lmax < height[l]) {
lmax = height[l]
} else {
ans += lmax - height[l]
}
l++
}
}
return ans
};
法二、暴力解法
- 根据题目意思,分别算出当前位置左边和右边的最大值
- 取左右两边最大值的最小值
- 如果当前的值小于最小值,说明可以存水,用最小值-当前值
var trap = function(height) {
let ans = 0
for(let i = 0; i < height.length; i++) {
let lmax = 0
let rmax = 0
for(let j = i - 1; j > -1; j--) {
lmax = Math.max(lmax, height[j])
}
for(let j = i + 1; j < height.length; j++) {
rmax = Math.max(rmax, height[j])
}
if(Math.min(lmax,rmax) > height[i]) {
ans += Math.min(lmax,rmax) - height[i]
}
}
return ans
};
总结
- 空间优化,对于类似前后递归相加的问题,要注意是否可以优化空间,如果再算完之后,前面的数已经没有作用,那么实际上只需要三个变量的空间,用后面的覆盖前面的变量
- 双指针思想,对于二维或者一维数组可以考虑双指针,二维上可以是行指针和列指针,一维上可以是前后,或者一个指针再当前位置,另一个指针往后寻找符合条件的解
- 边界问题可以在两侧增加不影响结果的数据
- 计算对角线时可双重循环,令j < i