|必拿offer系列| 算法| 数组(二)

移动零

leetcode

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明:

必须在原数组上操作,不能拷贝额外的数组。 尽量减少操作次数。

这道题是双指针问题无疑了,基本上数组的题都跟双指针分不开,基本上 双指针问题都要用到先排序,不过这个题不需要。

func moveZeroes(nums []int)  {
    i,j := 0,0
    for j < len(nums){ 
        if nums[j] != 0 { //  注意这个是j j代表了全部的遍历过程 i代表了新的重组结构。
            nums[i] = nums[j]
            i++
        }
        j++
    }
    // 这个循环完毕以后,i的下下标豆薯属于非零的了,那么可以说i后面的下标都是0了,但是
    // 目前还没有。因为有一些内存地址还是其它值的,

    // 比如这样 [1,3,12,3,12]
    // 那么我们要做的就是把i后面的索引变成0就OK了。对吧对吧。
    for i < len(nums) {
        nums[i] = 0
        i++
    }
}

这道题的解题思路就是使用两个指针,然后j肯定是比i快的,最起码是一样快,所以不用担心i的内容会覆盖掉j,那么j前面的数据被i给覆盖了, 然后当覆盖到i结束,就是说0 - i 这个内存空间已经是正确的所有数据了,那么在i后面的数据尽管还有值,但是这些值其实已经被i这个指针提取到了前面 所以说i后面的索引值的内存空间是无用的,那么我们可以在i到len的这个空间内将数据变为0.


重塑矩阵

leetcode

在MATLAB中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。

给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。

如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

示例 1:

输入:

nums = 
[[1,2],
 [3,4]]
r = 1, c = 4
输出: 
[[1,2,3,4]]

解释: 行遍历nums的结果是 [1,2,3,4]。新的矩阵是 1 * 4 矩阵, 用之前的元素值一行一行填充新矩阵。 示例 2:

输入:

nums = 
[[1,2],
 [3,4]]
r = 2, c = 4
输出: 
[[1,2],
 [3,4]]

解释: 没有办法将 2 * 2 矩阵转化为 2 * 4 矩阵。 所以输出原矩阵。 注意:

给定矩阵的宽和高范围在 [1, 100]。 给定的 r 和 c 都是正数。

func matrixReshape(nums [][]int, r int, c int) [][]int {
    if len(nums) * len(nums[0]) != r * c || r == 0 || c == 0 || len(nums) == 0 || len(nums[0]) == 0{
        return nums
    }

    result  :=  make([][]int,r)
    count,col := 0,len(nums[0])
    for i := range result { // 这两次的构造是按照新的数据结构去构造的。
        result[i] = make([]int,c)
        for j := range result[i] {
            result[i][j] = nums[count/col][count%col] 
	    
	    // 这里是关键,也就是说我们需要两个辅助的变量同时用到了小学数学里的商和余数。建议数学不好的同学,(例如我。。。)去补补小学数学。。
            count++ 
	    // 这里的++ 不需要进行限制,因为这个限制已经在j := range result[i] 这里进行了数量上的限制了。
        }
    }
    return result
}

这道题我有两个思路 一个就是 先构造新的result然后开始遍历这个result的数组, 然后再使用取商和取余数的方式把老数组的内容一个一个的取到新的数据结构中。

第二个思路 就是使用一个queue 然后把数据先导入进去,然后便利新的数组的时候一个一个加进去就OK了, 但是使用queue空间复杂度就大了,不如直接取余数和商。这种需要重新构造的数组一般取余数和取商是最好的方式了, 只需要使用一个一直增加的下标索引,然后再加上每个子数组的长度,就OK 了,这种取余的手法在循环stack queue的时候都有用到。 我感觉 这种取余的手法非常常见也非常重要

下标跟数组长度取商可以发现是几倍于这个数组,取余可以发现是在这个数组长度的具体哪个索引处


最大连续1的个数

leetcode

给定一个二进制数组, 计算其中最大连续1的个数。

示例 1:

输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 注意:

输入的数组只包含 0 和1。 输入数组的长度是正整数,且不超过 10,000。

解法一双指针解法


func findMaxConsecutiveOnes(nums []int) int {
    count := 0
    // 这里的count是一个计数值,是为了最后的输出。
    for i,j := 0,-1;i < len(nums);i++ { 
        
    // i ,j 在这种题中一般都是两个指针
    // 即所谓的数组双指针问题,一个指针是i一个是j,i是主指针主索引,j是辅助指针。

    // 在这道题中j是关键,i - j是关键,如果当nums[i] == 0的时候这个时候i - j就不能增加。所以这个时候j就要等于i,如果不是0的时候这个i - j的值就要增加所以这个时候i - j就要++ 所以这就是为什么最开始的时候j = -1 
    // 因为j要比i小一,才能在i - j的时候是增加的。

    // 在上次是0下次是1的时候j = i 然后i ++ 了但是j没有,所以差值仍然是+1

    // 如果连续几次都是nums[i] = 1 那么i一直在增加,j没有增加差值刚好是增加的次数。
        if nums[i] == 0 {
            j = i
        }else {
            if count < i - j {
                count = i -j 
            }

        }
    }
    return count
}

通用动态规划的写法

按照步骤分析,每一次要么是1 要么是0 而且按照步骤分析前后并没有影响的关系。 如果看不懂就算了可以去使用第一种解法。


func findMaxConsecutiveOnes(nums []int) int {
    if len(nums) == 0 {
        return 0
    }
    res  := 0
    result := make([]int,len(nums))
    result[0] = nums[0]
    for i := 1; i < len(result);i++ {
        if nums[i] == 0 {
            result[i] = 0
        }else {
            result[i]= result[i-1]+1
        }
    }
    for _,x := range result {
        if x > res {
			res = x
		}
    }
    return res
}

加入微信公众号:<<算法和数据结构的峡谷>> 一起学编程一起努力一起拿offer!

p


本篇文章由一文多发平台ArtiPub自动发布

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值