資料結構與演算法(二)向量結構

2021-09-03 07:00:16 字數 4956 閱讀 8453

陣列:起始於位址a、物理位置連續的一段儲存空間。a[ ]。a[ i]=a+i x s.int *p=a +3,則可以認為p[0]=a[3]。

*p+=2;就相當於*p=*p+2;

其中*p指向字串「123」的第乙個元素,即『1』,指標p向後移兩個元素的位址,即指向『3』

而*(p+2)才是基於p每次向後移乙個字串的長度,即*(p+2)指向「456」

向量的構造:向量的adt介面,向量的模板類。

向量裡面內建了元素型別為t的私有陣列,由new申請而成。向量的構造:預設構造,基於複製的構造。

向量的析構:釋放用於存放元素的內部陣列。向量的動態空間管理:要求向量的實際規模與內部陣列容量的比值也稱裝填因子,不超過一,也不接近於0。可擴充策略:若未滿時不擴容,否則加倍,並複製原內容到新陣列,釋放原來的。在插入時均會調入該演算法。在刪除操作遠多餘插入操作,則會減小裝填因子。所以,需要在每次刪除操作後,進行縮容演算法,保證裝填因子大於25%。在對單次操作的執行速度及其敏感的應用場合以上策略並不適用。

向量資料結構的構建:抽象資料型別的構建要素:不管是宣告還是實現均在同乙個標頭檔案下。

1.類的介面宣告

2.類介面包含成員變數,成員函式。

成員分為私有成員,公有成員,保護成員。保護成員:關鍵字protected修飾的成員宣告為保護成員,不能被類物件直接訪問。其訪問許可權與私有成員近似,所不同的是其可對於基類的派生類是可見的,而私有成員則不可見。

所以根據情況將變數和函式分為保護,私有和公有三部分。

保護成員函式:初始化,複製向量某一段,擴容,縮容,排序,取最大元素,各種排序。

公有成員:類物件可直接訪問的介面。比如:唯讀,可寫,遍歷 ,構造等。四種介面。

size(),建構函式,析構函式一般為虛函式,insert(),find(),remove(),disorded(),sort()。

保護成員:成員變數和函式一般都只能在類內訪問,無法類物件訪問。一般為公有成員所呼叫。結構一般如下:

templateclass vector容量為c,規模為s,預設初始化。s必須小於s。

vector(t const*a,int n){};陣列複製,為何用const。可用a[i]表示元素。

t& operator (int r) const;過載下標操作符,從而引用各元素。為何返回引用?

vector& opertor=(vectorconst&);過載賦值操做符。為何必須返回物件的引用呢?

};實現**:

templatevector& vector::operator=(vectorconst& v)

1.關於const的使用:一般若函式不改變其傳入的引數,則對該引數加const。

t const obj = const t obj。表示該物件為乙個常量,不能改變。const t*obj 表明該obj為乙個指標,指向乙個任意t常量或者變數,但卻將其當做常量。也可以寫成t const*obj。

t *const obj,乙個指標常量,只能指向乙個固定的變數obj。t const *const obj 表明乙個指標常量可以指向乙個變數或常量,但當做常量來看。也可以寫成const t*const obj。

常量引用也是類似,t const &obj 等價於const t &obj。均指對常量的引用,初始化時允許引用的物件可以不是常量。const修飾的量確切的說是唯讀量。

一般若函式不改變成員變數的值,可以在函式後面加const,則會導致該函式無法呼叫類中的非const成員函式,可以訪問所有成員變數不能修改。

陣列無法值傳遞,只能靠指標傳遞。

2.鏈式賦值

如何理解函式的返回值?

函式的返回值一般與返回型別相同。若為空返回型別,則return 可當做break退出函式。

值是如何被返回的?返回的值用於初始化呼叫點的乙個臨時量,該臨時量為函式呼叫的結果。

返回的值一般為乙個未命名的臨時物件,再將該臨時物件的值對呼叫點的值進行初始化。

賦值運算子的結果為左側運算物件,是乙個左值。

所以如果返回型別為引用的話,則該物件不能為區域性物件。指標也是。可以避免拷貝。引用返回左值。

賦值運算子過載要返回引用以用於類似 (a=b)=c 這樣的再次對a=b進行寫操作的表示式。所以引用必須。

在物件導向程式設計中,物件間的相互拷貝和賦值是經常進行的操作。

如果物件在申明的同時馬上進行的初始化操作,則稱之為拷貝運算。例如:

class1 a("af"); class1 b=a;

此時其實際呼叫的是b(a)這樣的淺拷貝操作。

如果物件在申明之後,在進行的賦值運算,我們稱之為賦值運算。例如:

class1 a("af"); class1 b;

b=a;

此時實際呼叫的類的預設賦值函式b.operator=(a);

關於為何要過載的問題?

int main()

在程式編譯之後,a和b在stack上都被分配相應的記憶體大小。只不過物件a的域都被初始化,而b則都為隨機值。

如果我們執行以下:

b=a;

