第一次作业
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
------------------------------------------------------
这个思路方向是正确的,看似算法也没错,但其实还是有错误:
这个也有问题,当M=2,List={2,1}时
-----------
-----------
昨天看了一小时多的Scala视频,在一边练英语听力一边学函数式编程的情况下把第一周的课程看完了.
于是开始做练习,发现函数式编程大部分都是使用递归,这让一直算法不好的我有很大的压力,但我想,连简简单单的几个在线课程的练习都不会做的话,我的算法也算是废了.
// Scala的练习中所有List都只有三个API
// chars.isEmpty: Boolean returns whether a list is empty
// chars.head: Char returns the first element of the list
// chars.tail: List[Char] returns the list without the first element
Exercise 1: 杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
...
Do this exercise by implementing the pascal function in Main.scala, which takes a column c and a row r, counting from 0 and returns the number at that spot in the triangle. For example, pascal(0,2)=1, pascal(1,2)=2 and pascal(1,3)=3.
def pascal(c: Int, r: Int): Int = {
if (c == r) 1
else if (c == 0) 1
else pascal(c - 1, r - 1) + pascal(c, r - 1)
}
总结:
第一次做递归程序的练习,总结出了一个小规律:
1) 首先要找出结束递归的条件,如杨辉三角中当row和column相等时,直接返回1;当column等于1的时候,也返回1
2) 其次,大事化小,小事化了,任何的func(x),总能推出func(x-1)的相关公式 (怎么这么像数学归纳法?)
第一题花了大概5分钟,尚可
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
Exercise 2: Parentheses Balancing
Write a recursive function which verifies the balancing of parentheses in a string, which we represent as a List[Char] not a String. For example, the function should return true for the following strings:
(if (zero? x) max (/ 1 x))
I told him (that it’s not (yet) done). (But he wasn’t listening)
The function should return false for the following strings:
:-)
())(
计算一个表达式中的括号匹配情况是否正确
如(if (zero? x) max (/ 1 x))就是正确的
:-)和())(就是不正确的
def balance(chars: List[Char]): Boolean = {
def myFunc(cs: List[Char], brace: Int): Boolean = {
if (brace< 0) false
else if (cs.isEmpty) brace== 0
else if (cs.head == '(') myFunc(cs.tail, brace+ 1)
else if (cs.head == ')') myFunc(cs.tail, brace- 1)
else myFunc(cs.tail,brace)
}
myFunc(chars, 0)
}
总结:
1) 定义辅助函数,多加了一个参数,用于计算左括号(的数量
2) 递归字符串列表,找到一个左括号,就+1,找到一个右括号就-1
3) 当右括号太多时(brace < 0),返回false
4) 当递归结束时,判断brace是否是0
例如:
balance "())(" ==>
myFunc "))(",1 ==>
myFunc "))(",1 ==>
myFunc ")(",0 ==>
myFunc ")",-1 ==> false
5) 感觉这个就是尾递归
第二题花了10分钟不到,一次运行,一次通过,挺意外的
------------------------------------------------------
Exercise 3: Counting Change
Write a recursive function that counts how many different ways you can make change for an amount, given a list of coin denominations. For example, there are 3 ways to give change for 4 if you have coins with denomiation 1 and 2: 1+1+1+1, 1+1+2, 2+2.
此题我感觉是很常见的算法题,可是我貌似从来没自己独立完成过,于是,此次练习给予我莫大的挑战
思路:
一开始的思路是:
假设money总和是M,零钱是A,B,C等,首先处理A
假设money总和是M,零钱是A,B,C等,首先处理A
会有以下三种情况:
1) 没有用到A
递归countChange(money, coins.tail),因为把coins列表的头(A)去掉
换句话说,就是处理列表除了A意外其他零钱组合成M的那些情况
2) 用到了A一次
递归countChange(money - coins.head, coins.tail)
换句话说,就是处理列表除了A意外其他零钱组合成M-A的那些情况
3) 用到了A多次
换句话说,就是处理列表所有零钱组合成M-A的那些情况
错误的答案1:
def countChange(money: Int, coins: List[Int]): Int = {
if (coins.isEmpty) 0
else if (coins.head == money) 1
else if (money < 0) 0
else
countChange(money, coins.tail) + countChange(money - coins.head, coins) +
countChange(money - coins.head, coins.tail) // ERROR
}
这个思路方向是正确的,看似算法也没错,但其实还是有错误:
情况2和3其实本质相同,会有重复计算,如M=4,List=1,2
countChange(4,{1,2})
=>
countChange(4,{2})+
countChange(3,{1,2})+ //情况2)
countChange(3,{2}) //情况3)
=>
countChange(3,{2}) + //情况2)的情况1)
countChange(3-1,{1,2})+
countChange(3-1,{2})
在这种算法下,情况2)的下一轮递归其实和情况3)是相同的,所以说,这样子会出现重复计算,于是要去除情况3)
-----------
错误的答案2:
def countChange(money: Int, coins: List[Int]): Int = {
if (coins.isEmpty) 0
else if (coins.head == money) 1 // ERROR
else if (money < 0) 0
else
countChange(money, coins.tail) + countChange(money - coins.head, coins)
}
这个也有问题,当M=2,List={2,1}时
如果执行这条语句 (coins.head == money) 1
那么直接返回成功,也就是处理了2=2的情况,而没有处理2=1+1的情况
-----------
正确的答案:
def countChange(money: Int, coins: List[Int]): Int = {
if (money == 0) 1 //直接用money=0来处理
else if (coins.isEmpty) 0
else if (money < 0) 0
else
countChange(money, coins.tail) + countChange(money - coins.head, coins)
}
-----------
如果题目再扩充一下,希望输出所有成功的加法组合,那么就要像之前说的那样,加一个参数,用来保存函数调用栈中所使用的零钱
带打印的答案:
def countChange(money: Int, coins: List[Int], p: List[Int]): Int = {
if (money == 0) {
println(p)
1
} else if (coins.isEmpty) 0
else if (money < 0) 0
else
countChange(money, coins.tail, p) + // 没有用到coins.head,所以p不做任何操作
(money - coins.head, coins, p :+ coins.head)countChange // 用到了coins.head,让p尾部增加coins.head
}
总结:
这题一共做了大约半小时左右,第一个错误的答案的思路就想了将近20分钟,这也看出了我在递归算法中的薄弱环节,以后仍然需要练习,不过从这题中也到了些许递归题目的思路,我觉得以后看"编程之美"应该会能够看懂更多吧