Swift 算法實戰(zhàn)之路:數(shù)組焊虏,字符串淡喜,集合,與字典


上次講解了基本的語法和一些Swift的小技巧诵闭。這期我們來看幾個最基本的數(shù)據(jù)結(jié)構(gòu):數(shù)組炼团,字符串,集合和字典疏尿。

數(shù)組

數(shù)組是最基本的數(shù)據(jù)結(jié)構(gòu)瘟芝。Swift中改變了以前Objective-C時代NSMutableArray和NSArray分開的做法,統(tǒng)一到了Array唯一的數(shù)據(jù)結(jié)構(gòu)褥琐。雖然看上去就一種數(shù)據(jù)結(jié)構(gòu)锌俱,其實它的實現(xiàn)有三種:

  • ContiguousArray<Element>:效率最高,元素分配在連續(xù)的元素上敌呈。如果數(shù)組是值類型(棧上操作)贸宏,Swift會自動調(diào)用Array的這種實現(xiàn);如果注重效率磕洪,推薦聲明這種類型吭练,尤其是在大量元素是類時,這樣做效果會很好析显。

  • Array<Element>:會自動橋接到 Objective-C 中的 NSArray线脚,如果是值類型,其性能與ContiguousArray無差別。

  • ArraySlice<Element>:它不是一個新的數(shù)組浑侥。只是一個片段姊舵,內(nèi)存上與原數(shù)組享用同一區(qū)域。

下面是數(shù)組最基本的一些運用寓落。

// 聲明一個不可修改的數(shù)組
let nums = [1, 2, 3]
let nums = [Int](repeating: 0, count: 5)

// 聲明一個可以修改的數(shù)組
var nums = [3, 1, 2]
// 增加一個元素
nums.append(4)
// 對原數(shù)組進行升序排序
nums.sort()
// 對原數(shù)組進行降序排序
nums.sort(by: >)
// 將原數(shù)組除了最后一個以外的所有元素賦值給另一個數(shù)組
let anotherNums = Array(nums[0 ..< nums.count - 1])

不要小看這些簡單的操作:數(shù)組可以依靠它們實現(xiàn)更多的數(shù)據(jù)結(jié)構(gòu)括丁。Swift雖然不像Java中有現(xiàn)成的隊列和棧,但我們完全可以用數(shù)組配合最簡單的操作實現(xiàn)這些數(shù)據(jù)結(jié)構(gòu)伶选,下面就是用數(shù)組實現(xiàn)棧的示例代碼史飞。

// 用數(shù)組實現(xiàn)棧
class Stack {
  var stack: [AnyObject]
  var isEmpty: Bool { return stack.isEmpty }
  var peek: AnyObject? { return stack.last }

  init() {
    stack = [AnyObject]()
  }
  
  func push(object: AnyObject) {
    stack.append(object)
  }
  
  func pop() -> AnyObject? {
    if (!isEmpty()) {
      return stack.removeLast()
    } else {
      return nil
    }
  }
}

最后特別強調(diào)一個操作:reserveCapacity()。它用于為原數(shù)組預留空間仰税,防止數(shù)組在增加和刪除元素時反復申請內(nèi)存空間或是創(chuàng)建新數(shù)組构资,特別適用于創(chuàng)建和removeAll()時候進行調(diào)用,為整段代碼起到提高性能的作用陨簇。

字典和集合

這兩個數(shù)據(jù)結(jié)構(gòu)經(jīng)常使用的原因在于吐绵,查找數(shù)據(jù)的時間復雜度為O(1)。一般字典和集合要求它們的Key都必須遵守Hashable協(xié)議河绽,Cocoa中的基本數(shù)據(jù)類型都滿足這一點己单;自定義的class需要實現(xiàn)Hashable,而又因為Hashable是對Equable的擴展耙饰,所以還要重載 == 運算符纹笼。

下面是關(guān)于字典和集合的一些實用操作:

let oddNums: Set = [1, 3, 5, 7, 9]
let primeNums: Set = [3, 5, 7, 11, 13]

// 交集、并集苟跪、差集
let oddAndPrimeNums = primeNums.intersection(oddNums)
let oddOrPrimeNums = primeNums.union(oddNums)
let oddNotPrimeNums = oddNums.subtracting(primeNums)

// 用字典和高階函數(shù)計算字符串中每個字符的出現(xiàn)頻率
Dictionary("hello".map { ($0, 1) }, uniquingKeysWith: +)

集合和字典在實戰(zhàn)中經(jīng)常與數(shù)組配合使用廷痘,請看下面這道算法題:

給一個整型數(shù)組和一個目標值,判斷數(shù)組中是否有兩個數(shù)字之和等于目標值

這道題是傳說中經(jīng)典的2Sum件已,我們已經(jīng)有一個數(shù)組記為nums牍疏,也有一個目標值記為target,最后要返回一個Bool值拨齐。
最粗暴的方法就是每次選中一個數(shù)鳞陨,然后遍歷整個數(shù)組,判斷是否有另一個數(shù)使兩者之和為target瞻惋。這種做法時間復雜度為O(n^2)厦滤。
采用集合可以優(yōu)化時間復雜度。在遍歷數(shù)組的過程中歼狼,用集合每次保存當前值掏导。假如集合中已經(jīng)有了目標值減去當前值,則證明在之前的遍歷中一定有一個數(shù)與當前值之和等于目標值羽峰。這種做法時間復雜度為O(n)趟咆,代碼如下添瓷。

func twoSum(nums: [Int], _ target: Int) -> Bool {
  var set = Set<Int>()
  
  for num in nums {
    if set.contains(target - num) {
      return true
    }
    
    set.insert(num)
  }
  
  return false
}

如果把題目稍微修改下,變?yōu)?/p>

