scala写算法-List、Stream、以及剑指Offer里部分题目基于scala解法
Stream(immutable)
Stream是惰性列表。实现细节涉及到lazy懒惰求值、传名参数等等技术(具体细节详见维基百科-求值策略)。
Stream和List是scala中严格求值和非严格求值两个代表性不可变函数式数据结构。
考虑字符串拼接的表达式"foo"+"bar"的到"foobar",空串""就是这个操作的单位元(identity,数学中又称幺元),也就是说s+""或者""+s的值就是s。如果将三个字符串相加s+t+r,由于操作是可结合的(associative),所以说((s+t)+r)和(s+(t+r))的结果一样。
在函数式编程里,把这类代数称为monoid。结合律(associativity)和同一律(identity)法则被称为monoid法则。
可折叠数据结构
比如List、Stream、Tree、Vector等等这类都是可折叠数据结构。monoid与折叠操作有着紧密联系。
比如words=List("aa","bb","cc"),运用折叠方法如下:
words.foldRight("")((a,b)=>a+b) == ((""+"aa")+"bb")+"cc"
words.foldLeft("")((a,b)=>a+b) == "aa"+("bb"+("cc"+""))
首先实现Stream.fold方法,然后用fold去实现map filter flatMap等等高阶函数(大可不必仅使用fold去编写其它函数,就像尺规作图没有刻度一样。这样写仅仅为了好玩,没有银弹No Silver Bullet)。至于map、flatMap是什么?函子(Functor)是对map的泛化,Monad是对flatMap的泛化(相关概念参见Fp in Scala)。
fold源码如下(采用尾递归):
def fold[B](z: =>B)(f:(A,B)=>B):B={
@tailrec
def loop(stream: Stream[A])(result: =>B)(f:(A,B)=>B):B=stream match {
case Empty =>result
case Cons(h,t) =>loop(t())(f(h(),result))(f)
}
loop(self)(z)(f)
}
Stream实现如下:
trait Stream[+A] {self=>
import Stream.{cons,empty}
def fold[B](z: =>B)(f:(A,B)=>B):B={
@tailrec
def loop(stream: Stream[A])(result: =>B)(f:(A,B)=>B):B=stream match {
case Empty =>result
case Cons(h,t) =>loop(t())(f(h(),result))(f)
}
loop(self)(z)(f)
}
def toList=fold(Nil:List[A])((a,b)=> ::(a,b)).invert
def exits(p:A=>Boolean)=fold(false)((a,b)=>p(a)||b)
def forall(p:A=>Boolean)=fold(true)((a,b)=>p(a)&&b)
def invert=fold(Empty:Stream[A])((a,b)=>cons(a,b))
def map[B](f:A=>B)=fold(Empty:Stream[B])((a,b)=> cons(f(a),b)).invert
def ++[B>:A](stream:Stream[B])=invert.fold(stream)((a,b)=>{cons(a,b)})
def flatMap[B>:A](f:A=>Stream[B])=fold(Empty:Stream[B])((a,b)=>{f(a)++b}).invert
def filter(p:A=>Boolean)=fold(Empty:Stream[A])((a,b)=>p(a) match {
case true =>cons(a,b)
case false =>b
}).invert
def take(n:Int)=fold((n,empty[A]))((a,b)=> b._1 match {
case r if r >0=>(r-1,cons(a,b._2))
case 0=>(0,b._2)
})._2.invert
def drop(n:Int)=fold((n,empty[A]))((a,b)=>b._1 match {
case r if r>0 =>(r-1,b._2)
case 0 => (0,cons(a,b._2))
})._2
}
case class Cons[+A](h:()=>A,t:()=>Stream[A]) extends Stream[A]
case object Empty extends Stream[Nothing]
object Stream{
def cons[A](hd: =>A,tl: =>Stream[A]) :Stream[A]={
lazy val head=hd
lazy val tail=tl
Cons(()=>head,()=>tail)
}
def empty[A]:Stream[A]=Empty
def apply[A](xs:A*):Stream[A]=
if(xs.isEmpty)
empty
else
cons(xs.head,apply(xs.tail:_*))
}
List(immutable)
scala中的List实现方式类似于c语言中的链表数据结构,但是不同于链表,List是不可变的。就像字符串操作a=b+c,字符串a加上字符串b的到新的字符串c,同理,不可变的List,al=bl++cl,listbl与listcl相连接的到新的listal,而bl与cl本身不变。相比抽象数据结构ADT,我更愿意把List称作代数数据结构。其不变性与函数式数据结构中的数据共享有关(通过复制与共享实现了不变性)。
至于exists forall takeWhile take filter map foldRight等函数均采用尾递归方式,无需考虑爆栈问题。与官方库基于CanBuildFrom这类的虚类型实现方式不同,本程序完全采用递归实现。
trait List[+A]{self=>
def tail=self match {
case Nil => Nil
case _::t=>t
}
def init:List[A]=self match {
case Nil=> Nil
case h::Nil => Nil
case h::t=> ::(h,t.init)
}
def print={
def loop(list: List[A]):String=list match {
case Nil=>"Nil"
case h::t=>s"$h :: ${loop(t)}"
}
loop(self)
}
override def toString= print
def setHead[B>:A](newHead:B)= ::(newHead,self.tail)
def drop(n:Int):List[A]=n match {
case 0=>self
case _ if n>0 => self match {
case _::t=> t.drop(n-1)
}
case _ if n<0 =>Nil
}
def dropWhile(p:A=>Boolean):List[A]=self match {
case Nil=>Nil
case h::t=>p(h) match {
case true => t.dropWhile(p)
case false=> self
}
}
def ++[B>:A](list: List[B]):List[B]=self match {
case Nil=>list
case h::t=> ::(h,t++list)
}
def foldRight[B](z:B)(f:(A,B)=>B)={
@tailrec
def loop(list: List[A])(z:B)(f:(A,B)=>B):B=list match {
case Nil => z
case h::t => loop(t)(f(h,z))(f)
}
loop(self)(z)(f)
}
def invert=foldRight(Nil:List[A])(::(_,_))
def length=foldRight(0)((_,b)=>b+1)
def flatMap[B>:A](f:B=>List[B]):List[B]=self match {
case Nil=> Nil
case h::t=> f(h)++t.flatMap(f)
}
def map[B](f:A=>B)={
@tailrec
def loop(list: List[A])(result:List[B])(f:A=>B):List[B]=list match {
case Nil=>result
case h::t => loop(t)(::(f(h),result))(f)
}
loop(self)(Nil:List[B])(f).invert
}
def filter(p:A=>Boolean):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(p:A=>Boolean):List[A]=list match {
case Nil => result
case h::t => p(h) match {
case true =>loop(t)(::(h,result))(p)
case false=>loop(t)(result)(p)
}
}
loop(self)(Nil)(p).invert
}
def take(n:Int):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(n:Int):List[A]=list match {
case Nil=> result
case h::t => n match {
case 0 =>result
case _ if n>0 =>loop(t)(::(h,result))(n-1)
}
}
loop(self)(Nil)(n).invert
}
def takeWhile(p:A=>Boolean):List[A]={
@tailrec
def loop(list: List[A])(result:List[A])(p:A=>Boolean):List[A]=list match {
case Nil=>result
case h::t=>p(h) match {
case true => loop(t)(::(h,result))(p)
case false => result
}
}
loop(self)(Nil)(p).invert
}
def forall(p:A=>Boolean):Boolean={
@tailrec
def loop(list: List[A])(result:Boolean)(p:A=>Boolean):Boolean=list match {
case Nil => result
case h::t => p(h) match {
case true => loop(t)(result)(p)
case false=>false
}
}
loop(self)(true)(p)
}
def exists(p:A=>Boolean):Boolean={
@tailrec
def loop(list: List[A])(result:Boolean)(p:A=>Boolean):Boolean=list match {
case Nil => result
case h::t => p(h) match {
case true=>true
case false=>loop(t)(result)(p)
}
}
loop(self)(false)(p)
}
}
case class ::[+A](h:A,t:List[A]) extends List[A]
case object Nil extends List[Nothing]
object List{
def empty[A]:List[A]=Nil
def apply[A](xs:A*):List[A]={
if(xs.isEmpty) empty[A]
else ::(xs.head,apply(xs.tail:_*))
}
}
Tree
trait Tree[+A] {
}
case object EmptyNode extends Tree[Nothing]{
override def toString: String = "Nil"
}
case class Node[+A](value:A,left:Tree[A],right:Tree[A]) extends Tree[A]
object Tree{
def node[A](value:A,left:Tree[A]=EmptyNode,right:Tree[A]=EmptyNode)=Node(value,left,right)
def empty[A]:Tree[A]=EmptyNode
}
剑指Offers上的有关题目(基于scala解法)
数组中只出现一次的数字
| 0 | 0 | 0 ^ 0 = 0 |
|---|---|---|
| 0 | 1 | 0 ^ 1 = 1 |
| 1 | 1 | 1 ^ 1 = 0 |
一个整型数组里除了一个数字外,其它数字均出现两次,如数组Array(2, 3, 6, 3, 2, 5, 5),返回结果应该为6,程序一句话就ok
scala写算法-List、Stream、以及剑指Offer里部分题目基于scala解法的更多相关文章
- 面试经典算法题集锦——《剑指 offer》小结
从今年 3 月份开始准备找实习,到现在校招结束,申请的工作均为机器学习/数据挖掘算法相关职位,也拿到了几个 sp offer.经历这半年的洗礼,自己的综合能力和素质都得到了一个质的提升. 实话说对于未 ...
- 《剑指offer》全部题目-含Java实现
1.二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. publi ...
- 剑指offer算法总结
剑指offer算法学习总结 节选剑指offer比较经典和巧妙的一些题目,以便复习使用.一部分题目给出了完整代码,一部分题目比较简单直接给出思路.但是不保证我说的思路都是正确的,个人对算法也不是特别在行 ...
- JS数据结构与算法 - 剑指offer二叉树算法题汇总
❗❗ 必看经验 在博主刷题期间,基本上是碰到一道二叉树就不会碰到一道就不会,有时候一个下午都在搞一道题,看别人解题思路就算能看懂,自己写就呵呵了.一气之下不刷了,改而先去把二叉树的基础算法给搞搞懂,然 ...
- 《剑指offer》面试题11: 数值的整数次方
面试题11: 数值的整数次方 剑指offer面试题11,题目如下 实现函数double power(double base,int exponent),求base的exponent次方, 不得使用库 ...
- 【剑指offer】旋转数组的最小值
採用二分查找的策略,重点要考虑一些边界情况:旋转了0元素.即输入的是一个升序排列的数组.仅仅包括一个数字的数组.有非常多反复数字的数组等. AC代码: #include<stdio.h> ...
- LeetCode—剑指 Offer学习计划
第 1 天 栈与队列(简单) 剑指 Offer 09. 用两个栈实现队列 class CQueue { public: CQueue() { } stack<int>s1,s2; void ...
- 【剑指offer】丑数
把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. leetcode上也 ...
- 剑指offer之字符串是否为数值
1. 题目 这是<剑指offer>上的一道题,刚开始觉得这是一道挺简单的题目,后来发现自己太年轻了,考虑的因素太少了,思考了而是分钟还是无从下手,看了作者的思路深深被他折服了,题目如下: ...
随机推荐
- HTML5学习指导路线
HTML5是现在热门的技术,经过8年的艰苦努力,该标准规范终于制定完成,在这里为想要学习HTML5初级程序员详细划分一下学习内容和步骤,让大家清楚的知道HTML5需要学什么?能够快速掌握HTML5开发 ...
- C语言之循环计数
#include<stdio.h>int main(){int num,count=0,i=0;scanf("%d",&num);num/=10;count++ ...
- CPU 虚拟化
前面 虚拟化技术总览 中从虚拟平台 VMM 的角度,将虚拟化分为 Hypervisor 模型和宿主模型,如果根据虚拟的对象(资源类型)来划分,虚拟化又可以分为计算虚拟化.存储虚拟化和网络虚拟化,再细一 ...
- azure上连续部署web
连续部署web 连续部署web,可以在第一次部署完web应用后,方便修改和自动提交代码部署新版本的web应用.其中自动提交使用github中的webhook,使代码在master上提交修改后可以自 ...
- [LeetCode] 74 Search a 2D Matrix(二分查找)
二分查找 1.二分查找的时间复杂度分析: 二分查找每次排除掉一半不合适的值,所以对于n个元素的情况来说: 一次二分剩下:n/2 两次:n/4 m次:n/(2^m) 最坏情况是排除到最后一个值之后得到结 ...
- django框架中的中间件
什么是中间件 中间件就是在url进入路由之前进行检测的一个类 也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对 ...
- 使用DataFlow表达ControlFlow的一些思考
一.控制流 从接触面向过程语言开始,使用控制流编程的概念已是司空见惯. if (condition) { // do something } else { // do something else } ...
- Nginx日志切割案例讲解,Nginx的知识讲解
Nginx 是一个非常轻量的 Web 服务器,体积小.性能高.速度快等诸多优点.但不足的是也存在缺点,比如在产生的访问日志文件一直就是一个,不会自动地进行切割,如果访问量很大的话,将会导致日志文件容量 ...
- “use strict” 严格模式使用(前端基础系列)
ECMAscript5添加一种严格模式的运行模式("use strict"),让你的js语句在更加严格的环境下进行运行: 一.主要作用: 消除版本javascript中一些不合理及 ...
- solr6.5.0版本(Windows安装图解)
此教程为solr6.5.0安装,自己制作,希望可以帮到你们.