「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
今日目标
哈希_两数之和
两数平方和
二路归并排序
二分查找
最长递增子序列
哈希_两数之和
两数之和是否等于目标值;上来我就for循环;
暴力得到任意两数之和与目标值匹配,返回两者的下标;
当然这中O(n^2)的时间复杂度肯定是要优化一下的;
var twoSum = function(nums, target) {
for(let i = 0 ; i < nums.length ; i++){
for(let j = i +1 ; j < nums.length ; j++){
const t = nums[i] + nums[j];
if(t === target) return [i,j];
}
}
};
上述方法基本没什么难度;
通过上述代码可以了解这个思路时间复杂度为什么那么高?对于任意两个数字a、b;
如果 a + b = target;则有 target - a = b;
通过上述分析,如果将查询过的数据a和需要的数据b放在哈希表中;是不会可以降低时间复杂度?
比如数组 nums = [3,7,2,1], target = 9;
- 声明个哈希表map
- 拿到3,哈希表中没有3,target-3 = 6;将6放入map,map中key是6、value是0;
- 拿到7,哈希表中没有7,target-7 = 2;将2放入map,map中key是2、value是1;
- 拿到2,和希表中有2,返回map中2的value和当前下标[2,1]
比如数组nums = [4,5,2,6], target = 10 ;
- 声明个哈希表map
- 拿到4,哈希表中没有4,target-4 = 6;将6放入map,map中key是6、value是0;
- 拿到5,和希表中有5,target-5 = 5;将5放入map,map中key是5、value是1;
- 拿到2,和希表中有2,target-2 = 8;将8放入map,map中key是8、value是2;
- 拿到6,和希表中有6,返回map中7的value和当前下标[0,4]
根据上述思路编辑代码如下:
var twoSum = function(nums, target) {
const map = {};
for(let i = 0 ; i < nums.length ; i++){
const n = nums[i];
const k = target - n;
if(map[n] === undefined){
map[k] = i;
}else{
return [i,map[n]]
}
}
return []
};
两数平方和
咋一看,题目描述简单,要求也简单;那就简简单单来个思路:
- 两个整数a、b的平方是否能组成c;
- 确定了a,b的范围只能是0、1、4、9、16......;
- 通过2确定了a,b是单调递增的
- 单调性,肯定考虑万能的for循环啊;
- 在根据两数之和的思路知道a,知道目标c;求b?
直接套代码吧!!
var judgeSquareSum = function (c) {
const n = Math.sqrt(c)
// 直接能开方,返回true
if (n % 1 === 0) return true
const end = Math.floor(n);
// 声明个空哈希表
const map = {}
for (let i = 0; i <= end; i++) {
const t = Math.pow(i, 2);
// 拿到【b】
const r = c - t
map[t] = 1
if (map[r] === undefined) {
map[r] = 1
} else {
// 哈希表中找到到值,返回true
return true
}
}
return false
}
AC搞定;
审查代码,不对呀;两数之和中的【b】没什么规律,所以需要哈希表存储;在这个题中,b是有规律的,b的开方是整数即可满足条件,所以优化一下代码:
var judgeSquareSum = function (c) {
const n = Math.sqrt(c)
// 直接能开方,返回true
if (n % 1 === 0) return true
const end = Math.floor(n);
for (let i = 0; i <= end; i++) {
const t = Math.pow(i, 2);
// 拿到【b】
const r = Math.sqrt(c - t);
// 开方为整数,返回true
if(Math.floor(r) === Math.ceil(r)) return true
}
return false
}
单调性,单调递增性,哪不得来个双指针法呀;双指针,但不二分😄;
左指针指地(最小值);
右指针指天(最大值);
左右指针向中间前进;哈哈
代码如下
var judgeSquareSum = function (c) {
let left = 0;
let right = Math.ceil(Math.sqrt(c));
while(left <= right){
const t = Math.pow(left,2) + Math.pow(right,2);
if(t === c) return true;
if(t < c){
left = left+1;
}else{
right = right-1;
}
}
return false
}
二路归并排序
排序算法的有一种思路;
排序算法核心通过尽量少的比较将数据放在合适的位置
时间复杂度:O(nlogn)
空间复杂度:O(n) 递归是比较消耗空间
//归并排序
var sortArray = function(nums) {
//获取数组长度
const len = nums.length;
//如果数组长度小于2返回数组
if(len < 2) return nums;
// 否则分割数组
const mid = len >> 1;//右移1位,类似除以2
// 递归分解左侧数组
const left = sortArray(nums.slice(0,mid));
// 递归分解右侧数组
const right = sortArray(nums.slice(mid,len))
// 合并左右两个数组
return merge(left,right);
// 合并数组
function merge(left,right){
let l = 0;
let r = 0;
let list = [];
while(l < left.length && r < right.length){
if(left[l] < right[r]){
list.push(left[l])
l++
}else{
list.push(right[r])
r++
}
}
// 这两行代码省去了两个判断
list = list.concat(left.slice(l,left.length));
list = list.concat(right.slice(r,right.length));
return list
}
};
二分法
二分法条件:
有序、有目标; 二分法大致伪代码
let left = 0;
let right = max;
const m = (left + right) >> 1;
if(m < target){
left = m+1
}else{
right = m-1
}
实际代码
var search = function (nums, target) {
let left = 0
let right = nums.length - 1
while (left <= right) {
// console.log(left, right)
const mid = Math.floor(left + (right - left) / 2)
if (nums[mid] === target) return mid
if (nums[mid] < target) {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
最长递增子序列
思路在注释中
/**
* @param {number[]} nums
* @return {number}
*/
var lengthOfLIS = function (nums) {
const len = nums.length
// 数组长度为1,直接返回
if (len === 1) return 1;
let idx = 1;
//声明一个单调递增的数组;该数组中最后一位数字是当前数组中最大值
const list = []
list[idx] = nums[0]
for (let i = 1; i < len; i++) {
// 如果数组最大值小于新增数值,将数组长度加+1
if (list[idx] < nums[i]) {
idx++
list[idx] = nums[i]
} else {
// 如果当前数值小于最小值;
// 用二分法找到一个位置,
// 该位置的条件是上一位比当前数小,下一位比当前数大
// 将当前(nums[i])数替换该位置的数;
let left = 1
let right = idx
const n = nums[i]
let pos = 0
while (left <= right) {
const m = (left + right) >> 1
if (list[m] < n) {
pos = m
left = m + 1
} else {
right = m - 1
}
}
// 替换该位置
list[pos + 1] = nums[i]
}
}
return idx
}