給定一個整型數(shù)組中有且僅有兩個數(shù)字之和等于目標值值纱,求兩個數(shù)字在數(shù)組中的序號

思路與上題基本類似鳞贷,但是為了方便拿到序列號,我們采用字典虐唠,時間復雜度依然是O(n)搀愧。代碼如下。

func twoSum(nums: [Int], _ target: Int) -> [Int] {
  var dict = [Int: Int]()
        
  for (i, num) in nums.enumerated() {
    if let lastIndex = dict[target - num] {
      return [lastIndex, i]   
    } else {
      dict[num] = i
    }
  }
        
  fatalError("No valid output!")
}

字符串

字符串在算法實戰(zhàn)中極其常見疆偿。在Swift中咱筛,字符串不同于其他語言(包括Objective-C),它是值類型而非引用類型杆故。首先還是列舉一下字符串的通常用法:

// 字符串和數(shù)字之間的轉(zhuǎn)換
let str = "3"
let num = Int(str)
let number = 3
let string = String(num)

// 字符串長度
let len = str.count

// 訪問字符串中的單個字符迅箩,時間復雜度為O(1)
let char = str[str.index(str.startIndex, offsetBy: n)]

// 修改字符串
str.remove(at: n)
str.append("c")
str += "hello world"

// 檢測字符串是否是由數(shù)字構(gòu)成
func isStrNum(str: String) -> Bool {
  return Int(str) != nil
}

// 將字符串按字母排序(不考慮大小寫)
func sortStr(str: String) -> String {
  return String(str.sorted())
}

下面是本篇的精華所在,我們來一起看一道以前的Google面試題处铛。

Given an input string, reverse the string word by word.
A word is defined as a sequence of non-space characters.
The input string does not contain leading or trailing spaces and the words are always separated by a single space.
For example,
Given s = "the sky is blue",
return "blue is sky the".
Could you do it in-place without allocating extra space?

這道題目一看好簡單饲趋,不就是翻轉(zhuǎn)字符串的翻版嗎?這種方法有以下兩個問題

  • 每個單詞長度不一樣
  • 空格需要特殊處理

這樣一來代碼寫起來會很繁瑣而且容易出錯罢缸。不如我們先實現(xiàn)一個字符串翻轉(zhuǎn)的方法。

fileprivate func _reverse<T>(_ chars: inout [T], _ start: Int, _ end: Int) {
  var start = start, end = end
  
  while start < end {
    _swap(&chars, start, end)
    start += 1
    end -= 1
  }
}

fileprivate func swap<T>(_ chars: inout [T], _ p: Int, _ q: Int) {
  (chars[p], chars[q]) = (chars[q], chars[p])
}

有了這個方法投队,我們就可以實行下面兩種字符串翻轉(zhuǎn):

  • 整個字符串翻轉(zhuǎn)枫疆,"the sky is blue" -> "eulb si yks eht"
  • 每個單詞作為一個字符串單獨翻轉(zhuǎn),"eulb si yks eht" -> "blue is sky the"

整體思路有了敷鸦,我們就可以解決這道問題了

func reverseWords(s: String?) -> String? {
  guard let s = s else {
    return nil
  }

  var chars = Array(s), start = 0
  _reverse(&chars, 0, chars.count - 1)
  
  for i in 0 ..< chars.count {
    if i == chars.count - 1 || chars[i + 1] == " " {
      _reverse(&chars, start, i)
      start = i + 2
    }
  }
  
  return String(chars)
}

時間復雜度還是O(n)息楔,整體思路和代碼簡單很多。

總結(jié)

Swift中數(shù)組扒披、字符串值依、集合以及字典是最基本的數(shù)據(jù)結(jié)構(gòu),但是圍繞這些數(shù)據(jù)結(jié)構(gòu)的問題層出不窮碟案。幸運的是解決方法也并不是千變?nèi)f化愿险、高深莫測,大家做好相應(yīng)的積累即可价说。下期我們講鏈表辆亏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鳖目,隨后出現(xiàn)的幾起案子扮叨,更是在濱河造成了極大的恐慌,老刑警劉巖领迈,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彻磁,死亡現(xiàn)場離奇詭異碍沐,居然都是意外死亡,警方通過查閱死者的電腦和手機衷蜓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門累提,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恍箭,你說我怎么就攤上這事刻恭。” “怎么了扯夭?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵鳍贾,是天一觀的道長。 經(jīng)常有香客問我交洗,道長骑科,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任构拳,我火速辦了婚禮咆爽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘置森。我一直安慰自己斗埂,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布凫海。 她就那樣靜靜地躺著呛凶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪行贪。 梳的紋絲不亂的頭發(fā)上漾稀,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音建瘫,去河邊找鬼崭捍。 笑死,一個胖子當著我的面吹牛啰脚,可吹牛的內(nèi)容都是我干的殷蛇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼橄浓,長吁一口氣:“原來是場噩夢啊……” “哼晾咪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贮配,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤谍倦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泪勒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昼蛀,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡宴猾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叼旋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仇哆。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夫植,靈堂內(nèi)的尸體忽然破棺而出讹剔,到底是詐尸還是另有隱情,我是刑警寧澤详民,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布延欠,位于F島的核電站,受9級特大地震影響沈跨,放射性物質(zhì)發(fā)生泄漏由捎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一饿凛、第九天 我趴在偏房一處隱蔽的房頂上張望狞玛。 院中可真熱鬧,春花似錦涧窒、人聲如沸心肪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硬鞍。三九已至,卻和暖如春呜象,著一層夾襖步出監(jiān)牢的瞬間膳凝,已是汗流浹背碑隆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工恭陡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人上煤。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓休玩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親劫狠。 傳聞我的和親對象是個殘疾皇子拴疤,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容