LeetCode算法

力扣算法

常用方法:

Set
set的属性
  • size():返回集合所包含的元素数量
set的方法
  • add(value):向集合中添加一个新的项
  • delete(value):删除集合中某个元素
  • has(value):判断集合中是否存在value,返回true或false
  • keys()(values()):返回包含所有键的数组
//	基本用法
let set = new Set();
set.add('a');
set.add('b');
set.add('c');
console.log(set.keys());    // SetIterator {"a", "b", "c"}
console.log(set.values());  // SetIterator {"a", "b", "c"}

//	拓展用法
x const s = new Set();

[2,3,5,4,5,2,2].forEach(x => s.add(x))
for(let i in s){   
    console.log(i)  //  2 3 5 4
}

//  数组的去重
let arr = [1, 2, 1, 4, 5, 3];
let diffArr = [...new Set(arr)]
console.log(diffArr)    //  [1, 2, 4, 5, 3]
Map
let m = new Map()
m.set('Jay','Jay Chou')
m.set(true,'真的')

console.log(m.has('Jay'))		//	true
console.log(m.has('Jay Chou'))	//	false
console.log(m.size)				//	2
console.log(m.keys())			//	MapIterator{"Jay",true}
console.log(m.values())			//	MapIterator{"Jay Chou","真的"}
console.log(m.get('Jay'))		//	Jay Chou

//	遍历
for(let key of m.keys()){
    console.log(key)			//	Jay true
}

常用算法:

二分查找
要求

​ 线性表中的记录必须按关键码有序,并且必须采用顺序存储

原理
  1. 设置查找区间:low = 0 ,high = arr.length - 1

  2. 若查找区间不存在,则查找失败,否则转步骤三

  3. 取中间位:mid = (high + low) / 2,比较target 与 arr[mid]的大小

    ​ 3.1 若target < arr[mid],则high = mid + 1,查找在左半区进行,转步骤2

    ​ 3.2 若target > arr[mid],则low = mid + 1,查找在右半区进行,转步骤2

    ​ 3.3 若target = arr[mid],则查找成功,返回mid

性能分析

​ 最好时间复杂度:O(1),target = arr[mid]

​ 最坏时间复杂度:O(logn),查找不到目标元素

循环实现
function BinarySearch(let arr,let target){
    let low = 0
    let high = arr.length
    while(low < high){
        let mid = ~~((high + low) / 2)
        if(target < arr[mid]){
            high = mid - 1
        }else if(target > arr[mid]){
            low = mid + 1
        }else{
            return mid
        }
    }
    return -1
}
递归实现
let longestPrefix = function (str) {
    if (str.length === 0) {
        return ""
    }
    //  找出长度最小的那个字符串
    let minLength = str[0].length;
    for (let i = 0; i < str.length; i++) {
        minLength = Math.min(minLength, str[i].length)
    }
    //  定义了二分查找的两个端点
    let low = 0, high = minLength;
    while (low < high) {
        let mid = ~~((high - low + 1) / 2) + low
        //  使用二分查找 查找公共前缀的位置
        if (isCommonPrefix(str, mid)) {
            low = mid
        } else {
            high = mid - 1
        }
    }
    return str[0].substring(0, low)
}

function isCommonPrefix(strs, length) {
    let str0 = strs[0].substring(0, length)
    let count = strs.length
    for (let i = 1; i < count; i++) {
        let str = strs[i]
        for (let j = 0; j < length; j++) {
            if (str0.charAt(j) !== str.charAt(j)) {
                return false
            }
        }
    }
    return true;
}

简单:

1.两数之和

​ 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

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

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
解法一:暴力枚举
  • 枚举在数组中所有不同的两个下标的组合
  • 逐个检查它们所对应的数的和是否等于target
var twoSum = function(nums, target) {
    let arr = []
    let length = nums.length 
    for(let i = 0 ; i < length ; i++){
        for(let j = i + 1; j < length ; j++){
            if(nums[i] + nums[j] === target){
                arr.push(i)
                arr.push(j)
            }
        }
    }
    return arr
}
解法二:查找表法
  • 在遍历的同时,记录一些信息,以省去一层循环,这是“以空间换时间”的想法
  • 需要记录已经遍历过的数值和它对应的下标,可以借助查找表实现
  • 查找表有两个常用的实现:
    • 哈希表
    • 平衡二叉搜索树

​ 哈希表:

  • 算出当前数和target的差
  • 检查哈希表中是否存在该差,若存在,返回下标
  • 若不存在,当前数字为key,索引为value存入哈希表
