大家好我是django今天给大家带来的算法题分享有《平方数之和》、《二分法查找》、《两数之和》、《最长递增子序列》以及《归并排序》
平方数之和
要求
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 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;
}