類比動態數組的實現(自動擴容),完成數組的增刪查改操作:
自訂的數組結構
type Array struct { data []interface{} // 泛型數組 size int // 元素數量}
數組的介面
type ArrayInterface interface { // 添加 Add(int, interface{}) // 插入元素 AddLast(interface{}) AddFirst(interface{}) // 刪除 Remove(int) interface{} RemoveFirst() interface{} RemoveLast() interface{} // 尋找 Find(interface{}) int // 尋找元素返回第一個索引 FindAll(interface{}) []int // 尋找元素返回所有索引 Contains(interface{}) bool // 尋找是否存在元素 Get(int) interface{} // 修改 Set(int, interface{}) // 基本方法 GetCapacity() int // 獲得數組容量 GetSize() int // 獲得元素個數 IsEmpty() bool // 查看數組是否為空白 // 自調用的方法 resize() // 用於數組的擴、縮容}
數組實現
resize 規則
數組 resize
規則:
- 超過數組容量,按照當前容量的 2 倍擴容,
- 數組元素個數為當前容量 1/4 時,鎖容為當前容量的一半。
如果在 1/2 時縮容,會導致擴容臨界點添加、刪除一個元素都是 O(n) 複雜度的情況。
數組實現
建構函式
Go 沒有提供建構函式,我們可以聲明一個函數代替
// 獲得自訂數組,參數為數組的初始長度func GetArray(capacity int) *Array { arr := &Array{} arr.data = make([]interface{}, capacity) arr.size = 0 return arr}
基本方法
// 獲得數組容量func (a *Array) GetCapacity() int { return len(a.data)}// 獲得數組元素個數func (a *Array) GetSize() int { return a.size}// 判斷數組是否為空白func (a *Array) IsEmpty() bool { return a.size == 0}
數組容量調整
// newCapacity 新數組容量// 邏輯:聲明新的數組,將原數組的值 copy 到新數組中func (a *Array) resize(newCapacity int) { newArr := make([]interface{}, newCapacity) for i := 0; i < a.size; i++ { newArr[i] = a.data[i] } a.data = newArr}
尋找元素
尋找元素指輸入元素返回元素的索引
// 獲得元素的首個索引,不存在則返回 -1func (a *Array) Find(element interface{}) int { for i:= 0; i < a.size; i++ { if element == a.data[i] { return i } } return -1}// 獲得元素的所有索引,返回索引組成的切片func (a *Array) FindAll(element interface{}) (indexes []int) { for i := 0; i < a.size; i++ { if element == a.data[i] { indexes = append(indexes, i) } } return}// 查看數組是否存在元素,返回 boolfunc (a *Array) Contains(element interface{}) bool { if a.Find(element) == -1 { return false } return true}// 獲得索引對應元素,需要判斷索引有效範圍func (a *Array) Get(index int) interface{} { if index < 0 || index > a.size - 1 { panic("Get failed, index is illegal.") } return a.data[index]}
修改元素
修改索引對應元素值func (a *Array) Set(index int, element interface{}) { if index < 0 || index > a.size - 1 { panic("Set failed, index is illegal.") } a.data[index] = element}
添加元素
添加元素需要考慮擴容問題
func (a *Array) Add(index int, element interface{}) { if index < 0 || index > a.GetCapacity() { panic("Add failed, require index >= 0 and index <= capacity") } // 數組已滿則擴容 if a.size == len(a.data) { a.resize(2 * a.size) } // 將插入的索引位置之後的元素後移,騰出插入位置 for i := a.size - 1; i > index; i-- { a.data[i + 1] = a.data[i] } a.data[index] = element // 維護數組元素的數量 a.size++}func (a *Array) AddLast(element interface{}) { a.Add(a.size, element)}func (a *Array) AddFirst(element interface{}) { a.Add(0, element)}
刪除元素
刪除元素需要考慮縮容問題
func (a *Array) Remove(index int) interface{} { if index < 0 || index >= a.size { panic("Remove failed, index is illegal.") } removeEle := a.data[index] // 從 index 之後的元素,都向前移動一個位置 for i := index + 1; i < a.size; i++ { a.data[i-1] = a.data[i] } a.size-- // 清理最後一個元素 a.data[a.size] = nil // 考慮邊界情況,不能 resize 為0 if a.size == len(a.data)/4 && len(a.data)/2 != 0 { a.resize(len(a.data) / 2) } return removeEle}func (a *Array) RemoveFirst() interface{} { return a.Remove(0)}func (a *Array) RemoveLast() interface{} { return a.Remove(a.size - 1)}
重寫 String 方法
重寫數組列印時的展示形式
func (a *Array) String() string { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("Array: size = %d, capacity = %d\n", a.size, a.GetCapacity())) buffer.WriteString("[") for i := 0; i < a.size; i++ { buffer.WriteString(fmt.Sprint(a.data[i])) if i != a.size - 1 { buffer.WriteString(",") } } buffer.WriteString("]") return buffer.String()}
最終測試
func main() { arr := GetArray(10) for i := 0; i < 10; i++ { arr.AddLast(i) } fmt.Println(arr) arr.Add(1, 100) fmt.Println(arr) arr.AddFirst(-1) fmt.Println(arr)}
輸出結果:
Array: size = 10, capacity = 10[0,1,2,3,4,5,6,7,8,9]Array: size = 11, capacity = 20[0,100,2,2,3,4,5,6,7,8,9]Array: size = 12, capacity = 20[-1,100,100,2,2,3,4,5,6,7,8,9]
時間複雜度分析
添加操作
- Add(int, interface{}) O(n)
- AddLast(interface{}) O(1)
- AddFirst(interface{}) O(n)
實際 AddLast 的均攤複雜度是 O(1),因為涉及到擴容的操作。長度為 n 的數組添加 n + 1 個元素,會操作 2n + 1。因為第 n + 1 次導致擴容的 n 次操作。
刪除操作
- Remove(int) interface{} O(n)
- RemoveFirst() interface{} O(n)
- RemoveLast() interface{} O(1)
尋找操作
- Find(interface{}) int O(n)
- FindAll(interface{}) []int O(n)
- Contains(interface{}) bool O(n)
- Get(int) interface{} O(1)
修改操作
- Set(int, interface{}) O(1)