一眼看懂的算法解题思路-Leetcode 283.移动0

549 阅读1分钟

话说最近嘀起心肝(下定决心)练习算法, 虽说大学也学过算法, 但是当时觉得犹如天书并差点挂科, 此时又是算法大热门时代(各大中厂的敲门砖), 于是乎给自己定下了一个小目标, 跟着leetcode学算法, 并在此记录一些总结, 尽量把自己理解的过程写出来,便于参考回顾

leetcode的链接地址是leetcode-cn.com/

今天这道题如下:

image.png

一看到题目, 第一个想到的方法就是数组的操作方法splice和push, 于是一顿简单粗暴

var moveZeroes = function(nums) {
    var length = nums.length
    var i= 0
    while(i<length) {
        if(nums[i] === 0) {
            // 从头遍历一次,把非0的剪出来, 再push进去数组尾部,同时遍历长度-1 (因为后面的已经全是0了,就没必要重复遍历了)
            nums.splice(i,1)
            nums.push(0)
            length--
        } else {
            i++
        }
    }
}

提交看看

image.png

What??? 用时312ms? 才打败10.25%用户? (不得不为leetcode的这个功能点赞,成功地引起了我的胜负欲)

于是继续思考, 是不是可以用嵌套遍历的方法,把遇到的0跟后面的第一个非0数值调换顺序,一顿敲

var moveZeroes = function(nums) {
    var length = nums.length
    for(var i=0; i<length-1;i++) {
        // 遇到0就处理(往后找非0), 遇到非0就跳过
        if(nums[i] === 0) {
            for(var j=i+1;j<length;j++) {
                // 我要找的是非0,所以后面是0的时候也跳过,不跟你换
                if(nums[j] === 0) continue;
                // 非0就跟外层循环中找到的0调换,并且跳出内层for循环,继续在外层for循环中继续找0
                var temp = nums[j]
                nums[j] = nums[i]
                nums[i] = temp
                break;
            }
        }
    }
}

行了提交看看

image.png

稍微有进步,但是感觉应该会有更优的方法(当你苦思冥想想不出来的时候,就去参考一下别人的解决方案吧,何必难为自己)

找到了,Oh,双指针,我也想过,但是思路不算很清晰,参考后一敲一试

//left始终指向第一个0, right每一轮都+1,找到一个非0就跟left上的元素交换,让非0元素往前挪,然后left+1(left是否往前走取决于right的检测结果)
var moveZeroes = function(nums) {
    var length = nums.length,left=0,right=0;
    while(right<length) {
        if(nums[right]) {
            var temp = nums[left]
            nums[left] = nums[right]
            nums[right] = temp
            left++
        }
        right++
    }
}

image.png

wow,执行用时666👍,但是这个思路其实不是很好理解(为啥都从0开始,为啥判断nums[r]??), 所以我就试着在纸上用一个简单的示例演算了一下, 豁然开朗! 好思路不如烂笔头!

image.png

image.png

image.png

nice~

小总结:
对比上面几个算法, 基本上很少有空间和时间都最优的解法, 要么就是空间换时间, 要么是时间换空间, 具体问题具体分析

有更棒的解法欢迎交流, 共同进步!