LintCode 常用算法JS解法小结

507 阅读5分钟

最近准备面试,在lintcode上刷了些简单和中级的算法题,也掌握了一些常用的算法,由于我目前只会js相关的语法,所以解法都是我自己用js去解的,在此留个坑纪念一下,希望能帮到和我一样的初学者或者是同行者。

最大子数组

常规暴力算法 -- 易于理解,复杂度O(n^3)

/**
 * 给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。
 * 
 * @param nums: A list of integers
 * @return: A integer indicate the sum of max subarray
 */
const maxSubArray = function(nums) {
    var l = nums.length;
    var ans = -10000; //初始化值
    for (var i = 0; i < l; i++) {
        for (var j = i; j < l; j++) {
            let sum = 0;
            for (var k = i; k < j; k++) {
                sum += nums[k];
            }
            if (sum > ans) {
                ans = sum;
            }
        }
    }
    return ans;
}
/* 暴力枚举出所有子集合 复杂度n^3 */
let num = [-2, 2, -3, 4, -1, 2, 1, -5, 3]
console.log(maxSubArray(num));

贪心算法 --- 丢弃子串和为负数的子串,只保留正子串O(n)

function maxSubArray1(nums) {
    var l = nums.length;
    var ans = -10000;
    var sum = 0;
    for (var i = 0; i < l; i++) {
        sum += nums[i];
        if (sum > ans) {
            ans = sum;
        }
        if (sum < 0) {
            sum = 0; // 子串和为负数,丢掉
        }
    }
    return ans
}

寻找主元素

/**
 * 给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的1/k。
 * 数组只有唯一主元素
 * 
 * @param nums: A list of integers
 * @param k: An integer
 * @return: The majority number
 */
const majorityNumber = function(nums, k) {
    var i, l = nums.length,
        obj = {};
    var temp = ~~(l * 1 / k); // 中间数
    for (i = 0; i < l; i++) {
        if (nums[i] in obj) {
            obj[nums[i]] += 1;
        } else {
            obj[nums[i]] = 1;
        }
        // 因为是唯一主元素
        if (obj[nums[i]] > temp) {
            return nums[i];
        }
    }
}

搜索二维矩阵

/**
 * 搜索二维矩阵
 * 
 * 复杂度O(log(n)) + O(log(m))
 * 
 * @param matrix: matrix, a list of lists of integers
 * @param target: An integer
 * @return: a boolean, indicate whether matrix contains target
 */
const searchMatrix = function(matrix, target) {
    var row = 0;
    if (matrix.length === 0) {
        return false;
    }
    for (var i = 0; i < matrix.length; i++) {
        let last = matrix[i].pop()
        if (last < target) {
            continue;
        } else if (last === target) {
            return true;
        } else {
            row = i;
            break;
        }
    }
    for (var j = 0; j < matrix[row].length; j++) {
        if (matrix[row][j] === target) {
            return true;
        } else {
            continue;
        }
    }
    return false;
}

搜索二维矩阵 II

/**
 * 搜索二维矩阵 II
 * 
 * 写出一个高效的算法来搜索m×n矩阵中的值,返回这个值出现的次数。
 * 每行中的整数从左到右是排序的。
 * 每一列的整数从上到下是排序的。
 * 在每一行或每一列中没有重复的整数。
 * 
 * @param matrix: A list of lists of integers
 * @param target: An integer you want to search in matrix
 * @return: An integer indicate the total occurrence of target in the given matrix
 */
const searchMatrix1 = function(matrix, target) {
    var i, l = matrix.length,
        num = 0;
    for (i = 0; i < l; i++) {
        var row = matrix[i];
        var j, jl = row.length;
        for (j = 0; j < jl; j++) {
            if (row[j] === target) {
                num += 1;
                break;
            }
        }
    }
    return num;
}

寻找第K大个数

/**
 * 寻找第K大个数
 * 
 * @param n: An integer
 * @param nums: An array
 * @return: the Kth largest element
 */
const kthLargestElement = function(n, nums) {
    if (n === 1) {
        return Math.max.apply(null, nums);
    }

    let i,
        l = nums.length,
        left = [],
        right = [],
        pivot = nums[~~(Math.random() * l)];

    for (i = 0; i < l; i++) {
        if (nums[i] > pivot) {
            right.push(nums[i])
        } else {
            left.push(nums[i])
        }
    }

    if (right.length > n) {
        return kthLargestElement(n, right);
    } else if (right.length === n) {
        return Math.min.apply(null, right);
    } else {
        n = n - right.length;
        return kthLargestElement(n, left);
    }
}

console.log(kthLargestElement(10, [1, 2, 3, 4, 5, 6, 8, 9, 10, 7])); // 1
console.log(kthLargestElement(4, [9, 3, 2, 4, 8])); // 3
/**
 * 给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的1/k。
 * 数组只有唯一主元素
 * 
 * @param nums: A list of integers
 * @param k: An integer
 * @return: The majority number
 */
const majorityNumber = function(nums, k) {
    var i, l = nums.length,
        obj = {};
    var temp = ~~(l * 1 / k); // 中间数
    for (i = 0; i < l; i++) {
        if (nums[i] in obj) {
            obj[nums[i]] += 1;
        } else {
            obj[nums[i]] = 1;
        }
        // 因为是唯一主元素
        if (obj[nums[i]] > temp) {
            return nums[i];
        }
    }
}


console.log(majorityNumber([3, 1, 2, 3, 2, 3, 3, 4, 4, 4], 3));

贪心问题:

钱币找零问题

假设有数目不限的面值为20,10,5,1的硬币。 给出需要找零数,求出找零方案,要求:使用数目最少的硬币

策略:每次选取可供钱币的最大值

/* m 为可供找零的面值倒序数组 eg.[20,10,5,1] n为找零数 */
function greedyMoney(m, n) {
    let result = [];
    for (var i = 0; i < m.length; i++) {
        while (n >= m[i] && n > 0) {
            result.push(m[i])
            n = n - m[i];
        }
    }
    return result;
}

小船过河问题

只有一艘船,能乘2人,船的运行速度为2人中较慢一人的速度,过去后还需一个人把船划回来,问把t个人运到对岸,最少需要多久 假设t个人的速度按升序排列。

策略

  1. 最快的和次快的过河,然后最快的将船划回来;次慢的和最慢的过河,然后次快的将船划回来,所需时间为:t[0]+2*t[1]+t[n-1]。
  2. 最快的和最慢的过河,然后最快的将船划回来,最快的和次慢的过河,然后最快的将船划回来,所需时间为:2*t[0]+t[n-2]+t[n-1]。
  3. 每次运行不影响他人,所以具有贪心子结构。解法如下
let array = [1, 2, 3, 4, 5, 6, 7, 9, 12, 15] // 10个人,每个人过河花费时间对应为1,2,...n

function greedyBoat(arr) {
    let total = 0; // 总用时
    while (arr.length > 0) {
        var l = arr.length;
        // 如果人数剩余3人
        if (l === 3) {
            total += arr[0] + arr[1] + arr[2];
            break;
        }
        // 如果人数剩余2人
        if (l === 2) {
            total += arr[1];
            break;
        }
        // 如果人数剩余1人
        if (l === 1) {
            total += arr[0];
            break;
        }
        // 一次过河用时
        total += Math.min.apply(null, [arr[l - 1] + 2 * arr[0] + arr[l - 2], arr[0] + 2 * arr[1] + arr[l - 1]])
        // 同时去除数组末尾两个数字
        arr.length -= 2;
    }
    return total;
}

console.log(greedyBoat(array));