算法學習(三): 數(shù)組

數(shù)組的基本概念


定義:

  • 通常由一組相同類型的元素的集合所組成的結(jié)構(gòu)(javascript允許數(shù)組中包含不同類型的元素)
  • 計算機會分配一塊連續(xù)的內(nèi)存來存儲這個元素集合
  • 利用索引可以取出元素對應的存儲地址

特性:

  • 在內(nèi)存中為連續(xù)空間
    定址公式: addr(curElem)=addr(intialElem)+sizeof(curElem)*index
  • 通過index獲取數(shù)組元素的時間復雜度為O(1)
  • 通常數(shù)組第一個位置的索引是0

數(shù)組示意圖如下:


在java中可以這么定義一個數(shù)組

int[] array = {2,4,5,6,22,89,35} //定義數(shù)組

array[1] // 值為4 取出索引1位置的值
array[5] // 值為89 取出索引5位置的值

我們還可以定義二維數(shù)組

int[][]  array={{1,2,3},{66,45,33,4},{2,7,8}}

三維數(shù)組也是可以定義的, 這里就不舉例了


為什么使用數(shù)組


  • 簡化代碼, 提高效率
  • 將相同類型的元素組織在一起

比如, 我們要存1-1000這1000個數(shù), 用數(shù)組就能很簡單的存儲, 并且由于數(shù)組的每一個元素都是連續(xù)開辟的內(nèi)存空間, 所以也能快速的取到中間任意的值

int[] nums = new int[1000];
for(int i=0; i<nums.length;i++) {
  nums[i] = i+1;
}

nums[5] //6
nums[926] //927


常見數(shù)組算法題


因為我自己比較熟悉js, 所以關(guān)于算法題的代碼, 我都用js代碼寫, 當然我也只是個初學者, 所以大家不要糾結(jié)我代碼寫的好不好看, 主要看解題思路就好了

例1: 兩數(shù)之和

給定一個整數(shù)數(shù)組(無重復元素)和一個目標值, 找出數(shù)組中和為目標值的兩個數(shù)。
按照從小到大的順序輸出結(jié)果對
將所有結(jié)果對輸出到一個數(shù)組
例:
Input: number = {2,7,11,15}, target = 9

Output: {2,7}

分析需求:

  1. 數(shù)組是否排序
  2. 有沒有重復元素
  3. 結(jié)果是返回全部還是返回任意一個

解題思路:

思路1:
暴力解法來解

function twoSum(arr, target) {
  let result = []
  let index = 0 
  if(arr.length < 2) {
    return result
  }
  for(let i=0; i<arr.length; i++) {
    for(let j=i+1; j<arr.length; j++) {
      if(arr[i] + arr[j] === target) {
        if(arr[i] < arr[j]) {
          result[index] = []
          result[index].push(arr[i])
          result[index].push(arr[j])
          index++
        }else {
          result[index] = []
          result[index].push(arr[j])
          result[index].push(arr[i])
          index++
        }

      }
    }
  }
  return result
}

let newArray = twoSum([1,3,4,5,6,8,78], 9)
console.log(newArray)

思路1總結(jié):

  • 時間復雜度O(n2)
  • 存在無意義操作
    例如:
    78已經(jīng)大于9了, 所以每次對它操作都是在浪費時間



思路2:
排序+兩根指針

這里由于我們還沒有講到排序, 所以對于排序我們直接用js里面提供的sort方法, 如果大家以后面試的話, 除非是考官強制要求不能使用api, 否則能用編程語言提供的api, 我們一定要使用語言自帶的api, 這樣能節(jié)省很多時間

兩根指針的方法在面試中是一個很實用的方法

  • 通過對數(shù)組排序與兩個指針組合, 減少無意義的遍歷
  • 兩根指針: 一根指針(start)指向數(shù)組的第一個元素(數(shù)組中最小元素), 另一個指針(end)指向數(shù)組最后一個元素(數(shù)組中最大元素)
  • 核心思想: 如果現(xiàn)在兩根指針所指元素之和大于目標值, 則表明現(xiàn)在兩數(shù)之和過大, 應使end指針指向更小的數(shù), 即索引減小(end--), 反之則表明現(xiàn)在兩數(shù)之和過小, 應使start指針指向更大的數(shù), 即索引增加(start++)
function sum(arr, target) {
  let start = 0
  let end = arr.length-1
  let result = []
  let index = 0
  if(arr.length < 2) {
    return result
  }
  arr.sort((x,y) => {
    return x-y
  })
  while(start < end) {
    if(arr[start] + arr[end] > target) {
      end--
      continue
    }else if(arr[start] + arr[end] < target) {
      start++
      continue
    }else if(arr[start] + arr[end] === target) {
      result[index] = []
      result[index].push(arr[start])
      result[index].push(arr[end])
      start++
      index++
      continue
    }
  }
  return result
}

思路2總結(jié):

  • 通過對數(shù)組排序與兩根指針組合, 減少無意義的遍歷
  • 使用兩個指針(而不是一個)以相同/相反的方向遍歷數(shù)組
  • 時間復雜度分析: 排序: O(nlogn), 兩根指針算法: O(n)
    時間復雜度: O(nlogn) + O(n) => O(nlogn)


