原文:chenmingyu.top/data-struct…java
數組是用於儲存多個相同類型數據的集合,使用一段連續的內存空間存儲數據算法
數組做爲最基本的數據結構,想必你們必定已經足夠了解,數組的增刪操做時間複雜度是O(n),而查詢的時間複雜度是O(1),這裏的查詢指的是按下標進行查找,若是是比對數據進行查詢時間複雜度仍是O(n)api
前提:數組
假設數組的長度是n數據結構
新增:app
咱們新增一條數據到數組中時,當新增的數據在末尾的時候時間複雜度是O(1),而當新增的數據插入到數組的第x個位置時,因爲數組使用的是連續的內存,因此x以後的數據都須要日後移動一位,把第x位置騰出來,才能夠將新增的數據插入到第x個位置,若是按最壞的狀況考慮,是將數據插入到數據的首位,這時數組裏全部的數據都須要日後移一位,這時的時間複雜度就是O(n),因此平均的時間複雜度就是O(n)性能
刪除:編碼
當咱們從數組中刪除一條數據的時候,假設刪除的數據位於數組的第x位置,也是因爲數組使用的是連續的內存,當咱們把x位置的數據刪除以後,爲了保證數組的內存是連續的,x位置以後的數據都須要往前移動一位,假設當刪除的是數組首位的數據,這時首位以後的全部數據都須要往前移動一位,時間複雜度是O(n),當刪除的數據是數組尾部的數據時,不會發生數據移動,時間複雜度是O(1)spa
查詢:指針
關於查詢,要說的點仍是,因爲數組的內存是連續的,這就提供了咱們隨機訪問的能力,隨機訪問的意思就是指咱們能夠按照數組的下標進行查詢,這時的時間複雜度是O(1),而遍歷全部數組匹配查詢的時候時間複雜度是O(n)
咱們看一下數組在內存中的存儲結構
//實例化一個數組,
int[] m = new int[]{0,5,10,15,20};
複製代碼
隨機訪問:因爲數組內的地址是連續的,想要獲取m[3]位置數據的時候咱們只要根據尋址公式計算出m[3]的內存地址就能夠直接獲取到數據,這時假設數組的首地址是k,一個int類型佔4個字節,m[3]的內存地址就是k+3*4
匹配查詢:這時候因爲須要遍歷數組,若是在第一個m[0]的位置就匹配上,則時間複雜度是O(1),在數組尾位匹配上就是O(n),因此平均的時間複雜度就是O(n)
總結一下
因爲數組存儲的是相同類型的數據,而且內存的地址是連續的,就致使可使用尋址公式計算出數組中某個元素的內存地址,因此數組的隨機訪問是高效的,可是要新增和刪除數據的時候,爲了保持數組的內存連續性,必然會致使數據的移動,因此數組的新增和刪除是低效的
注意:使用數組須要警戒的是別形成數組越界
因爲數組的容量是實例化的時候就固定的,因此沒有辦法進行動態擴容,而java
中的容器類則彌補了這一缺陷,好比ArrayList
,ArrayList
將底層操做數組的細節封裝,經過提供api供開發人員使用,ArrayList
當咱們調用add
方法的時候不須要關心底層數組的容量夠不夠使用也不用關心擴容的邏輯是什麼,當須要的擴容的時候ArrayList
會本身進行擴容:
int newCapacity = oldCapacity + (oldCapacity >> 1);
複製代碼
oldCapacity
是數組的舊的容量大小,而newCapacity新的數組容量大小是原來的1.5倍(oldCapacity >> 1
右移一位至關於oldCapacity/2
)
擴容的原理就是計算出新的數組大小,而後申請內存,將原來的數組複製到新申請的數組中,這一過程是比較低效的,因此咱們通常在實例化集合的時候都會指定集合的大小好比new ArrayList(16)
;
對於我來講,編碼時使用更多的是集合而不是數組,由於集合使用起來更方便,但數組也不是絕對不用,Java中的集合不支持基本類型,而這時候使用若是使用集合的話必然會進行裝箱,拆箱的操做,形成沒必要要的性能消耗,因此當數據的容量是固定的,且數據類型是基本類型的時候,就能夠考慮使用數組
算法題來自leetcode
連接:leetcode-cn.com/problems/tw…
難度:簡單
給定一個整數數組 nums
和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你能夠假設每種輸入只會對應一個答案。可是,你不能重複利用這個數組中一樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
由於 nums[0] + nums[1] = 2 + 7 = 9
因此返回 [0, 1]
複製代碼
思路:
nums[i]=target-nums[j]
題解:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>(16);
for(int i=0;i<nums.length;i++){
int n = target-nums[i];
if(map.containsKey(target-nums[i])){
return new int[]{i,map.get(target-nums[i])};
}
map.put(nums[i],i);
}
return null;
}
}
複製代碼
連接:leetcode-cn.com/problems/co…
難度:中等
給定 n 個非負整數 a1,a2,...,an,每一個數表明座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別爲 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器能夠容納最多的水。
說明: 你不能傾斜容器,且 n 的值至少爲 2。
圖中垂直線表明輸入數組 [1,8,6,2,5,4,8,3,7]。在此狀況下,容器可以容納水(表示爲藍色部分)的最大值爲 49
示例:
輸入: [1,8,6,2,5,4,8,3,7]
輸出: 49
複製代碼
思路:
官方題解:雙指針法
面積=長度(x)*高度(y)
假設left爲x軸左起點,right爲x軸右起點,left對應的y軸高度爲a[left],right對應的y軸高度爲a[right],計算面積中的高度只能取a[left]和a[right]中小的那個,而長度的計算方式是right-left
,當a[left]>a[right]時,對應的x軸right就往前移動一位,反之left就日後移動一位
題解:
class Solution {
public int maxArea(int[] height) {
//x軸左起始位置
int left=0;
//x軸右起始位置
int right= height.length-1;
int maxarea = 0;
while(left<right){
//每次都須要計算面積,將面積更大的賦值給maxarea
maxarea = Math.max(maxarea,Math.min(height[left],height[right])*(right-left));
//若是左面的垂直線大於右面的垂直線
if(height[left]>height[right]){
//右面的往前移動一位
right--;
}else{
//左面的日後移動一位
left++;
}
}
return maxarea;
}
}
複製代碼
連接:leetcode-cn.com/problems/tr…
難度:困難
給定 n 個非負整數表示每一個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨以後能接多少雨水。
上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種狀況下,能夠接 6 個單位的雨水(藍色部分表示雨水)。 感謝 Marcos 貢獻此圖。
示例:
輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
複製代碼
思路:
解題思路也是使用雙指針,沒有思路的可詳細看下官方題解雙指針方法的那個動圖
題解:
class Solution {
public int trap(int[] height) {
int left=0;
int right=height.length-1;
int maxLeft=0;
int maxRight=0;
int area =0;
while(left<right){
if(height[left]<height[right]){
if(height[left]>=maxLeft){
maxLeft = height[left];
}else{
area += maxLeft-height[left];
}
++left;
}else{
if(height[right]>=maxRight){
maxRight = height[right];
}else{
area += maxRight-height[right];
}
--right;
}
}
return area;
}
}
複製代碼
參考:
極客時間:數據結構與算法之美
leetCode官網:leetcode-cn.com/problemset/…