Swift 算法实战之路:基本语法与技巧

Swift是苹果新推出的编程语言,也是苹果首个开源语言。相比于原来的Objective-C,Swift要更轻便和灵活。笔者最近使用Swift实践了大量的算法(绝大部分是硅谷各大公司的面试题),将心得体会总结于下。此文并不是纯粹讨论Swift如何实现某一个具体的算法或者数据结构,如冒泡排序、深度优先遍历,或是树和栈,而是总结归纳一些Swift常用的语法和技巧,以便大家在解决面试题中使用。

基本语法

先来看下面一段代码

func swap(chars:[Character],  p: Int, q: Int) {
  var temp = chars[p]
  chars[p] = chars[q]
  chars[q] = temp
}
// Assume array is a character array and it has enough elements
swap(array, p: 0, q: 1)

上面代码实现了一个非常简单的功能,就是交换一个数组中的两个值。乍一看非常正确,实际上存在以下几个问题。

在第一个参数前应该加上 inout 关键字。因为在Swift中,struct都是按值传递,class是按引用传递;数组和字典都是struct。所以要改变原来的chars数组,在其前部加入inout关键字,表示是按引用传递。
p 和 q 之前应该加入下划线。Swift默认函数的第一个参数是局部(local)变量,而后续参数都是外部(external)变量,外部变量必须声明。如果在p和q前加上下划线,则在调用函数时无需声明外部变量,这样调用起来更简便。
temp前的var应该改为let。let用于声明常量(constant),var用于声明变量(variable)。swap函数中,temp并未进行修改,所以应当视为常量,用let来声明。

修改过后的代码为

func swap(inout chars:[Character],  _ p: Int, _ q: Int) {
  let temp = chars[p]
  chars[p] = chars[q]
  chars[q] = temp
}
// Assume array is a character array and it has enough elements
swap(&array, 0, 1)

再来看一段代码

func toZero(x: Int) -> Int {
  while x > 0 {
    x -= 1
  }
  return x
}

这里在 x -= 1 处会报错。原因是函数中所有的参数是常量(let),是不可以修改的。解决的方法是在函数中写上var x = x,之后就可以对 x 进行修改了

循环

Swift循环分为for和while两种,注意他们的结构跟传统的 Java, C/C++有很大区别,笔者将其总结如下

// Assume names is an array holds enough Strings
// for loop
for name in names { }
for i in 0 ... names.count - 1 { }
for i in 0 ..< names.count { }
for _ in 0 ..< names.count { }
for name in names.reverse() { }
for i in 0.stride(through: names.count - 1, by 2) { }
for i in 0.stride(to: names.count, by: 2) { }

// while loop
var i = 0
while i < names.count { }
repeat { } while i < names.count

以上代码非常简单。需要说明的有两个,一个是 for _ in 0 ..< names.count { } 。当我们不需要数组中每一个具体的元素值时,我们就可以用下划线来代表序号。
另一个是是 repeat { } while i < names.count 。这个相当于我们熟悉(java)的 do { } while (i < names.length)。

排序

swift排序效率很高,写法也很简洁。笔者将其总结如下

// Sort an Int array,ascending
nums.sortInPlace({$0 < $1})

// Sort an Int array without mutating the original one, ascending
var sortedNums = nums.sort({$0 < $1})

// Sort an array with custom-defined objects, ascending
timeIntervals.sortInPlace({$0.startTime < $1.startTime})

// Sort keys in a dictionary according to its value, ascending
let keys = Array(dict.keys)
var sortedKeys = keys.sort() {
  let value0 = dict[$0]
  let value1 = dict[$1]
  return value0 < value1
}

活用Guard语句

使用Guard语句可以让逻辑变得非常清楚,尤其是在处理算法问题的时候,请看下面的代码

// Returns the index of the first occurrence of needle in haystack,
// or -1 if needle is not part of haystack
func strStr(haystack: String, _ needle: String) -> Int {
  var hChars = [Character](haystack.characters)
  var nChars = [Character](needle.characters)

  if hChars.count < nChars.count {
    return -1
  }
  if hChars.count == 0 {
    return -1
  }

  for i in 0 ... hChars.count - nChars.count {
    if hChars[i] == nChars[0] {
      for j in 0 ..< nChars.count {
        if hChars[i + j] != nChars[j] {
          break
        } else {
          if j == nChars.count - 1 {
            return i
          }
        }
      }
    }
  }
  return -1
}

上面这段代码是求字符串needle在字符串haystack里首次出现的位置。我们发现for循环中的嵌套非常繁琐,但是如果使用Guard语句代码会变得清晰很多:

func strStr(haystack: String, _ needle: String) -> Int {
  var hChars = [Character](haystack.characters)
  var nChars = [Character](needle.characters)

  guard hChars.count >= nChars.count else {
    return -1
  }
  if nChars.count == 0 {
    return 0
  }

  for i in 0 ... hChars.count - nChars.count {
    guard hChars[i] == nChars[0] else {
      continue
    }

    for j in 0 ..< nChars.count {
      guard hChars[i + j] == nChars[j] else {
        break
      }
      if j == nChars.count - 1 {
        return i
      }
    }
  }

  return -1
}

