从零开始学算法的第二天~(最长公共前缀,有效的括号,移动零)

前言

今天就是我 硬钢算法的第二天,我的基础真的很差。文章重在记录总结,我也很好奇我会坚持多久,我只有以写文章的形式记录每天刷的题才能让自己保持一个积极的态度,不然每次看一会就懈怠了😭。只有看到大家的反馈我才能源源不断的坚持下去!

过往文章:

👨‍💻从零开始学算法的第一天~(二分查找,双指针,两数相加,最长回文子串)

刷题

283. 移动零

var moveZeroes = function(nums) {
    let left = 0,right = 0
    while(right < nums.length){
        if(nums[right]){
            [nums[right],nums[left]] = [nums[left],nums[right]]
            left ++
        }
        right++
    }
};

实现的思路在于定义两个指针,遍历数组,每一次遍历都会 向右移动right指针 的位置,但是只有 当前遍历值值不为零 才移动left的位置,这样就实现了 将left一直保留在0 的位置,如果值不为零就将它与left调换位置,把不为零的值调到前面去。循环结束后不为零的值就都移动到左侧去了。

这里换一种写法更加直观,是不是顿时清晰了:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function(nums) {
    let zeroPosition = 0
    nums.forEach((item,index)=>{
        if(item){
            [nums[zeroPosition],nums[index]] = [nums[index],nums[zeroPosition]]
            zeroPosition++
        }
    })
};

7. 整数反转

数字溢出的阈值通过 Math.pow 来计算,正负的记录则使用 Math.sign

  • Math.pow(2,31)获取最大数字
  • Math.sign()获取数字的正负


var reverse = function(x) {
    const sign = Math.sign(x)
    x = Math.abs(x)
    const num =  sign * parseInt(('' + x).split('').reverse().join('')) 
    if(num > Math.pow(2,31)||num < Math.pow(-2,31)){
        return 0
    }
    return num
};

这题比较简单,转换为字符串再转换为数组后翻转, 翻转后再转为数字,进行一个判断即可~

9. 回文数

这一题的要求是不能转换为字符串去判断是否为回文数。

实现的核心在于创建一个新变量,将数字给翻转过来之后赋值给新变量,并判断新变量是否与传入的数字相同。

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    if(x < 0|| (x!==0&&x%10 ===0)){
        return false
    }
    let resverNum = 0
    let num = x
    while(num){
        resverNum = resverNum * 10 + num % 10
        num = Math.floor(num/10)
    }
    return x === resverNum
};

首先排除一部分的数值,负数 以及 非零可以被10整除的数字 ,因为如果最后一位为0的话第一位必须是0,但是整数的第一位不可能是零所以要排除这种情况。

接下来就是翻转数字,实现的方式是先创建一个新的数值,初始值为0。将原数字除十取余数,并将余数加到新变量上,原数字不断除十直到取整后为0,而新变量每次循环都乘十再加余数,这样循环完成后数字就翻转过来了。

20. 有效的括号

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
    const arr = s.split('')
    if(arr.length % 2 !== 0){
        return false
    }
    const map = {
        '(':')',
        '{':'}',
        '[':']'
    }
    const stack = []
    for(let item of s){
        if(map[item]){
            stack.unshift(map[item])
        }
        else{
            const match = stack.shift()
            if(match !== item){
                return false
            }
        }
    }
    if(stack.length){
        return false
    }
    return true
};

这题是面试老客户了,虽然我没刷过 leetcode,但是对于这一题还是很亲切的哈哈哈。

实现的思路在于两点,一个是创建一个对象用于 存储括号的对应关系,再创建一个栈,利用栈 后进先出 的特点来匹配括号

在js中实现栈的话就是通过数组模拟,使用 unshiftshift 两个api模拟入栈出栈操作。

11. 盛最多水的容器

var maxArea = function(height) {
 let maxV = 0
 let left = 0
 let right = height.length - 1
 while(left<right){
     const V = (right - left) * Math.min(height[left],height[right])
     height[left]>height[right]?right--:left++
     if(V>maxV){
         maxV = V
     }
 }
 return maxV
};

