有序陣列的一種實現

2021-09-16 23:57:09 字數 3601 閱讀 3780

你可以去 github 上檢視全部** ,但下面會有更多講解。

實現部分很直觀,因為把所有東西都橋接到用來實際儲存的陣列那裡。由於 index 是 int 型別,你甚至不用自己實現index(_:offsetby:)distance(from:to:)函式,標準庫已經提供了預設的實現。

let sorted: sortedarray = [3,1,2]
extension sortedarray: expressiblebyarrayliteral where element: comparable
也許 swift 4 中可以實現 conditional protocol conformance。

使用有序陣列的好處之一就是可以通過 binary search 快速找到某乙個陣列元素。在這裡 binary search 的時間複雜度 是 log n 而不是線性的 。

為了實現該演算法,我首先寫了乙個輔助函式search(for:)。你可以去 github 上檢視完整** ;這裡我想討論一下返回的型別:

fileprivate enum match

extension sortedarray

}

了:

extension sortedarray 

}/// 返回乙個布林值,表示這個序列是否包含給定的元素

////// - complexity: o(_log(n)_),_n_ 是陣列的大小

public func contains(_ element: element) -> bool

}

需要注意的是,這裡的實現不止比標準庫里的實現更高效,而且通用性更強. 標準庫里這個方法還要求where iterator.element: comparable的約束,而 sortedarray 總是擁有乙個排序演算法,所以不需要這樣的約束。

下乙個任務是利用 binary search 的優勢去提高插入元素的效率。我決定提供兩個插入函式: 第乙個會在正確的位置去插入單個元素,保持陣列有序。它利用 binary search 去找到正確的插入位置,複雜度為o(log n)。插入新元素到非空陣列裡,最糟糕的時間複雜度是 o(n),因為全部已有元素不得不移動位置去提供空間。

第二個函式可以插入一組序列。這裡我選擇先把所有元素都插入到陣列最後,然後進行一次重新排序. 這比重複尋找正確的插入位置更快(如果插入的陣列元素個數大於log n)。

extension sortedarray 

/// 插入 `elements` 裡的所有元素到 `self` 裡,保持陣列有序

/// 這會比每個元素都單獨插入一遍更快

/// 因為我們只需要重新排序一次

////// - complexity: o(_n * log(n)_),_n_ 是插入後陣列的大小

public mutating func insert(contentsof newelements: s) where s.iterator.element == element

}

extension sortedarray 

/// 返回集合裡的最大值

////// - complexity: o(1).

@warn_unqualified_access

public func max() -> element?

}

當你在型別的內部實現中呼叫這些函式,卻沒有顯式地寫明self.的字首時,@warn_unqualified_access如同index(of:)contains(_:)一樣,我們的min()max()更加通用,因為它們不需要元素是 comparable 的. 我們獲得了更高的效率,更少的約束。

sortedarray 裡的實現並不會重寫預設實現(因為只要協議裡定義的方法才可以被重寫),他們只是附屬品。當你直接使用 sortedarray 的時候,更加高效的實現會讓你收益。但當它們作為泛型時將永遠不會被呼叫。例如:

let numbers = sortedarray(unsorted: [3,2,1])

// 這會直接呼叫 sortedarray.max()

let a = numbers.max()

func mymax(_ sequence: s ) -> s.iterator.element?

where s.iterator.element: comparable

// 這種寫法呼叫的是 sequence.max() 了(更低效的版本)

let b = mymax(numbers)

我們沒辦法改變這個 "bug",swift-evolution 有討論過讓這些方法變成協議的一部分(我不確定這是不是乙個好的做法)。

2017.02.09 更新: 我忘了index(of:)contains(_:)這些方法,現在還不是 sequence 和 collection 的一部分,因為他們需要 iterator.element 是 equatable 的。而現在還沒有方法去定義乙個泛型協議。brent royal-gordon 在 swift-evolution 裡進行了相關的討論並且提問泛型協議是否應該加入 swift 裡。

最後我還是決定放棄這種做法,因為長時間持有乙個 arrayslice 的例項不是一件好事. 就算只持有乙個非常大的陣列的切片,也會一直間接持有那個大的陣列,這會導致非常高的記憶體占用,這是使用者不想看到的,就算基底 array 的記憶體不會洩露,但切片還是會讓它無法及時釋放。

就 swift 3 而言,swift 無法在跨 module 的情況下表現出泛型型別的優勢。換而言之,如果你在**裡使用了sortedarray,並且 sortedarray 是定義在另乙個 module 的時候,編譯器無法為元素為 int 的 array 優化生成**,只能按照常規的方式,將每乙個泛型值打包到乙個容器裡,然後通過 witness table 進行方法派發。這很容易造成你的**在執行時被拖慢 一到兩個數量級 。

當前版本的 swift 編譯器無法約束從外部 module 引入的泛型(標準庫除外)。…這個限制會讓外部 module 引入的集合型別效能大幅下降。特別當集合中的元素是簡單的,被極度優化的值型別,例如 int,甚至是 string。 依賴引入的 module,你的集合裝填基礎資料型別時效能會有 10-200 倍的下降。

標準庫是唯一乙個例外,標準庫里的型別對於任何 module 都是可見的。

完整的實現 總共兩百多行,包括注釋。

就像你看到的,自定義集合型別有很多需要考量的東西。而且我們都只考慮了介面的設計,我們甚至還沒接觸底層的實現。但我覺得這些付出都是有回報的。我們獲得了乙個行為和內建集合型別完全一致的型別,相容序列和集合操作的同時還會根據演算法自我變化。

雖然跨 module 使用泛型型別確實對於效能有很大的影響。

有序鍊錶的合併的一種實現

概述 合併有序鍊錶的一種實現 在leetcode刷題的時候,碰到有序鍊錶的合併問題,第21題是兩個鍊錶的合併,第23提是k個鍊錶的合併,第23題利用第21題的解法,將兩個鍊錶合成乙個,再把合成的鍊錶作為新煉表和下乙個鍊錶合併即可。合併鍊錶有很多方法 a.將所有節點拆開,放到陣列裡進行排序,再放回鍊錶...

有序陣列的合併

includeusing namespace std define size 1024 1 如果可以申請輔助空間,那麼從前從後倒是沒什麼所謂吧,這就像是歸併的一次歸併了。void merge 1 int arr,int lenarr,int brr,int lenbrr while low1 len...

有序陣列的插入

習題1.9 有序陣列的插入 20 分 本題要求將任一給定元素插入從大到小排好序的陣列中合適的位置,以保持結果依然有序。函式介面定義 bool insert list l,elementtype x 其中list結構定義如下 typedef int position typedef struct ln...