Guard语句的好处是判断条件永远是我们希望的条件而不是特殊情况,且成功避免了大量的if嵌套。

另外在上面代码中,为何要将字符串转化成数组进行处理?因为Swift中没有方法能够以O(1)的时间复杂度取得字符串中的字符,我们常用的string.startIndex.advancedBy(n)方法,其时间复杂度为O(n)。所以笔者在这里以空间换时间的方法进行了优化。
总结

Swift是一门独特的语言,本文对其基本知识和语法进行了适当归纳,文中提到的都是基本细节。下期我们会讨论更加进阶的内容。

时间: 2024-10-15 19:57:48

Swift 算法实战之路:基本语法与技巧的相关文章

swift语言实战晋级-第9章 游戏实战-跑酷熊猫-7-8 移动平台的算法

在上个小节,我们完成了平台的产生.那么我们来实现一下让平台移动.平台的移动,我们只需要在平台工厂类中写好移动的方法,然后在GameScene类中统一控制就行了. 在GameScene类中,有个update方法会每隔一定的时间调用,用来控制平台的移动再适合不过了. 首先,我们在GameScene类中定义一个移动速度: //移动速度 var moveSpeed:CGFloat = 15 然后在update方法方法中调用平台工厂类的移动方法: override func update(currentT

swift语言实战晋级-第9章 游戏实战-跑酷熊猫-9-10 移除平台与视差滚动

原文:swift语言实战晋级-第9章 游戏实战-跑酷熊猫-9-10 移除平台与视差滚动 9.9 移除场景之外的平台 用为平台是源源不断的产生的,如果不注意销毁,平台就将越积越多,虽然在游戏场景中看不到.几十个还看不出问题,那几万个呢?几百万个呢? 所以我们来看看怎么移除平台,那什么样的平台需要移除呢?又如何移除呢?之前我们也说过,当平台完全移除游戏场景的时候就可以进行移除操作了.需要做两个操作,1从平台工厂类中移除,2从平台数组中移除. 而且,因为平台是一个接一个出现的,所以我们不需要每次都遍历

我的算法学习之路

关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口--况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的. 这篇文章讲了什么? 我这些年学习数据结构和算法的总结. 一些不错的算法书籍和教程. 算法的重要性. 初学 第一次接触数据结构是在大二下学期的数据结构课程.然而这门课程并没有让我入门--当时自己正忙于倒卖各种MP3和耳机,对于这些课程根本就不屑一顾--反正最后考试划个重点也能过,于是这门整个计算机专业本

软件工程(C编码实践篇)课程总结——我的第一次代码实战之路

课程内容来自网易云课堂-软件工程(C编码实践篇)--中国科学技术大学孟宁老师主讲! 作者:魏红 魏红<软件工程(C编码实践篇)>MOOC课程作业http://mooc.study.163.com/learn/USTC-1000002006 一.心得体会 在写这篇博客时,我的心情不是想象中的如释重负,而是感慨和激动.作为一名理科生,代码对于我来说,陌生.遥远.不曾触及,所以当我开始接触到实验,需要自己动手的时候,我就崩溃了.哪怕是最简单的hello world小程序,我都折腾了好久,第一次的实验

Peng Gong:我的算法学习之路

原文出处: Lucida (@peng_gong) 关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口--况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的. 这篇文章讲了什么? 我这些年学习数据结构和算法的总结. 一些不错的算法书籍和教程. 算法的重要性. 初学 第一次接触数据结构是在大二下学期的数据结构课程.然而这门课程并没有让我入门--当时自己正忙于倒卖各种MP3和耳机,对于这些课程根本就不屑一顾--

大神的算法学习之路

http://zh.lucida.me/ 大神的算法学习之路,布布扣,bubuko.com

转----我的算法学习之路

我的算法学习之路 关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口——况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的. 原文链接:http://zh.lucida.me/blog/on-learning-algorithms/ 原文作者:Lucida 这篇文章讲了什么? 我这些年学习数据结构和算法的总结. 一些不错的算法书籍和教程. 算法的重要性. 初学 第一次接触数据结构是在大二下学期的数据结构课程

Swift游戏实战-跑酷熊猫 01 创建工程导入素材

在这节里,我们将建立一个游戏工程,并导入一些必要的素材,例如序列帧动画文件,声音素材文件.动画文件我们使用atlas形式.在打包发布或者模拟器测试的时候,它会将整个.atlas文件夹下的图片打包成一张png图片. 要点: texture atlas :它包含了一组相关的texture.使用atlas可以提高性能 项目文件地址 http://yun.baidu.com/share/link?shareid=3824235955&uk=541995622 Swift游戏实战-跑酷熊猫系列 00 游戏

Swift游戏实战-跑酷熊猫 00 游戏预览

这个系列我们将要一起来做一个这样的游戏 Swift游戏实战-跑酷熊猫 00 游戏预览,布布扣,bubuko.com