4. 数组:为什么很多编程语言中数组都从0开始编号?

1. 数组的定义

  • 数组Array是一种 线性表 数据结构。 它用一组 连续的内存空间,来存储一组具有 相同类型的数据。 数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

2. 低效的“插入”和“删除”

  • 假设数组的长度为n, 讲一个数据插入到数组的第k个位置

    • 最优时间复杂度 O(1)
    • 最坏时间复杂度 O(n)
    • 平均时间复杂度 O(n)
  • 如果数组中存储的数据没有任何规律, 可以把要插入位置的元素和最后一个(指定位置)元素互换,时间复杂度为O(1)

  • 数组的删除和插入类似, 最后时间复杂度为O(1), 最坏时间复杂度为O(n), 平均时间复杂度为O(n)

  • JVM 标记清除垃圾回收算法的核心思想

    • 大多数主流虚拟机采用可达性分析算法来判断对象是否存活,在标记阶段,会遍历所有 GC ROOTS,将所有 GC ROOTS 可达的对象标记为存活。只有当标记工作完成后,清理工作才会开始。

    • 不足:1.效率问题。标记和清理效率都不高,但是当知道只有少量垃圾产生时会很高效。2.空间问题。会产生不连续的内存空间碎片。

3. 警惕数组访问越界问题

4. 用数组什么情况更合适

  • Java ArrayList 无法存储基本类型,比如 int、long,需要封装为 Integer、Long 类,而 Autoboxing、Unboxing 则有一定的性能消耗,如果特别关注性能,或者希望使用基本类型,就可以选用数组
  • 如果数据大小事先已知, 并且对数据的操作非常简单, 用不到ArrayList提供的大部分方法, 也可以直接使用数组
  • 当要表示多维数组时,用数组往往会更加直观

5. 二维数组内存寻址:

  • 对于 m * n 的数组,a [ i ][ j ] (i < m,j < n)的地址为:address = base_address + ( i * n + j) * type_size
int main(int argc, char* argv[]){
    int i = 0;
    int arr[3] = {0};
    for(; i<=3; i++){
        arr[i] = 0;
        printf("hello world\n");
    }
    return 0;
}


对于死循环那个问题,要了解栈这个东西。栈是向下增长的,首先压栈的i,a[2],a[1],a[0],这是我在我vc上调试查看汇编的时候看到的压栈顺序。相当于访问a[3]的时候,是在访问i变量,而此时i变量的地址是数组当前进程的,所以进行修改的时候,操作系统并不会终止进程。

6. 代码部分

6.1. Array的基本操作

  • Java版本
/**
 * @author xiaofan
 * 1.数组的插入、删除、按照下标随机访问操作
 * 2.数组中的数据时int类型的
 */
public class ArrayJavaDemo {
    // 定义整形数据data保存类型
    public int data[];
    // 定义数组中元素实际个数
    private int size;

    // 构造方法定义数组大小
    public ArrayJavaDemo(int capacity) {
        this.data = new int[capacity];
        this.size = 0;
    }

    // 根据索引找到指定的元素并返回
    public int find(int index) {
        if(index < 0 || index >= size) return -1;
        return data[index];
    }

    // 插入元素,头部插入,尾部插入
    public boolean insert(int index, int value) {
        // 数组空间已满
        if(size == data.length) {
            System.out.println("没有可以插入的位置");
            return false;
        }
        // 位置不合法
        if(index < 0 || index > size) {
            System.out.println("位置不合法");
            return false;
        }
        // 位置合法
        for(int i = size; i > index; --i) {
            data[i] = data[i - 1];
        }
        data[index] = value;
        ++size;
        return true;
    }

    // 根据索引删除数组中的元素
    public boolean delete(int index) {
        if(index < 0 || index >= size) return false;
        for(int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        --size;
        return true;
    }

    // 打印数组
    public void printAll() {
        for (int i = 0; i < size; ++i) {
            System.out.print(data[i] + " ");
        }
        System.out.println();
    }


    public static void main(String[] args) {
        ArrayJavaDemo array = new ArrayJavaDemo(5);
        array.printAll();
        array.insert(0, 3);
        array.insert(0, 4);
        array.insert(1, 5);
        array.insert(3, 9);
        array.insert(3, 10);
        int res = array.find(5);
        System.out.println(res);
        array.printAll();
    }
}
  • Scala版本
class ArrayDemo(capacity: Int) {

  var data: Array[Char] = new Array[Char](capacity)
  // 数组中元素的实际个数
  var length: Int = 0

  def find(index: Int): Char = {
    if(index < 0 || index >= length) {
      return (-1).toChar
    }
    data(index)
  }

  def insert(index: Int, value: Char): Boolean = {
    if(length == capacity) {
      return false
    }

    if(index < 0 || index >= capacity) {
      return false
    }
    // 注意: 取不到until后面的值,可以指定步长
    for(i <- length until index by -1) {
      data(i) = data(i - 1)
    }
    data(index) = value
    length += 1
    true
  }

