后浪笔记一零二四

数组

如何实现随机访问?

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

数组是如何实现根据下标随机访问数组元素的: a[i]_address = base_address + i * data_type_size

  • data_type_size 表示数组中每个元素的大小
  • base_address 表示内存块的首地址

由于数组支持随机访问,所以根据下标随机访问的时间复杂度为 O(1)。

  • 注意:在数组上查找数据的时间复杂度并不为O(1),即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)
  • 低效的“插入”和“删除”
    • 由于在每个位置上插入元素的概率是一样的,所以平均时间复杂度为(1+2+...n)/n=O(n)
      • 如果数组只是被当作一个存储数据的集合,并不保证数据有序,
      • 为了避免大规模的数据搬移,可以直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。
    • 由于在每个位置上插入元素的概率是一样的,所以平均时间复杂度为(0+1+2+...n-1)/n=O(n)
      • 在某些特殊场景下,我们并不一定非得追求数组中数据的连续性
      • 这个时候,可以先记录下已经删除的数据,当数组没有更多空间存储数据时,才触发一次真正的删除操作
  • 为什么大多数编程语言中,数组要从 0 开始编号,而不是从 1 开始呢?
    • 从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移(offset)”
    • 使用偏移量模型时,数组元素的内存地址计算公式为:a[k]_address = base_address + k * type_size
    • 如果数组从1开始计数,那计算公式就是:a[k]_address = base_address + (k-1)*type_size
    • 对比两个公式,从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。
    • 对于 m·n的数组,a[i][j](i<m,j<n)的地址为: base_address + ( i * n + j) * type_size

用golang实现数组的基本操作

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
 * 1) 数组的插入、删除、按照下标随机访问操作;
 * 2)数组中的数据是int类型的;
 */

type Array struct {
    data   []int
    length uint
}

//为数组初始化内存
func NewArray(capacity uint) *Array {
    if capacity == 0 {
        return nil
    }
    return &Array{
        data:   make([]int, capacity, capacity),
        length: 0,
    }
}

func (this *Array) Len() uint {
    return this.length
}

//判断索引是否越界
func (this *Array) isIndexOutOfRange(index uint) bool {
    if index >= uint(cap(this.data)) {
        return true
    }
    return false
}

//通过索引查找数组,索引范围[0,n-1]
func (this *Array) Find(index uint) (int, error) {
    if this.isIndexOutOfRange(index) {
        return 0, errors.New("out of index range")
    }
    return this.data[index], nil
}

//插入数值到索引index上
func (this *Array) Insert(index uint, v int) error {
    if this.Len() == uint(cap(this.data)) {
        return errors.New("full array")
    }
    if index != this.length && this.isIndexOutOfRange(index) {
        return errors.New("out of index range")
    }

    for i := this.length; i > index; i-- {
        this.data[i] = this.data[i-1]
    }
    this.data[index] = v
    this.length++
    return nil
}

func (this *Array) InsertToTail(v int) error {
    return this.Insert(this.Len(), v)
}

//删除索引index上的值
func (this *Array) Delete(index uint) (int, error) {
    if this.isIndexOutOfRange(index) {
        return 0, errors.New("out of index range")
    }
    v := this.data[index]
    for i := index; i < this.Len()-1; i++ {
        this.data[i] = this.data[i+1]
    }
    this.length--
    return v, nil
}

//打印数列
func (this *Array) Print() {
    var format string
    for i := uint(0); i < this.Len(); i++ {
        format += fmt.Sprintf("|%+v", this.data[i])
    }
    fmt.Println(format)
}

func TestInsert(t *testing.T) {
    capacity := 10
    arr := NewArray(uint(capacity))
    for i := 0; i < capacity-2; i++ {
        err := arr.Insert(uint(i), i+1)
        if nil != err {
            t.Fatal(err.Error())
        }
    }
    arr.Print()

    arr.Insert(uint(6), 999)
    arr.Print()

    arr.InsertToTail(666)
    arr.Print()
}

func TestDelete(t *testing.T) {
    capacity := 10
    arr := NewArray(uint(capacity))
    for i := 0; i < capacity; i++ {
        err := arr.Insert(uint(i), i+1)
        if nil != err {
            t.Fatal(err.Error())
        }
    }
    arr.Print()

    for i := 9; i >= 0; i-- {
        _, err := arr.Delete(uint(i))
        if nil != err {
            t.Fatal(err)
        }
        arr.Print()
    }
}

func TestFind(t *testing.T) {
    capacity := 10
    arr := NewArray(uint(capacity))
    for i := 0; i < capacity; i++ {
        err := arr.Insert(uint(i), i+1)
        if nil != err {
            t.Fatal(err.Error())
        }
    }
    arr.Print()

    t.Log(arr.Find(0))
    t.Log(arr.Find(9))
    t.Log(arr.Find(11))
}

专题:

本文发表于 2021-07-15,最后修改于 2021-07-15。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 位图 下一篇 » 链表

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image