例2: 三數(shù)之和

給定一個包含n個整數(shù)的數(shù)組(無重復元素)和一個目標值, 找出數(shù)組中和為目標值的三個數(shù)
例如:
給定一個數(shù)組nums = [-1, 0 ,1, 2, -5, 3], target = 0
滿足要求的三元組集合為:[[-1,0,1], [2,-5,3]]

思路1:

  • 暴力解法
    算法復雜度: O(n3)
    注意: 面試的時候這種算法基本上不用寫了, 太蠢了, 最多可以當成你思路的第一步告訴面試官
  • 嘗試用排序+兩根指針算法
  • 遍歷第一個數(shù)字num1, 看看另外兩個數(shù)之和是否能滿足target - num1, 這就轉(zhuǎn)化成兩數(shù)之和的問題了
  • 時間復雜度: 排序O(nlogn), 兩數(shù)之和: O(n2)
    O(nlogn) + O(n2) => O(n2)
function sum(arr, target) {
  let result = []
  let first
  let second = arr.length - 1
  let index = 0
  if(arr.length < 3) {
    return result
  }
  arr.sort((x,y) => {
    return x-y
  })
  for(let i=0; i<arr.length; i++ ) {
    first = i+1
    while(first < second) {
      if(arr[first] + arr[second] > target - arr[i]) {
        second--
        continue
      }else if(arr[first] + arr[second] < target - arr[i]){
        first++
        continue
      }else if(arr[first] + arr[second] === target - arr[i]) {
        result[index] = []
        result[index].push(arr[i])
        result[index].push(arr[first])
        result[index].push(arr[second])
        first++
        index++
        continue
      }
    }
  }
  return result
}


K-sum解法總結(jié)

  • 排序
  • 嘗試遍歷第一個數(shù), 將問題轉(zhuǎn)化成(K-1)Sum, 以此類推
  • 時間復雜度
    2-Sum: O(nlogn) + O(nlogn) => O(nlogn)
    3-Sum: O(nlogn) + O(n2) => O(n2)
    4--Sum: O(nlogn) + O(3) => O(n3
    5-Sum: O(nlogn) + O(n(k-1)) => O(n(k-1))


例3: 反轉(zhuǎn)數(shù)組

給定一個數(shù)組, 反轉(zhuǎn)數(shù)組中的所有數(shù)字
例:
Input: {1,2,88,45,3,7}
Output: {7,3,45,88,2,1}

分析:

  • 數(shù)組是否合法
  • 數(shù)組是否已經(jīng)排序
  • 數(shù)組是否有重復元素

這到底就很簡單了

  • 用雙指針, fisrt指向數(shù)組的第一個, end指向數(shù)組的最后一個
  • 交換arr[first]和arr[end]的值.然后指針first++往后移一位, 同時end--往前移一位
  • 直到first >= end(指針first和end相遇, 即表示整個數(shù)組已經(jīng)遍歷完), 結(jié)束交換
function reverse(arr) {
  let first = 0
  let end = arr.length - 1
  
  while(first < end) {
    [arr[first], arr[end]] = [arr[end], arr[first]]
    first++
    end--
  }
  return arr
}

算法復雜度: O(n)


例4: 奇數(shù)偶數(shù)排序

給定一組數(shù)組, 對它們進行排序, 以便所有奇數(shù)整數(shù)在偶數(shù)整數(shù)之前出現(xiàn)。元素順序可以改變编振。排序的奇數(shù)和偶數(shù)的順序無關(guān)緊要肖方。
例:
Input:{4,3,5,2,1,11,0,8,6,9}
Output:{9,3,5,11,1,2,0,8,6,4}

思路:

  • 第一步先用暴力解法:
    額外開辟兩個數(shù)組, 一個數(shù)組存儲奇數(shù), 一個數(shù)組存儲偶數(shù), 然后依次先把奇數(shù)輸出, 再把偶數(shù)輸出到另外一個新數(shù)組, 這種解法時間復雜度為O(n), 但是要另外開辟三個新數(shù)組, 所以要浪費額外空間復雜度O(n)
  • 用雙指針
    1. 跟之前所有套路一樣, first指向數(shù)組第一個, end指向數(shù)組最后一個
    2. 從數(shù)組的第一個開始判斷, 如果是奇數(shù), 則first++,指向數(shù)組的下一個元素,繼續(xù)判斷, 直到遇到偶數(shù),指針first記錄偶數(shù)的位置街望。 這時候從數(shù)組的最后一個開始判斷, 如果是偶數(shù), 則end--, 指針指向數(shù)組前一個元素, 繼續(xù)判斷, 直到遇到奇數(shù), end記錄奇數(shù)的位置寺鸥。交換first位置和end位置的兩個數(shù)
    3. 交換完成后, 按照第二步的方法繼續(xù)往后判斷, 知道firt指針和end指針相遇(first >= end), 就完成了排序
function swap(arr) {
  let first = 0
  let end = arr.length-1
  while(first < end) {
    while(first < end && arr[first] % 2 === 1) {
      first++
    }
    while(first < end && arr[end] % 2 === 0) {
      end--
    }
    [arr[first],arr[end]] = [arr[end], arr[first]]
    first++
    end--
  }
  return arr
}

總結(jié):

  • 因為這個算法只遍歷了一遍數(shù)組, 所以時間復雜度: O(n)
  • 沒有開辟額外的內(nèi)存空間, 所以空間復雜度: O(1)
  • 相比暴力算法時間復雜度是一樣的, 但是節(jié)省了空間

例5: 合并兩個有序數(shù)組

給定兩個有序整數(shù)數(shù)組, 按照遞增順序?qū)⑺麄兒喜⒌揭粋€排序數(shù)組中
Input: {1,3,5}, {2,4,6}
Output: {1,2,3,4,5,6}

