• 超神之路 数据结构 1 —— 关于数组的基本认识


            数组的基本认知:

            如果有面试官问你:为什么数组访问会那么快?我相信你把下面的式子告诉他,那么你就已经赢了一半了。

    addr = base_addr + i * type_size

            如果你连这个都不知道如何去回答,我觉得,你和我曾经有一段时间一样,也太可爱了。好好听着,大家都是这么一点点学的。

            式中的 base_addr 是此数组首元素在内存中基本位置。外加上数组是一组大小连续的内存空间,所以每个元素的大小 type_size相同, i 就是偏移量,通过这个公式 一算 就非常容易计算出a[i] 地址, 从而实现 访问元素一个公式计算一步到位 ,

    这就是为什么人们 常说:数组的访问是 O(1) 的 时间复杂度。就是这么快!

            为什么索引从0开始而不是从1开始呢?

            我们的公式的i 计算的是偏移量。如果从1开始,偏移量每次就要减一,那么CPU要多做一次减法运算,降低了访问速度。

            另外还有一个知识点:CPU从内存中加载数据到CPU缓存时,并非只读要访问的特定地址,而是会读取一个数据块到CPU缓存中,因为这CPU缓存这地方访问效率更高,所以在下次访问该数组时,会先从CPU缓存 开始查找,实在找不到才会到内存中读取数据。

            从上可知,数组的最大优点就是可以做到:首个元素的地址就是整个数组的地址,因为首个元素的偏移是0 ,数组的索引本质就是数组的偏移量。所以根据偏移量 i,也就是索引,根据公式就 可快速访问查询到特定的元素。所以,数组最好应用在索引有语意的情况,但是也不是所有有寓意的索引都适合数组,如果索引过大,意味着偏移过大,就意味着开辟的内存空间就会很大,比如:身份证账号这种情况。

            理想很丰满,现实很骨感,工作中遇到的大部分的应用场景都是索引没有语意的情况。没有语意情况下:如何表示没有元素,如何添加,删除元素?等...一系列的问题,需要处理。带着这些问题,我们要基于java的数组,封装一个属于自己的可动态扩容缩容的动态数组类。

            这是我最近通过视频创作的动态数组的Java版本,里面有一个比较完整的从0到1,逐步建构出一个可用的动态数组的全过程实现,支持泛型,动态的扩容缩容,及一些算法分析的知识点,我即将加入一些JDK的动态数组 ArrayList 源码分析。数据结构 Java实现 - 播单 - 优酷视频

                 视频的详细代码如下:

    1. package com.company.array;
    2. public class DynamicArray {
    3. private static final int DEFAULT_SIZE = 10;
    4. // container:篮子:element:apple
    5. private T[] data;
    6. private int size;
    7. public DynamicArray(int capacity) {
    8. data = (T[]) new Object[capacity];
    9. size = 0;
    10. }
    11. public DynamicArray() {
    12. this(DEFAULT_SIZE);
    13. }
    14. //todo delete
    15. public int getSize(){
    16. return size;
    17. }
    18. // 0 ==> O(n) O(1) ==> index 发生概率 O( n/2 ) 概率正态分布 c * n O(n)
    19. public void add(T e, int index) {
    20. if (index < 0 || index > size) {
    21. throw new IllegalArgumentException(" index illegal");
    22. }
    23. if (data.length == size) {
    24. resize( data.length + (data.length >> 1) );
    25. }
    26. for (int i = size - 1; i >= index; i--) {
    27. data[i + 1] = data[i];
    28. }
    29. data[index] = e;
    30. size++;
    31. }
    32. private void resize(int newCapacity) {
    33. T [] newData = (T[]) new Object[newCapacity];
    34. for(int i = 0 ;i < size; i++){
    35. newData[i] = data[i];
    36. }
    37. data = newData;
    38. }
    39. public void addFirst(T e){
    40. add(e,0);
    41. }
    42. public void addLast(T e){
    43. add(e,size);
    44. // if (data.length == size) {
    45. // throw new IllegalArgumentException(" array is full .");
    46. // }
    47. // data[size] = e;
    48. // size ++ ;
    49. }
    50. /**
    51. * 查询data中,index位置的元素。
    52. * @param index 查询索引
    53. * @return 返回查询索引得到的元素。
    54. */
    55. public T get(int index){
    56. if (index < 0 || index > size-1) {
    57. throw new IllegalArgumentException(" index illegal");
    58. }
    59. return data[index]; // baseAddress + type_size * i == 访问。
    60. }
    61. public void set(T e,int index){
    62. if (index < 0 || index > size-1) {
    63. throw new IllegalArgumentException(" index illegal");
    64. }
    65. data[index] = e;
    66. }
    67. //contains,indexOf,remove
    68. public boolean contains(T e){
    69. for( int i = 0;i
    70. if(data[i] == e){
    71. return true;
    72. }
    73. }
    74. return false;
    75. }
    76. public int indexOf(T e){
    77. for(int i = 0;i
    78. if(data[i] == e){
    79. return i ;
    80. }
    81. }
    82. return -1;
    83. }
    84. public T remove(int index){
    85. if (index < 0 || index > size-1) {
    86. throw new IllegalArgumentException(" index illegal");
    87. }
    88. // [index...size-1]
    89. T ret = data[index];
    90. for(int i = index;i1;i++){
    91. data[i] =data[i+1];
    92. }
    93. //loitering objects. GC ,内存溢出:GC内存没有被管理到的。
    94. data[size-1] = null;
    95. size--;
    96. if(size <= data.length/2 && data.length/2 != 0){
    97. resize(data.length/2);
    98. }
    99. return ret;
    100. }
    101. @Override
    102. public String toString() {
    103. StringBuilder sb = new StringBuilder(String.format("Dynamic Array : size/capacity = %d/%d \n [ ",size,data.length));
    104. for(int i = 0;i
    105. sb.append(data[i]);
    106. if(i !=size-1){
    107. sb.append(" , ");
    108. }
    109. }
    110. sb.append(" ]");
    111. return sb.toString();
    112. }
    113. }

            每一行代码思路,已经在视频中我已经介绍过了,这里就不再用文字冗余的再来一遍。愿各位在追求知识的路上,都能有所收获。加油!Go Go !

  • 相关阅读:
    盘点JAVA中基于CAS实现的原子类, 你知道哪些?
    【Transformer Based Cls&Det】Transformer系列分类和检测网络原理和源码讲解导航
    GStreamer安装——Mac OS X
    java list 通过对象中某一参数去重,返回一个新list
    网络层概述
    【vue3】一些关于hooks的使用经验
    【Java SE】this详解
    用DIV+CSS技术设计的体育篮球主题 校园体育网页与实现制作(web前端网页制作课作业)
    创建一个Django用户认证系统
    Leetcode LCR182:动态口令
  • 原文地址:https://blog.csdn.net/wdw18668109050/article/details/127780366