算法连载(第四期)

315 阅读2分钟

大家好我是django今天给大家带来的算法题分享有《平方数之和》、《二分法查找》、《两数之和》、《最长递增子序列》以及《归并排序》

平方数之和

要求

给定一个非负整数 c ,你要判断是否存在两个整数 ab,使得 a2 + b2 = c

示例 1:

输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5

示例 2:

输入:c = 3
输出:false

思考

  • 方法一: 最容易想到的是循环 0 ~ a个数硬解,a需要被限制以避免不必要的判断如a * a <= c 这样能减少时间复杂度
  • 方法二:双指针写法, 假设a为最小值b为不大于根号c

实现

/**
 * @param {number} c
 * @return {boolean}
 */
// 方法一
var judgeSquareSum = function(c) {
    for(let a = 0; a * a <= c; a++) {
        const b = Math.sqrt(c - a * a);
        if(b === parseInt(b)) {
            return true;
        }
    }
    return false
};

// 方法二
var judgeSquareSum = function(c) {
    if(c === 0) return true
    let a = 0;
    let b = parseInt(Math.sqrt(c));

    while(a <= b) {
        const sum = a * a + b*b;
        if(sum === c) {
            return true;
        }else if(sum < c) {
            a ++;
        } else {
            b --;
        }
    }
    return false
};

二分法查找

要求

给定一个 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

提示: 你可以假设 nums 中的所有元素是不重复的。 n 将在 [1, 10000]之间。 nums 的每个元素都将在 [-9999, 9999]之间。

思路

  • 取到中间值pivot
  • pivot === target返回true
  • pivot > target那么target就在pivot的左边
  • pivot < target那么target就在pivot的右边

实现

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let min = 0;
    let max = nums.length
    
    while(min <= max) {
      	// 取中心点
        const pivot = ((max - min) >> 1) + min;
        const pivotVal = nums[pivot];
        if(pivotVal === target) return pivot;
        else if(pivotVal > target) max = pivot - 1;
        else min = pivot + 1;
    }
    return -1
};

两数之和

要求

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。

示例 1: 输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路

采用map记录循环过的值的下标值,以target - k作为key

实现

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    const sumMap = {};
    for(let i = 0; i < nums.length; i++) {
        const val = nums[i]
        if(typeof sumMap[val] === 'number') {
            return [sumMap[val],i];
        }
        sumMap[target - val] = i;
    }
};

最长递增子序列

要求

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

思路

采用动态规划方法,将复杂问题拆分成多个小的简单问题,k0=1表示当数组只有1位时增长序列为1。创建一个dp[i]的数组记录每个位置的最大递增数

实现

/**
 * @param {number[]} nums
 * @return {number}
 */
var lengthOfLIS = function(nums) {
    const countArr = new Array(nums.length).fill(1);
    let maxCount = 1;
    for(let i = 1; i < countArr.length; i++) {
        for(let j = 0; j < i; j++) {
            if(nums[i]  > nums[j]) {
                countArr[i] = Math.max(countArr[i], countArr[j] + 1);
            }
        }
        maxCount = Math.max(maxCount, countArr[i]);
    }
    
    return maxCount
};

归并排序


  • 归并排序不是原地排序
  • 假设数组长度为 n,那么拆分数组共需 logn 步, 又每步都是一个普通的合并子数组的过程,时间复杂度为 O(n),故其综合时间复杂度为 O(nlogn)。 最佳情况:T(n) = O(nlogn)。 最差情况:T(n) = O(nlogn)。 平均情况:T(n) = O(nlogn)。
  • 假设两个有序的数组将他们进行线性合并,k0 = [a]数组只有一个元素时我们认为是有序的,
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    if(nums.length <= 1) return nums;
    const pivot = nums.length >> 1;
    return merge(sortArray(nums.slice(0,pivot)),sortArray( nums.slice(pivot)))
};

function merge(left, right) {
    const maxLeftLength = left.length;
    const maxRightLength = right.length;
    let i = 0;
    let j = 0;
    const newArr = [];
    while(i < maxLeftLength && j < maxRightLength) {
        if(left[i] <= right[j]) {
            newArr.push(left[i]);
            i ++;
        } else {
            newArr.push(right[j]);
            j ++;
        }
    }

    while(j < maxRightLength) {
        newArr.push(right[j]);
            j ++;
    }

    while(i < maxLeftLength) {
        newArr.push(left[i]);
            i ++;
    }

    return newArr;
}