日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

arraylist转int数组_五千字的数组拓展,面试官对我竖起大拇指喊停

發布時間:2025/3/21 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 arraylist转int数组_五千字的数组拓展,面试官对我竖起大拇指喊停 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 為什么數組下標從0開始?
  • 數組定義
    • 為什么這么下定義?
    • 定義數組的三種方式
  • 從 ArrayList 源碼看數組增刪改查
    • 初始化
    • 增加
    • 刪除
    • 修改
    • 查找
  • 數組和容器
    • 數組時間復雜度
    • 數組插入,刪除優化
    • 容器替代數組?
  • 字節高頻算法題:移動零
  • 算法發散
?沒有最完美的數據結構,只有最合適的數據結構。
?

為什么數組下標從0開始?

這個問題上大學第一課C語言的時候我就疑惑,沒有接觸過計算機之前,數數都是從1開始的呀,一只羊兩只羊三只羊,別睡著了。

參考原因如下:

  • 高級語言爺爺級的C語言,就是從0開始,后面發展的語言都是沿用這個,降低學習成本;但是發展了這么多年,幾個更加新的語言,Python等支持負數下標;
  • 下標代表偏移量,
  • 推導得到第i個元素地址公式:

    a[i]_address = first_address + i * data_type_size

    如果從1開始,推導得到第i個元素地址公式: a[i]_address = first_address + (i-1) * data_type_size

    即多了一次-1操作,對于CPU來說,就是多了一次減法指令

    數組定義

    數組是一種線性表結構,它用一組連續的內存空間,來存儲一組具有相同類型的數據。

    「線性表」:具有像線一樣性質的表。即線性表上的數據只有前后關系,數組,鏈表,隊列,棧這樣的都是前后關系的線性表結構,樹和圖這樣的前后左右都有關系的即是非線性表結構。

    為什么這么下定義?

    一般下定義都是留下了最精煉的字來概括內容,就像一部好的電影沒有一句廢話,下面來分析一下數組定義。

    「連續」:正是因為連續的內存空間,所以我們能推算出每個元素的地址,假設一個數組有五個元素,起始地址為00,那么后面元素地址一次為01,02,03,04,別人一問你第五個元素地址,你立馬可以告訴她是04,這正是因為數組的內存空間是一段連續的空間。

    然而如果這五個元素存放在鏈表里,那么你就不能立馬告訴別人第五個元素的地址是04了,你要先找到第一個元素取得第二個元素的地址,然后取得第三個元素的地址,一直找下去找到最后一個元素,就是因為鏈表存儲的空間不是連續的,鏈表元素里面除了數據本身還需要多存放下一個元素的地址,通過這種方式來找下一個元素,如果要同時知道鏈表前后是誰就需要雙鏈表了。

    注:正是因為數組需要連續的內存空間,所以定義數組的時候都需要指定數組的初始大小,要不然會報錯。JAVA容器類ArrayList底層是Object[]數組實現的,數組指定的初始大小在JDK1.8之前是10,JDK1.8時候變成了0。

    「相同類型」:試想一下,你一個數組,一會兒放個int類型,一會兒放個long類型,那么上面提到的內存連續也拯救不了你。你讓計算機咋搞呢,int類型占四個字節,long類型占八個字節(64位操作系統下),計算機是把四個字節看成一個元素,還是八個字節當做一個元素呢,要知道所需存儲空間不同地址不同呀,即使你內存連續都不能根據下標統一尋址了。

    「因此,數組兩大特性:」連續內存空間,相同類型元素。數組一切的一切,都是基于這兩個的,基于這兩大特性,數組實現了最大的優點:隨機存取,我們很多時候使用數組都是貪圖這個優點。

    定義數組的三種方式

    初始化數組主要分為靜態初始化和動態初始化,無論哪一種都需要指定數組大小:

    靜態初始化:定義數組時候,開辟空間的同時設置內容,一次性初始化完成;

    動態初始化:數組先開辟空間,再使用索引進行內容賦值;

    從 ArrayList 源碼看數組增刪改查

    感覺純粹看數組的增刪改挺無趣的,我們每個人只要靜下心來都可以實現數組的增刪改查,極客算法里面通過看JAVA的 ArrayList 源碼的方式來看數組增刪改,我覺得挺不錯的:

    一來可以看看設計者們怎么封裝的,感受感受優秀代碼設計;

    二來可以熟悉熟悉源碼,更加清楚天天用的 ArrayList 底層實現,可以看到有什么值得平時注意的。

    以下都是基于JDK1.8,選取ArrayList是因為這個我們平時用的最多。

    初始化

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {// 序列化idprivate static final long serialVersionUID = 8683452581122892189L;// 默認初始的容量private static final int DEFAULT_CAPACITY = 10;// 一個空對象private static final Object[] EMPTY_ELEMENTDATA = new Object[0];// 一個空對象,如果使用默認構造函數創建,則默認對象內容默認是該值private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];// 當前數據對象存放地方,transient表明當前對象不參與序列化transient Object[] elementData;// 當前數組長度private int size;// 數組最大長度private static final int MAX_ARRAY_SIZE = 2147483639;// 方法開始 } 復制代碼

    默認構造函數:

    public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;/** 也就是實現了 Object[] elementData;elementData = new Object[0] ,即new了一個空的對象數組,數組長度是0 **/} 復制代碼

    增加

    ArrayList 添加了四種添加方法:

    • add(E element)
    • add(int i , E element)
    • addAll(Collection)
    • add(int index, E element)

    數組末尾追加元素 add(E element)

    public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true;} 復制代碼

    ensureCapacityInternal() 確保添加的元素有地方存儲,size+1,默認size為0,+1保證數組下標為size+1這個地方可以存儲新元素,下面的 elementData[size++] = e 進行新的元素追加到數組并且上面的保證使其賦值不會數組越界;

    private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);} 復制代碼

    minCapacity 為增加元素時所需最小長度數組容量大小;

    下面第一次add時候,將當前elementData數組的長度用 Math.max 變為10,即第一次add時候 將數組長度 minCapacity 變為默認初始容量10;(jdk1.8以前都是直接初始化的時候指定this(10)直接指定默認容量大小)

    非第一次add的時候,minCapacity 為原數組的長度+1:

    如果所需的最小長度大于了現有數組長度,那么現在的數組容量肯定是不夠的,需要進行擴容;

    modCount 是從 abstractList 里面繼承過來的值,用于迭代器Iterator的操作次數記錄;

    private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// 右移運算符等價于除以2,如果第一次是10,擴容之后的大小是15int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 考慮邊界問題,數組最大容量為2的31次方,int為四個字節,每個字節8位if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;} 復制代碼

    擴容,如果添加元素所需最小容量minCapacity(即當前的數組已使用空間(size)加1)大于數組長度,則增大數組容量,擴大為原來的1.5倍。(右移一位相當于除以2)

    數組最大容量為2的31次方,數組長度length屬性是int,int為四個字節,每個字節8位,2G內存,沒有人會喪心病狂搞這么大數組吧!

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {@SuppressWarnings("unchecked")T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy;}public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length); 復制代碼

    Arrays.copyOf追蹤下去代碼,確保有足夠的容量之后,使用System.arraycopy 將舊數組拷貝到新的數組.

    數組中間插入一個元素

    public void add(int index, E element) {// 判斷index 是否有效rangeCheckForAdd(index);// 計數+1,并確認當前數組長度是否足夠,和上面的追加一樣ensureCapacityInternal(size + 1); // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index); // 將index 后面的數據都往后移一位elementData[index] = element; // 設置目標數據size++;}private void rangeCheckForAdd(int index) {if (index > size || index < 0)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));} 復制代碼

    需要插入的位置(index)后面的元素統統往后移動一位,然后將新值插入。

    整個插入過程:

  • 確保數插入的位置小于等于當前數組長度,并且不小于0,否則拋出異常;
  • 確保數組此數組能放得下新的數據 所需長度minCapacity=size+1;
  • 修改次數(modCount)標識自增1,如果當前數組所需長度大于當前的數組長度,則調用grow方法,增長數組;
  • grow方法會將當前數組的長度變為原來容量的1.5倍;
  • 確保有足夠的容量之后,使用System.arraycopy 將需要插入的位置(index)后面的元素統統往后移動一位;
  • 將新的數據內容存放到數組的指定位置(index)上;
  • 刪除

    ArrayList 中提供了 五種刪除數據的方式:

    • remove(int i)
    • remove(E element)
    • removeRange(int start,int end)
    • clear()
    • removeAll(Collection c)
    public E remove(int index) {// 判斷索引是否有效,范圍檢查rangeCheck(index); modCount++;// 獲取對應數據E oldValue = elementData(index); // 判斷刪除數據位置int numMoved = size - index - 1; // 如果刪除數據不是最后一位,則需要移動數組// 先將index后面的元素往前面移動一位(調用System.arraycooy實現)if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);// 然后將最后一個元素置空,進行垃圾回收elementData[--size] = null; return oldValue;} 復制代碼

    修改

    這個簡單,需要改哪個,直接 data[index] = 4 重新賦值就可以

    查找

    數組支持隨機訪問,根據下標隨機訪問的時間復雜度為O(1)。

    但是這并不代表數組的查找時間復雜度是O(1),即使是排好序的數組,你用二分查找,時間復雜度也是O(logn),這是兩個概念。

    數組和容器

    數組時間復雜度

    如果在數組的末尾插入元素,那就不需要移動數據了,這時的時間復雜度為O(1)。

    但如果在數組的開頭插入元素,那所有的數據都需要依次往后移動一位,所以 最壞時間復雜度是O(n)。

    因為我們在每個位置插入元素的概率是一樣的,所以平均情況時間復雜度為(1+2+...n)/n=O(n)。

    數組插入,刪除優化

    上面數組的插入和刪除效率是很低的,正是因為數組是連續的空間內存,而插入和刪除的時候改變了數組的空間內存,為了維護連續的內存空間所以要進行數組元素的移動。

    具有這個特性,就要維護他,比如紅黑樹具有查找快速的特點,插入和刪除的時候就必須要通過各種左旋右旋操作來維護紅黑樹的平衡,其實是一樣的道理。

    插入優化

    如果數組中的數據是有序的,我們在某個位置插入一個新的元素時,就必須按照剛才的方法搬移插入位置之后的數據。

    但是,我們開發中,如果數組中存儲的數據并沒有任何規律,數組只是被當作一個存儲數據的集合。在這種情況下,如果要將某個數據插入到第i個位置,為了避免大規模的數據搬移,還有一個簡單高效的辦法就是,直接將第i位的數據搬移到數組元素的最后,把新的元素直接放入第i個位置(具體如下圖)。

    利用這種處理技巧,在特定場景下,在第i個位置插入一個元素的時間復雜度立即降為了O(1),快排就用到了這個處理思想。

    刪除優化(標記清除算法)

    標記清除算法 是JVM垃圾回收里面用到的核心算法,具體的可以看公眾號《阿甘的碼路》里面,有關垃圾回收機制相關的文章。

    如果數組中數據不要求連續的情況下,我們將多次刪除操作集中在一起執行,只做標記清除工作而不進行真正的刪除,然后統一進行刪除,刪除的效率會提高很多不用進行數據多次的搬遷。

    容器替代數組?

    容器優點:

  • 動態擴容,程序員很舒服只需要一直add就好了不需要管數組大小是否足夠
  • 封裝了很多細節,API豐富,將下標操作轉化為英文add,remove等人類語言
  • 容器缺點: 裝箱拆箱有一定的性能損耗

    數組優點:

  • 多維數組直接用數組表示更加直觀,如 int[3][4] arr 和 List<List<Integer>>?
  • 普通業務開發容器足夠,底層開發例如網絡框架這種對性能優化極致追求的代碼用數組還是比較高效的。
  • 字節高頻算法題:移動零

    審題: 保持非零元素相對順序,指的是元素在數組里面的相對順序,而不是讓保證元素相對大小。

    思路:

  • 遍歷,遇0刪除,列表最后添加0
  • 這里使用的Python的 api 還是很方便的,代碼也很清晰明了,思路簡單。JAVA就做不到這樣add然后remove,集合的實現方式不一樣,不信的話可以進行實現,你會發現有很多報錯。

    缺點: 空間復雜度很高,每次remove其實都需要移動此元素后面所有的元素。

  • 兩次遍歷
  • 創建兩個指針i和j,第一次遍歷的時候指針j用來記錄當前有多少非0元素。即遍歷的時候每遇到一個非0元素就將其往數組左邊挪,第一次遍歷完后,j指針的下標就指向了最后一個非0元素下標。

    第二次遍歷的時候,起始位置就從j開始到結束,將剩下的這段區域內的元素全部置為0。

    時間復雜度:O(n)

    空間復雜度:O(1)

  • 最優解
  • 在原數組上面進行操作,所有的非0元素往前移動,0自然在后面了

    • j記錄要填入的非零元素位置,遇到非0元素就挪動到j位置上;
    • 遍歷整個數組,遇到nums[i]==0 時候不處理;如果非0的時候,則把nums[i]的非0元素和nums[j]上的0元素互換,調換位置;
    • j始終指向的是下一個非0元素;

    很抽象,移動零最優解圖解如下:

    省了一次遍歷,借鑒了快排的思路:

    快排:快速排序首先要確定一個待分割的元素做中間點x,然后把所有小于等于x的元素放到x的左邊,大于x的元素放到其右邊;

    移動零:我們可以用0當做這個中間點,把不等于0(注意題目沒說不能有負數)的放到中間點的左邊,等于0的放到其右邊。

  • 從右往左開始遍歷,所有的0元素往后移動,和上面的思路一樣,讀者如果可以根據上面的解法自己實現這種方式,那么這道算法算是理解了。
  • 總結

    以上是生活随笔為你收集整理的arraylist转int数组_五千字的数组拓展,面试官对我竖起大拇指喊停的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 91亚洲国产成人久久精品网站 | 免费毛片看 | av在哪里看 | 色屋在线 | 一区二区三区精品免费视频 | 男同av在线观看一区二区三区 | 欧美aaa在线观看 | 久久久久久久久久影院 | 人与嘼交av免费 | av五月| 欧美日韩成人精品 | 久久天天躁狠狠躁夜夜av | 精品无码国产一区二区三区av | 中文字幕9| 国产成人精品一区二三区 | 日韩成人一区二区 | 亚洲无码乱码精品国产 | 理论片91 | 黄色动漫免费在线观看 | 超碰精品在线观看 | 亚洲激情午夜 | 色呦呦日韩精品 | 人与动物av| 日韩第八页 | 国产美女被遭强高潮免费网站 | 日本草逼视频 | 亚洲人成色777777老人头 | 国产调教视频 | 中文字幕亚洲一区二区三区五十路 | jizz国产| 国模丫头1000人体 | 私拍在线| 中文字幕在线永久 | 欧美专区在线 | 日本bdsm视频 | 国产精品99无码一区二区视频 | 狠狠撸狠狠干 | 国产精品午夜电影 | 欧美日韩精品电影 | 黄色av网址在线观看 | 樱桃国产成人精品视频 | 69亚洲精品久久久蜜桃小说 | 精品少妇人妻一区二区黑料社区 | 国产孕妇一区二区三区 | 欧洲亚洲一区二区三区 | 大地资源二中文在线影视免费观看 | 色婷婷国产精品综合在线观看 | 国产乱码一区二区 | www.欧美com| 国产精品激情偷乱一区二区∴ | 国产曰肥老太婆无遮挡 | 性色视频 | 亚洲精品乱码久久久久久写真 | 久草视频这里只有精品 | 免费在线看黄网站 | 久久桃色| 手机在线一区 | 成人免费看视频 | 一二三在线视频 | 久久一级电影 | 视频在线免费观看 | 在线观看中文字幕一区二区 | 国产无套精品一区二区 | 男女啪啪av | 天堂视频中文在线 | 久久婷婷婷 | 经典三级av在线 | 丁香婷婷深情五月亚洲 | 天天干天天爱天天射 | 99久久99久久久精品棕色圆 | 91av影视| 最新精品国产 | 翔田千里一区 | 久久777| 成人写真福利网 | 青青草草 | 激情成人av| 无码人妻aⅴ一区二区三区日本 | 国产中文字幕在线播放 | xxx国产在线观看 | 91黑丝视频| 不卡成人| 国产农村熟妇videos | 久久综合伊人 | 伊人365影院 | 毛片基地在线播放 | av在线手机版 | 国产免费一区二区视频 | √资源天堂中文在线 | 综合网中文字幕 | 日韩黄色高清视频 | 精品亚洲一区二区三区 | 超碰520| 最新国产拍偷乱偷精品 | 欧美操大逼 | 黑人毛片网站 | 亚洲国产二区 | 同人动漫在线观看 | 操操色 |