分析:

  • 數(shù)組是否已排序
  • 數(shù)組是否有重復
  • 返回什么數(shù)據(jù)

思路:

  • 同樣用雙指針, 但是這次不太一樣, 這次的雙指針分別指向兩個數(shù)組, 并且指針移動方向相同
  • 開辟一個新數(shù)組保存排序后的數(shù)組, 數(shù)組長度為兩個待排序數(shù)組長度之和
  • 兩個數(shù)組都從第一個開始遍歷, 從第一個位置開始比較兩個數(shù)組元素的值, 值比較小的那個元素輸入到新開辟的數(shù)組中的第一個位置, 然后小值所在的數(shù)組索引+1, 大值得數(shù)組索引不變
  • 拿之前小值所在的數(shù)組位置1的元素跟大值數(shù)組位置0的做對比, 跟上面上一樣, 哪個值小, 他所在的數(shù)組索引值就+1, 值大的那個數(shù)組索引不變, 繼續(xù)比直到其中一個數(shù)組所有元素都遍歷完了
  • 當一個數(shù)組所有的值遍歷完, 另一個數(shù)組還沒有遍歷完時, 將沒有遍歷完的數(shù)組剩下的值依次輸入到新數(shù)組中余下的位置
  • 新數(shù)組中的元素就是排序結(jié)果
function merge(arr1, arr2) {
  let mergeArr = new Array(arr1.length + arr2.length)
  let [index,index1,index2] = [0,0,0]
  
  while(index1 < arr1.length && index2 < arr2.length) {
    if(arr1[index1] <= arr2[index2]) {
      mergeArr[index] = arr1[index1]
      index1++
      index++
    }else if(arr1[index1] > arr2[index2]) {
      mergeArr[index] = arr2[index2]
      index2++
      index++
    }
  }
  
  for(let i = index1; i<arr1.length; i++) {
    mergeArr[index] = arr1[i]
    index++
  }
  
  for(let i = index2; i<arr1.length; i++) {
    mergeArr[index] = arr2[i]
    index++
  }
  return mergeArr
}

總結(jié):

  • 時間復雜度: 因為分別需要遍歷兩個數(shù)組, 所以為O(m+n)
  • 空間復雜度: 開辟了新的數(shù)組用來保存排序后的結(jié)果, 所以額外空間復雜度為O(m+n),
    這個空間是必須開辟的, 所以這并不算浪費空間

關(guān)于數(shù)組我們就說到這里, 請大家期待下次的更新吧.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末好爬,一起剝皮案震驚了整個濱河市扒俯,隨后出現(xiàn)的幾起案子奶卓,更是在濱河造成了極大的恐慌,老刑警劉巖撼玄,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺姑,死亡現(xiàn)場離奇詭異,居然都是意外死亡掌猛,警方通過查閱死者的電腦和手機盏浙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荔茬,“玉大人废膘,你說我怎么就攤上這事∧轿担” “怎么了丐黄?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坊萝。 經(jīng)常有香客問我,道長许起,這世上最難降的妖魔是什么十偶? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮园细,結(jié)果婚禮上惦积,老公的妹妹穿的比我還像新娘。我一直安慰自己猛频,他們只是感情好狮崩,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布蛛勉。 她就那樣靜靜地躺著,像睡著了一般睦柴。 火紅的嫁衣襯著肌膚如雪诽凌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天坦敌,我揣著相機與錄音侣诵,去河邊找鬼。 笑死狱窘,一個胖子當著我的面吹牛杜顺,可吹牛的內(nèi)容都是我干的骨宠。 我是一名探鬼主播惯悠,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茬底!你這毒婦竟也來了搭儒?” 一聲冷哼從身側(cè)響起穷当,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仗嗦,沒想到半個月后膘滨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡稀拐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年火邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片德撬。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡铲咨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜓洪,到底是詐尸還是另有隱情纤勒,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布隆檀,位于F島的核電站摇天,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恐仑。R本人自食惡果不足惜泉坐,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裳仆。 院中可真熱鬧腕让,春花似錦、人聲如沸歧斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至觉鼻,卻和暖如春俊扭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滑凉。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工统扳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畅姊。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓咒钟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親若未。 傳聞我的和親對象是個殘疾皇子朱嘴,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容