《劍指offer 閱讀筆記三》數據結構之數組

排版若是影響閱讀能夠 閱讀原文python

數組能夠說是最簡單的一種數據結構,它佔據一塊連續的內存並按照順序存儲數據。ios

建立數組時,須要先指定數組容器的大小,而後根據大小分配內存。c++

因爲數組的內存是連續的,因而能夠根據下標在O(1)時間讀寫任何元素,所以它的時間效率很高。git

根據數組時間效率高的優勢, 實現簡單的哈希表,用數組來實現簡單的哈希表,把數組的下標設爲哈希表的鍵,數組元素爲哈希表的值,
有了這樣的哈希表,咱們能夠在O(1)時間內查找。程序員

爲了解決數組空間效率不高的問題,人們又設計實現了多種動態數組,好比c++中的STL中的vector,爲了不浪費,先爲數組開闢較小的空間,而後往數組中添加數組。
當數組的數據超過數組容量時,咱們再從新分配一塊更大的空間(c++ 中vector 爲以前的兩倍,python list也爲以前的兩倍),把以前的數據複製到新數組中,再把以前的內存釋放。
這對時間性能有負面影響,所以使用動態數組時要儘可能減小改變數組容量大小的次數。github

關鍵點1

在c/c++中,數組和指針是既相關又有區別的概念,當咱們聲明一個數組時,其數組的名字也是一個指針,該指針指向數組的第一個元素。咱們能夠用指針來訪問數組。
c/c++沒有記錄數組的大小,所以在訪問數組元素時,程序員要確保沒有超出數組的邊界。面試

運行下面代碼,請問輸出什麼?算法

#include <iostream>
using namespace std;

int GetSize(int data[])
{
return sizeof(data);
}

int main(int argc, const char* argv[])
{
int data1[] = {1, 2, 3, 4, 5};
int size1 = sizeof(data1);

int* data2 = data1;
int size2 = sizeof(data2);

int size3 = GetSize(data1);
cout << size1 << endl << size2 << endl << size3 << endl;
return 0;
}

答案是: 20, 8, 8,data是一個數組,sizeof(data)求的是數組的大小,5個整數,每一個整數佔4個字節,所以佔20個字節。
data2是指針,指針在32位操做系統上佔4位,64位操做系統佔8位。在c/c++中,當數組看成函數的參數進行傳遞時,數組就自動退化爲同類型的指針。所以,儘管函數GetSize的參數data被聲明爲數組,但他會退化爲指針。編程

面試題3:

找出數組中重複的數字

在一個長度爲n的數組裏的全部數字都在0~n-1的範圍內。數組中某些數字是重複的,可是不知道有幾個數字是重複的了,也不知道數字重複了幾回。請找出數組中任意一個重複的數字。例如,若是輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出重複的數字2或者3。數組

解法一: 解決這個問題的一個簡單的辦法是先把輸入的數組排序。從排序的數組中找出重複的數字只須要掃描排序的數組就能夠了。排序一個長度爲n的數組須要O(nlgn)的時間。

解法二: 還能夠利用哈希表來解決這個問題,從頭至尾按順序掃描數組的每一個數字,每掃描到一個數字的時候,均可以用O(1)的時間來判斷哈希表裏是否已經包含了該數字,若是哈希表裏尚未這個數字,就把它加入到哈希表。若是哈希表裏已經存在這個數字,就找到了一個重複的數字。這個算法的時間複雜度是O(n),但它提升時間效率是以一個大小爲O(n)的哈希表爲代價的。

解法三: 咱們注意到數組中的數字都在0~n-1的範圍內。若是這個數組中沒有重複的數字,那麼當數組排序以後數組i將出如今下標爲i的位置。因爲數組中有重複的數字,有些位置可能存在多個數字。 如今讓咱們重排數組,從頭至尾掃描數組,當掃描到下標i時,判斷數組下標i的元素是否和i相同(arr[i] == i),不相同,則把它和第arr[i]個數進行交換(swap(arr[i], arr[arr[i]])),直到arr[i]和i同等,把arr[i]放回屬於他的位置。重複上述的操做。
直到咱們發現一個重複的數字。