則其執行的是預設定義的預設的賦值運算。所謂預設的賦值運算,是指物件中的所有位於stack中的域,進行相應的複製。但是,如果物件有位於heap上的域的話,其不會為拷貝物件分配heap上的空間,而只是指向相同的heap上的同乙個位址。因此,對於預設的賦值運算也就是淺拷貝操作,如果物件域內沒有heap上的空間,其不會產生任何問題。但是,如果物件域內有heap上的空間,那麼在析構物件的時候,就會連續兩次釋放heap上的同一塊記憶體區域,從而導致異常。.解決辦法--過載(overload)賦值運算子。

隨機向量的生成:置亂器,自後向前,將v[i-1]與v[0,i]中的某一元素進行交換。

向量的唯一化處理:o(n*n),若假如向量是有序的,再進行唯一化,可用o(n)的時間。依次用while迴圈找到不同元素,將其移植到緊鄰於前者右側。發現在唯一化時,向量的大小變了,但是後面的還是有元素存在。若果經過縮容,則不存在。

向量的查詢:無序查詢,有序查詢,二分查詢演算法。

向量的排序:排序演算法的總結,分類:內部排序(記憶體足夠容納),外部排序(借助外部儲存,記憶體只能容納一小部分資料。)

cba式演算法,即比較樹演算法。

穩定性:考察演算法對重複元素的處理。即重複元素的相對次序在排序前後保持一致。

氣泡排序:自左向右,逐一檢查各對相鄰元素,若逆序,則交換區域性有序。穩定演算法。

歸併排序:兩個有序向量,各取出其首元素作比較,小的取出追加至輸出向量末尾,該元素的後繼成為新的首元素。只需載入兩個向量的首元素。主體結構是典型的分治策略。先分治再合併。關鍵是合併程式。如何寫?

二分查詢演算法:返回值為查詢到的目標位置或者比目標數大的第乙個數的位置。範圍為[begin,end).

適用有重複數字查詢。begin=mi+1是為了防止進入死迴圈。有兩種情況。一種是找到結果,一種是沒找到。沒找到又分為兩種情況,一種是接近目標值,一種是等於begin或者是end。

int find(vector&a,int begin,int end,int target)

int mi=0;

int max=end;

while(begintarget) end=mi;

else return mi;

}if(begin==max) return -1;在執行時會檢測陣列是否越界情況。若begin=max,且a[begin]則會引發越界錯誤,儘管可用begin=max,執行返回,依舊會越界,所以必須嚴格要求陣列不越界。也可證明程式非嚴格順序執行。

return begin;

}關於二分查詢,若範圍為[begin,end]呢?可知結果不會有任何改變。依然正確。

merge(rank lo,rank mi,rank hi,t *)

int mid=(begin+end)/2;

long long left=0;

long long right=0;

long long count=0;

left=guibingsort(data,copy,begin,mid);

right=guibingsort(data,copy,mid,end);

count=merge(data,copy,begin,mid,end);

return left+right+count;

}int inversepairs(vectordata)

};在計算逆序時出錯。也就是在融合時計算逆序出錯。歸併排序屬於明顯的偽遞迴,所以可以轉化為迭代的方式計算。

向量的具體操作介面:

構造:std::vectorvalues;沒有元素,沒有分配空間。

std::vectorvalues(20);20個元素值為0

std::vectorvalues(20,1);20個元素值為一。

std:vectorwords ;列表初始化。

std::vectorwords_copy ;輸入迭代器。陣列列表均可,但是所指向的物件必須為string。[first,last).

std::vectorfourth (third); 另乙個vector物件。

獲取訪問元素:

values.front();

values.back();

values.push_back();

values.pop_back();

values.emplace_back();

auto iter = words.insert(++std::begin(words), "two");返回的迭代器指向被插入的元素 string(」two」)

auto riter = std::find(std::rbegin(str), std::rend(str) , "one");

data.clear();

auto iter = data.erase(std::begin(data)+1);

auto iter = data.erase(std::begin(data)+1,std::begin(data)+3);

std::swap(std::begin(data)+1,std::end(data)-1);

資料結構與演算法二

資料結構作為一門學科主要研究資料的各種邏輯結構和儲存結構,以及對資料的各種操作。因此,主要有三個方面的內容 資料的邏輯結構 資料的物理儲存結構 對資料的操作 或演算法 通常,演算法的設計取決於資料的邏輯結構,演算法的實現取決於資料的物理儲存結構。邏輯結構 是指資料物件中資料元素之間的相互關係。其實這...

資料結構與演算法(二)

函式的漸近增長 給定兩個函式 f n 和 g n 如果存在乙個整數n,使得對於所有的n n,f n 總是比g n 大,那麼我們說f n 的增長漸近快於g n 如圖 演算法時間複雜度的定義 在進行時間演算法分析時,語句總的執行次數t n 是關於問題規模n的函式,進而分析t n 隨n的變化情況並確定t ...

資料結構與演算法 二

佇列是一種特殊的線性表 線性結構 特殊之處在於它只允許在表的前端 front 進行刪除操作,而在表的後端 rear 進行插入操作,和棧一樣,佇列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。佇列中沒有元素時,稱為空佇列。佇列按照邏輯資料結構可以分為順序佇列和迴圈佇列....