  def delete(index: Int): Char = {
    if(length == 0) {
      throw new IllegalStateException("array is empty")
    }

    if(index >= length) {
      throw new IllegalStateException("index out of range, current data length is" + length)
    }

    val result = data(index)

    for(i <- index until length - 1) {
      data(i) = data(i + 1)
    }

    length -= 1
    result
  }

  def print: Unit = {
    println(data.subSequence(0, length).toString)
  }
}


object ArrayDemo {
  def main(args: Array[String]): Unit = {
    val arrDemo = new ArrayDemo(5)
    arrDemo.insert(0, 'a')
    arrDemo.insert(1, 'b')
    arrDemo.insert(2, 'c')
    arrDemo.insert(3, 'd')
    arrDemo.insert(4, 'e')
    arrDemo.print
  }
}

  • Python版本
class MyArray:

    def __init__(self, capacity: int):
        self._data = []
        self._capacity = capacity

    def __getitem__(self, item, position: int) -> object:
        return self._data[position]

    def __setitem__(self, index: int, value: object):
        self._data[index] = value

    def __len__(self) -> int:
        return len(self._data)

    def __iter__(self):
        for item in self._data:
            yield item

    def find(self, index: int) -> object:
        try:
            return self._data[index]
        except IndexError:
            return None

    def delete(self, index: int) -> bool:
        try:
            self._data.pop(index)
            return True
        except IndexError:
            return False

    def insert(self, index: int, value: int) -> bool:
        if len(self) >= self._capacity:
            return False
        else:
            return self._data.insert(index, value)

    def print_all(self):
        for item in self:
            print(item)


if __name__ == '__main__':
    array = MyArray(5)
    array.insert(0, 3)
    array.insert(0, 4)
    array.insert(1, 5)
    array.insert(3, 9)
    array.insert(3, 10)
    assert array.insert(0, 100) is False
    assert len(array) == 5
    assert array.find(1) == 5
    assert array.delete(4) is True
    array.print_all()

6.2. GenericArray

  • Java版本
public class GenericArray<T> {
    private T[] data;
    private int size;

    public GenericArray(int capacity) {
        this.data = (T[]) new Object[capacity];
        this.size = 0;
    }

    public GenericArray() {
        this(10);
    }
    // 数组容量
    public int getCapacity() {
        return this.data.length;
    }
    // 当前元素个数
    public int count() {
        return this.size;
    }
    // 判断数组是否为空
    public boolean isEmpty() {
        return size == 0;
    }
    // 修改index位置的元素
    public void set(int index, T e) {
        checkIndex(index);
        data[index] = e;
    }
    // 获取对应index位置的元素
    public T get(int index) {
        checkIndex(index);
        return data[index];
    }
    // 查看数组是否包含元素
    public boolean contains(T e) {
        for (int i = 0;i < size; i ++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }
    // 获取对应元素的下标,未找到, 返回-1
    public int find(T e) {
        for ( int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }
    // 在index位置, 插入元素e,时间复杂度为O(m+n)
    public void add(int index, T e) {
        checkIndex(index);
        // 如果当前元素个数等于数组容量
        if(size == data.length) {
            resize(2 * data.length);
        }
        for(int i = size - 1; i >= index; i --) {
            data[i+1] = data[i];
        }

        data[index] = e;
        size ++;
    }
    // 向数组头插入元素
    public void addFirst(T e) {
        add(0, e);
    }
    // 向数组头插入元素
    public void addLast(T e) {
        add(size, e);
    }
    // 删除index位置的元素,并返回
    public T remove(int index) {
        checkIndexForRemove(index);

        T ret = data[index];
        for (int i = index + 1; i < size; i ++) {
            data[i - 1] = data[i];
        }
        size --;
        data[size] = null;
        // 缩容
        if(size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }
        return ret;

    }
    // 删除第一个元素
    public  T removeFirst() {
        return remove(0);
    }
    // 从数组中删除指定元素
    public void removeElement(T e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    // 删除末尾元素
    public T removeLast() {
        return remove(size - 1);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(String.format("Array size = %d, capacity = %d \n", size, data.length));
        builder.append('[');
        for (int i = 0; i < size; i++) {
            builder.append(data[i]);
            if (i != size - 1) {
                builder.append(", ");
            }
        }
        builder.append(']');
        return builder.toString();
    }

    // 扩容方法,时间复杂度O(n)
    private void resize(int capacity) {
        T[] newData = (T[]) new Object[capacity];

        for (int i = 0;i < size;i ++) {
            newData[i] = data[i];
        }
        data = newData;

    }

    private void checkIndex(int index) {
        if(index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed! Require index >=0 and index <= size.");
        }
    }

    private void checkIndexForRemove(int index) {
        if(index < 0 || index >= size) {
            throw new IllegalArgumentException("remove failed! Require index >=0 and index < size.");
        }
    }

    public static void main(String[] args) {
        GenericArray<String> arr = new GenericArray<String>();
        arr.add(0, "100");
        arr.add(0, "200");
        arr.add(0, "300");
        arr.add(0, "400");
        arr.addFirst("e");
        System.out.println(arr);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值