swift算法:不同路径

1、描述

一个机器人位于一个m x n网格的左上角(起始点在下图中标记为“Start”)。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

请问总共有多少条不同的路径?

例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m和n的值不超过100。

例1:输入:m = 3, n = 2

          输出:3

          解释:从左上角开始,总共有3条路径可以到达右下角。

                     1)右下->向右->向下

                     2)向右->向下->向右 

                     3)向下->向右->向右

例2:输入:m = 7, n = 3

          输出:28

 

2、算法

1)递归

思想:

求 ( 0 , 0 ) 点到( m - 1 , n - 1) 点的走法。

         (0,0)点到(m - 1 , n - 1) 点的走法等于(0,0)点右边的点 (1,0)到(m - 1 , n - 1)的走法加上(0,0)点下边的点(0,1)到(m - 1 , n - 1)的走法。

         而左边的点(1,0)点到(m - 1 , n - 1) 点的走法等于(2,0) 点到(m - 1 , n - 1)的走法加上(1,1)点到(m - 1 , n - 1)的走法。

         下边的点(0,1)点到(m - 1 , n - 1) 点的走法等于(1,1)点到(m - 1 , n - 1)的走法加上(0,2)点到(m - 1 , n - 1)的走法。

         然后一直递归下去,直到 (m - 1 , n - 1) 点到(m - 1 , n - 1) ,返回 1。

          当我们求点 (x,y)到(m - 1 , n - 1) 点的走法的时候,递归求了点 (x,y)点右边的点 (x + 1,0)到(m - 1 , n - 1)的走法和(x,y)下边的点(x,y + 1)到(m - 1 , n - 1)的走法。而没有考虑到(x + 1,0)到(m - 1 , n - 1)的走法和点(x,y + 1)到(m - 1 , n - 1)的走法是否是之前已经求过了。事实上,很多点求的时候后边的的点已经求过了,所以再进行递归是没有必要的。基于此,我们可以用 visited 保存已经求过的点。

func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var visited : [String : Int] = [String : Int]()
        return getAns(0, 0, m-1, n-1, 0, visited)
    }
    private func getAns(_ x : Int, _ y : Int, _ m : Int, _ n : Int,_ num : Int, _ visited : [String : Int])->Int{
        var visited = visited
        if x == m && y == n {
            return 1
        }
        
        var n1 = 0
        var n2 = 0
        var key : String = String(x+1)+"@"+String(y)
        //判断当前点是否已经求过了
        if visited[key] == nil {
            if x+1 <= m {
                n1 = getAns(x+1, y, m, n, num, visited)
            }
        }else{
            n1 = visited[key]!
        }
        key = String(x)+"@"+String(y+1)
        if visited[key] == nil {
            if y+1 <= n {
                n2 = getAns(x, y+1, m, n, num, visited)
            }
        }else{
            n2 = visited[key]!
        }
        
        //将当前点加入visited中
        key = String(x)+"@"+String(y)
        visited[key] = n1+n2
        
        return n1+n2
    }

2)公式

思想:

func uniquePaths6(_ m: Int, _ n: Int) -> Int {
        //初始化最后一列
        let N = n+m-2
        let k = m-1
        var res = 1
        var i = 1
        while i <= k{
            res = res * (N-k+i) / i
            i += 1
        }
        return res
    }

3)动态规划

解法一

思路:

         我们令 dp[i][j] 是到达 i, j 最多路径

         动态方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]

         注意,对于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在边界,所以只能为 1

         时间复杂度:O(m*n)

         空间复杂度:O(m*n)

         优化:因为我们每次只需要 dp[i-1][j],dp[i][j-1]

         所以我们只要记录这两个数

/*
  时间复杂度:O(m*n)
  空间复杂度:O(m*n)
*/
func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var dp : [[Int]] = [[Int]].init(repeating: [Int].init(repeating: 0, count: n), count: m)
        for i in 0..<n {
            dp[0][i] = 1
        }
        for i in 0..<m {
            dp[i][0] = 1
        }
        for i in 1..<m {
            for j in 1..<n {
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
            }
        }
        return dp[m-1][n-1]
    }


//优化空间复杂度O(2n)
   func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var pre : [Int] = [Int].init(repeating: 1, count: n)
        var cur : [Int] = [Int].init(repeating: 1, count: n)
        for i in 1..<m {
            for j in 1..<n {
                cur[j] = cur[j-1]+pre[j]
            }
            pre = cur
        }
        return pre[n-1]
    }

    //优化空间复杂度O(n)
    func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var cur : [Int] = [Int].init(repeating: 1, count: n)
        for i in 1..<m {
            for j in 1..<n {
                cur[j] += cur[j-1]
            }
        }
        return cur[n-1]
    }

解法二

思想:基于递归的基础上优化算法,要做的就是要省略压栈的过程,直接出栈。很明显可以做到的,只需要初始化最后一列为 1 ,然后 1 列,1 列的向前更新就可以了。有一些动态规划的思想了。

func uniquePaths(_ m: Int, _ n: Int) -> Int {
        //初始化最后一列
        var dp : [Int] = [Int].init(repeating: 1, count: m)
        //从右向左更新所有列
        var i = n-2
        while i >= 0 {
            //最后一行永远是1,所以从倒数第2行开始
            //从下往上更新所有行
            var j = m-2
            while j >= 0{
                dp[j] = dp[j] + dp[j+1]
                j -= 1
            }
            i -= 1
        }
        return dp[0]
    }

解法三

思想:就是从左向右,从上到下一行一行更新(当前也可以一列一列更新)

时间复杂度:O(m*n)

func uniquePaths(_ m: Int, _ n: Int) -> Int {
        //初始化最后一列
        var dp : [Int] = [Int].init(repeating: 1, count: n)
        for i in 1..<m {
            for j in 1..<n {
                dp[j] = dp[j]+dp[j-1]
            }
        }
        return dp[n-1]
    }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值