这一题的思路是使用双指针,容器的体积通过 指针的距离 * 两个指针中小的那个值 计算,然后判断哪一边的指针指向的值小,将那一边指针向中间偏移,直到两个指针相遇。

14. 最长公共前缀

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    if(!strs.length){
        return ""
    }
    const minLength = Math.min(...strs.map(item=>item.length)) 
    const publicStrArr = []

    for(let i = 0;i<minLength;i++){
        let isPublic = true
        for(let strIndex in strs){
            if(!strs[strIndex][i]||strs[strIndex][i]!==strs[0][i]){
                isPublic = false
                break
            }
        }
        if(!isPublic){
            break
        }
        publicStrArr.push(strs[0][i])
    }
    
    return publicStrArr.join('')
};

这一题总感觉我的解决方式不是很优雅😅,首先我获取数组中长度最小的值的长度,然后执行两层遍历,外层是用于遍历数组内的每一个字符串,内层是遍历字符串数组。通过 isPublic 变量来判断数组中所有字符串的strIndex 位置的值是否相同,如果不同就跳出循环,如果相同就push进数组中。最后将数组拼接为字符串返回。

167. 两数之和 II - 输入有序数组

这种两数之和我最习惯的就是使用map的解法,利用对象可以通过 键的唯一性和映射关系 实现很多方便的功能。

/**
 * @param {number[]} numbers
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(numbers, target) {
    const map = {}
    for(let i in numbers){
        if(!map[target - numbers[i]] ){
            map[numbers[i]] = i
        }
        else{
            return [parseInt(map[target - numbers[i]])+1,parseInt(i)+1 ]
        }
    }
};

首先创建一个对象map,然后遍历数字数组,两数之和其实就是不断的去将 target 值与遍历的到的数字取差,如果这个差值是map中已经有的值说明他们俩相加等于target 就可以返回啦。如果是map中没有的就 把这个值当做一个键添加在map中

由于数组时有序的,因此也满足二分查找的条件,使用二分查找的解法

var twoSum = function(numbers, target) {
    
    for(let i in numbers){
        i= parseInt(i)
        let left = i+ 1 
        let right = numbers.length - 1
        while(left <= right){
        const mid = Math.floor((right - left)/2 + left)
        if( numbers[i] + numbers[mid] === target){
            return [i+1,mid+1]
        }
        else if(numbers[i] + numbers[mid] < target){
            left = mid + 1
        }
        else{
            right = mid - 1
        }
    }
    }
    
};

首先遍历数字数组,然后在当前遍历到的数字 右侧范围 进行一个二分查找,每一次 判断遍历到的数字 +
二分查找的中间值之和 与 target 的大小关系,然后不断缩小范围

557. 反转字符串中的单词 III

var reverseWords = function(s) {
    s = s.split(' ').map(item=>item.split('').reverse().join('')).join(' ')
    return s
};

这题简单,一行就能搞定,先将数组以空格分割开map遍历数组将数组中 每一个字符串给翻转后再将数组以空格拼接 起来,这样就可以保持字符串空格的位置保持不变。

876. 链表的中间结点

这一题我认为对我帮助很大,让我学习到了一个快慢双指针实现指向链表中间位置的方法。

由于链表不能直接获取中间位置的指针,因此我们定义一快一慢两个指针,每次遍历时快的指针走两步,慢的指针走一步,快的走两步。当快的指针走到链表末尾时,慢的指针刚好走到链表中间。这个思路对于以前没刷题的我来说是很大启发哈哈~

var middleNode = function(head) {
    let slow,fast
    slow = fast = head;
    while (fast && fast.next) {//快慢指针遍历,直到快指针到达最后
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
};

总结

刷题第二天啦,每天都好累,但是还是得坚持呀~

非常非常欢迎你点个赞或者写个评论让我知道 有人在监督我,避免我偷懒!,如果你对算法学习有一定心得也欢迎你为我提供帮助哈~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值