var twoSum = function(nums, target) {
    //	创建一个map
    let map = new Map()
    let length = nums.length
    for(let i = 0 ; i < length ; i++){
        //	如果map中存在该target-nums[i],返回两个数的下标
        if(map.has(target - nums[i])){
            return [map.get(target - nums[i]),i]
        }else{
            //	否则存入nums[i]和对应的下标
            map.set(nums[i],i)
        }
    }
    return []
}
9.回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。
解法一:将整数转为字符串
  • 由于给定的参数x为数值类型,要先将x转换为字符串类型
  • 将字符串的前半部分和后半部分分别提取出来进行比较
  • 若相同,返回true,否则返回false
let isPalindrome = function(x) {
    let str = x + ''
    //	前半部分向下取整
    let prefixStr = str.slice(0,Math.floor(x/2))
    //	后半部分向上取整
    let suffixStr = str.slice(Math.ceil(x/2),str.length)
    //	将后半部分利用reverse方法反转
    let reverseStr = suffixStr.split('').reverse().join('')
    return reverseStr === prefixStr
}
解法二:每次求余得到反转的数
  • 负数、10的倍数都不可能是回文数
  • 利用整数反转来获取反转后的数,若和参数完全一致,则是回文数
let isPalindrome = function(x) {
    if (x < 0 || (x % 10 === 0) && x !== 0) {
        return false
    }
    let revertNum = 0
    let index = x
    while(index != 0){
        revertNum = revertNum * 10 + index % 10
        index = ~~(index / 10)
    }
    return revertNum === x
}
算法优化

​ 在整数反转到一半时,即可判断后半部分和前半部分是否相等

  • 若x的长度为偶数,直接比较
  • 若x的长度为奇数,中间的数字并不影响最终的结果,只需再次 /10即可
let isPalindrome = function(x) {
    if (x < 0 || (x % 10 === 0 && x !== 0)) {
        return false
    }
    let revertNum = 0
    while(x > revertNum){
        revertNum = revertNum * 10 + x % 10
        x = ~~(x / 10)
    }
    return revertNum === x || ~~(revertNum / 10) === x
}
13.罗马数字转整数

​ 罗马数字包含以下七种字符: IVXLCDM

​ 例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:

输入: s = "III"
输出: 3

示例 2:

输入: s = "IV"
输出: 4

示例 3:

输入: s = "IX"
输出: 9

示例 4:

输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
思路

通常情况下,罗马数字小的数放在大的数的后面

如果输入的字符串是上述格式,则对每个字符串进行累加即可

如果小的数放在大的数的前面,则说明该数类似IV、IX,只需将V - I即后面的数减去前面的数即可

let romanToInt = function (s) {
    let map = new Map()
    map.set('I', 1)
    map.set('V', 5)
    map.set('X', 10)
    map.set('L', 50)
    map.set('C', 100)
    map.set('D', 500)
    map.set('M', 1000)
    
    let sum = 0
    let value = 0
    let len = s.length
    
    for(let i = 0 ; i < len ; i++){
        //	得到每一个字符串对应的值
        value = map.get(s[i])
        //	若小的数在前面
        if(value < map.get(s[i+1])){
            sum = sum + map.get(s[i+1]) - value
            //	i后面的数已经计算过,也为了防止数组溢出
            i++
        }else{
            sum += value
        }
    }
}
14.最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
解法一:横向扫描
  • 当字符串数组长度为0时,则前缀为空,直接返回
  • 令最长公共前缀ans为第一个字符串,进行初始化
  • 遍历后面的字符串,依次与ans进行比较,当字符不相等时,直接跳出循环
  • 如果查找过程中出现了ans为空,则公共前缀不存在直接返回
/**
* @param {string[]} strs
* @return {string}
*/
let longestCommonPrefix = function (str) {
    //	当数组为空时,直接返回
    if(str.length === 0){
        return ""
    }
    let ans = str[0]
    //	遍历str数组
    for(let i = 0 ; i < str.length ; i++){
        let j = 0;
        //	遍历数组元素的每一个字符,当字符串不相等,跳出循环
        for(; j < str.length && j < ans.length; j++){
            if(ans.chart[j] !== str[i][j]){
                break;
            }
        }
        ans = ans.subString(0,j)
        //	当ans为空时,直接返回
        if(ans === ""){
            return ""
        }
    }
    return ans
}
解法二:二分查找
  • 最长公共前缀的长度不会超过字符串数组中的最短字符串的长度
  • 用minLength表示字符串数组中最短字符串的长度
  • 可以在[0,minLength]的范围内通过二分查找得到最长公共前缀的长度
  • 每次查找取查找范围的中间值mid,判断每个字符串的长度为mid的前缀是否相同
  • 如果相同,则最长公共前缀的长度一定大于或等于mid
  • 如果不相同,则最长公共前缀的长度一定小于mid
  • 通过上述方式将查找范围缩小一般,直到得到最长公共前缀的长度
let longestCommonPrefix = function (str) {
    if(str.length === 0){
        return ""
    }
    int minLength = 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值