arr[] = {2, 3, 1, 0, 2, 5, 3}
i = 0; arr[i] != i; swap(arr[i], arr[arr[i]]);  // {1, 3, 2, 0, 2, 5, 3}
   arr[i] != i; swap(arr[i], arr[arr[i]]);  // {3, 1, 2, 0, 2, 5, 3}
   arr[i] != i; swap(arr[i], arr[arr[i]]);  // {0, 1, 2, 3, 2, 5, 3}
i = 1; arr[i] == i;
i = 2; arr[i] == i;
i = 3; arr[i] == i;
i = 4; arr[i] != i; arr[arr[i]] = arr[i]; return arr[i];

c++: array_repeat.cpp

python: array_repeat.py

本題考點:

  • 老查應聘者對一維數組的理解及編程能力。一維數組在內存種佔據連續的空間,所以咱們能夠根據下標定位對應的元素。
  • 考查應聘者分析問題的能力。當應聘者發現問題比較複雜時。 能不能經過具體的例子找出其中的規律,是可否解決這個問題的關鍵。

不修改數組找出重複的數字

在一個長度爲n+1的數組裏的全部數字都在1~n的範圍內,因此數組種至少有一個數字是重複的。 請找出數組中任意一個重複的數字,但不能修改輸入的數組。例如,若是輸入長度爲8的數組{2, 3, 5, 4, 3, 2, 6, 7}, 那麼對應的輸出是重複的數字2或者3。

解法一: 看起來和上一題相似,因爲題目要求不能修改輸入的數組,咱們能夠建立一個n+1的數組爲哈希表來輔助,則須要O(n)的空間,O(n)的時間。

解法二: 接下來咱們嘗試O(1)的空間,咱們把從1-n是數字從中間的數字m分爲兩部分,前一半爲1m,後一半爲m+1n,若是1~m的數字出現的次數超過m,那麼這一半確定有重複的數字,不然另外一半確定有重複的數字。咱們能夠繼續把重複的區間一分爲二,直到找到一個重複的數字。這個過程和二分法很像,只是多了一步統計區間裏的數字。
每次統計範圍內的數字O(n),二分查找O(lgn),時間複雜度爲O(nlgn),空間複雜度爲O(1),這是利用時間換空間的算法。

int nums[] = {2, 3, 5, 4, 3, 2, 6, 7};  // length = 8;
//mid=(1+8)/2, 1~4 出現了5次,5-7 出現了3次,重複數字在1~4內
//mid=(1+4)/2, 1~2 出現了2次,3-4 出現了3次,重複數字在3~4內
//mid=(3+4)/2, 3 出現了2次,4 出現了1次,重複數字爲3

須要指出的是,這種算法並不能找出全部重複的數字。例如上例,咱們只找出了3,其實2也是重複的,因此若是面試官要求的功能不一樣或是性能要求不一樣,咱們選擇的算法也不相同,這就須要和麪試官溝通。

c++: array_repeat2.cpp
python: array_repeat2.py

本題考查:

  • 考查一維數組的理解和編程能力。
  • 考查應聘者對二分法的理解,並能快速、正確地實現二分查找算法。
  • 考查應聘者的溝通能力,應聘者只有具有良好的溝通能力,才能充分了解面試官的需求,從而針對性地選擇算法解決問題。

面試題4: 二維數組中的查找

在一個二維數組中,每一行都按照從左到右的遞增的順序排序,每一列都按照從上到下的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

解法一: 遍歷數組,判斷目標元素是否在二維數組內,時間複雜度O(m*n), 空間複雜度O(1);
解法二: 找到目標元素所在的列,遍歷目標列,時間複雜度O(m+n),空間複雜度O(1);
解法三: 從最右上角開始找,首先,咱們選取數組右上角的9,因爲9>7, 而且9仍是第4列的最小的數,所以7不可能出如今數字9所在列,因而咱們把這一列從須要考慮的區域踢出,整個過程以下如所示。

c++: find_number_in_2D_array.cpp
python: find_number_in_2D_array.py
在這裏插入圖片描述

相關文章
相關標籤/搜索