数组

191 阅读2分钟

数组查找

剑指office 数组查找

image.png 解法一,暴力求解

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
};

斐波那契数列

剑指office

image.png 定义 斐波那契数列指的是这样一个数列: 这个数列从第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
};

转圈圈

牛客

image.png 注意边界问题

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
};

寻找众数

寻找众数

image.png 经典的统计个数问题,运行时间: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
};

升序数组中次数

升序数组

image.png 双指针思想

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
};

升序数组两数之和

牛客链接 image.png 双指针

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
};

重复数字

牛客链接

image.png

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
};

构建乘积数组

牛客链接

image.png

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
};

程序员面试宝典刷题序号

image.png

旋转矩阵

程序员面试宝典链接 image.png 思路: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 
        }
    }
};

零矩阵

程序员面试金典链接

image.png

/**
 * @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)
};

两数之和

leetcode 1

image.png 双指针

/**
 * @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

leetcode

image.png 解法一、双指针

  • 左指针从左往右遍历,右指针从右往左遍历
  • 当左指针指向的值大于右指针时,令计算右指针的值,并令右指针往左移动
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