數據結構——數組

數組

github地址

數組基礎

  • 數組最大的有點:快速查詢。索引快
  • 數組最好應用于 “索引有語義” 的情況
  • 但并非所有有語義的索引都適用于數組(身份證號)
  • 數組也可以處理 ”索引沒有語義“ 的情況

封裝數組類

  • 數組類該具備的功能:增 刪 改 查

使用泛型:

  • 讓我們的數據結構可以放置 “任何” 數據類型

實現

構造函數

我們希望有兩個構造函數,可以支持給定容量和默認容量,這里默認容量我們設置成 10 :

template<typename T>
MyArray<T>::MyArray() {size_ = 0;capacity_ = 10;data_ = new T[capacity_];std::cout << "調用 MyArray() 構造." << std::endl;
}template<typename T>
MyArray<T>::MyArray(int capacity) {if (capacity <= 0) {std::cout << "MyArray(int) error. Capacity is illegal." << std::endl;throw "MyArray(int) error. Capacity is illegal.";}size_ = 0;capacity_ = capacity;data_ = new T[capacity_];std::cout << "調用 MyArray(int capacity) 構造." << std::endl;
}

析構函數

template<typename T>
MyArray<T>::~MyArray() {if (nullptr != data_) {delete[] data_;data_ = nullptr;}size_ = 0;capacity_ = 0;std::cout << "調用 ~MyArray() 析構." << std::endl;
}

拷貝構造和拷貝賦值

首先添加拷貝構造測試:

// 初始化
class ArrayTest : public testing::Test {protected:void SetUp() override {for (int i = 0; i < 10; ++i) {my_array_.AddLast(i);}}void TearDown() override {}MyArray<int> my_array_;
};// 拷貝構造測試
TEST_F(ArrayTest, CopyConstructorTest) {MyArray<int> my_array(my_array_);EXPECT_EQ(10,my_array.GetSize());EXPECT_EQ(9,my_array.Get(9));
}

這里出現了 TEST_F,在TDD實例有例子。

拷貝構造函數和賦值函數:

template<typename T>
MyArray<T>::MyArray(const MyArray &arr) {this->size_     = arr.size_;this->capacity_ = arr.capacity_;this->data_     = new T[capacity_];for (int i = 0; i < size_; ++i) {this->data_[i] = arr.data_[i];}std::cout << "調用  MyArray(const MyArray &arr) 拷貝構造" << std::endl;
}template<typename T>
MyArray<T> &MyArray<T>::operator=(const MyArray<T> &arr) {assert(this != &arr);if (this->data_ != nullptr) {delete[] this->data_;this->data_  = nullptr;}//分配內存this->size_     = arr.size_;this->capacity_ = arr.capacity_;this->data_     = new T[capacity_];//拷貝數據for (int i = 0; i < size_; i++) {//如果是自定義的復雜數據類型,必須對 = 運算賦進行重載,  operator=this->data_[i] = arr.data_[i];}std::cout << "調用 = 賦值操作 " << std::endl;return* this;
}

移動構造和移動賦值

首先添加測試用例:

// 移動構造和移動賦值測試
TEST_F(ArrayTest, MoveTest) {// 移動拷貝構造MyArray<int> my_array(std::move(my_array_));// 移動賦值傳入本身 會報錯
//  my_array = std::move(my_array);
//  EXPECT_EQ(10,my_array.GetSize());
//  EXPECT_EQ(9,my_array.Get(9));MyArray<int> my_array1;// 移動賦值my_array1 = std::move(my_array);EXPECT_EQ(10,my_array1.GetSize());EXPECT_EQ(0,my_array.GetSize());EXPECT_EQ(0,my_array_.GetSize());
}

移動構造和移動賦值:

template<typename T>
MyArray<T>::MyArray(MyArray &&arr) noexcept {assert(this != &arr);capacity_ = std::exchange(arr.capacity_, 0);size_     = std::exchange(arr.size_, 0);    // 非類類型成員的顯式移動data_     = std::move(arr.data_);           // 類類型成員的顯式移動arr.data_ = nullptr;std::cout << "調用  MyArray(const MyArray &&arr) 移動構造函數" << std::endl;
}template<typename T>
MyArray<T> &MyArray<T>::operator=(MyArray<T> &&arr) noexcept {assert(this != &arr);if (this->data_ != nullptr) {delete[] this->data_;this->data_ = nullptr;}//分配內存this->size_ = std::exchange(arr.size_, 0);this->capacity_ = std::exchange(arr.capacity_, 0);this->data_ = new T[capacity_];//拷貝數據for (int i = 0; i < size_; i++) {//如果是自定義的復雜數據類型,必須對 = 運算賦進行重載,  operator=this->data_[i] = std::move(arr.data_[i]);}std::cout << "調用 = 移動賦值操作 " << std::endl;return * this;
}

<< 重載

測試用例:

// 測試 << 重載
TEST_F(ArrayTest, OperatorTest) {std::cout << my_array_ << std::endl;
}

實現:

// 內部友元函數實現 重載輸出 << 操作符,這里必須定義成友元
friend std::ostream &operator<<(std::ostream &out, MyArray<T> &obj) {out << "MyArray size = " << obj.size_ << ", Capacity = " << obj.capacity_ << std::endl;out << "MyArray: [";for (int i = 0; i < obj.size_; ++i) {out << obj.data_[i];if (i != obj.size_ - 1)out << ", ";}out << "] ";return out;
}

[] 重載

測試用例:

// [] 測試
TEST_F(ArrayTest, SquareBracketsTest) {EXPECT_EQ(0,my_array_[0]);EXPECT_EQ(1,my_array_[1]);
}

實現:

template<typename T>
T &MyArray<T>::operator[](int index) {if (index < 0 || index >= size_) {std::cout << "[] fail. Index is illegal." << std::endl;throw "[] fail. Index is illegal.";}std::cout << "[] 調用" << std::endl;return this->data_[index];
}

添加元素

首先添加測試:

TEST(ArrayTestNoF, AddAndGet) {MyArray<int> my_array;my_array.Add(0,1);my_array.Add(1,2);my_array.Add(2,3);EXPECT_EQ(1,my_array.Get(0));EXPECT_EQ(2,my_array.Get(1));EXPECT_EQ(3,my_array.Get(2));my_array.AddFirst(4);EXPECT_EQ(4,my_array.Get(0));EXPECT_EQ(4,my_array.GetFirst());my_array.AddLast(5);EXPECT_EQ(5,my_array.Get(4));EXPECT_EQ(5,my_array.GetLast());
}

我們希望提供三個接口:在指定位置插入元素,在頭部和尾部插入元素

后兩個方法可以通過第一個方法實現,我們先實現第一個方法,如下:

template<typename T>
void MyArray<T>::Add(int index, T t) {if (index < -1 || index > size_) {std::cout << "Add fail. Index is illegal." << std::endl;throw "Add fail. Index is illegal.";}if (IsFull()) {  Resize(capacity_ * 2);}for (int i = size_; i > index; --i) {data_[i] = data_[i - 1];}data_[index] = t;size_++;
}

后兩個方法在此基礎上:

template<typename T>
void MyArray<T>::AddFirst(T t) {Add(0, t);
}template<typename T>
void MyArray<T>::AddLast(T t) {Add(size_, t);
}

判斷空滿

測試用例:

TEST(ArrayTestNoF, IsEmpty) {MyArray<int> my_array(10);EXPECT_TRUE(my_array.IsEmpty());
}
template<typename T>
bool MyArray<T>::IsFull() const {return size_ == capacity_;
}template<typename T>
bool MyArray<T>::IsEmpty() const {return size_ == 0;
}

獲取容量和大小

測試用例:

TEST_F(ArrayTest, GetSizeAndGetCapacity) {EXPECT_EQ(10,my_array_.GetCapacity());EXPECT_EQ(10,my_array_.GetSize());
}

實現:

template<typename T>
int MyArray<T>::GetSize() const {return size_;
}template<typename T>
int MyArray<T>::GetCapacity() const {return capacity_;
}

對指定位置元素賦值

測試用例:

TEST_F(ArrayTest, Set) {my_array_.Set(0,11);EXPECT_EQ(11,my_array_.GetFirst());
}

實現:

template<typename T>
void MyArray<T>::Set(int index, T t) {if (index < 0 || index >= size_) {std::cout << "Set Fail. Index is illegal." << std::endl;throw "Set fail. Index is illegal.";}if (IsEmpty()) {std::cout << "Set Fail. Array is empty." << std::endl;throw "Set fail. Array is empty.";}data_[index] = t;
}

查找元素

測試用例:

TEST_F(ArrayTest, FindAndContain) {EXPECT_EQ(0,my_array_.Find(0));EXPECT_TRUE(my_array_.Contain(1));EXPECT_FALSE(my_array_.Contain(111));
}

實現:

template<typename T>
int MyArray<T>::Find(T t) const {if (IsEmpty()) {std::cout << "Find fail. Array is empty." << std::endl;throw "Find Fail. Array is empty";}for (int i = 0; i < size_; ++i) {if (data_[i] == t) {return i;}}return -1;
}template<typename T>
bool MyArray<T>::Contain(T t) const {return Find(t) != -1;
}

調整空間

測試用例:

TEST_F(ArrayTest, Resize) {EXPECT_EQ(10,my_array_.GetSize());EXPECT_EQ(10,my_array_.GetCapacity());my_array_.AddLast(10);EXPECT_EQ(11,my_array_.GetSize());EXPECT_EQ(20,my_array_.GetCapacity());for (int j = 0; j < 6; ++j) {my_array_.RemoveLast();}EXPECT_EQ(5,my_array_.GetSize());EXPECT_EQ(20,my_array_.GetCapacity());my_array_.RemoveLast();EXPECT_EQ(4,my_array_.GetSize());EXPECT_EQ(10,my_array_.GetCapacity());
}

全部的源碼傳送門

時間復雜度分析

  • 添加操作?????? O(n)
    • addLast(e)????? O(1)
    • addFirst(e)????? O(n)
    • add(index,e)???O(n/2)=O(n)

最壞情況:O(n) , resize() 時間復雜度 O(n)

  • 刪除操作 O(n)
    • removeLast()?????O(1)
    • removeFirst()?????O(n)
    • remove(index)????O(n/2)=O(n)

最壞情況:O(n) , resize() 時間復雜度 O(n)

  • 修改操作 O(1)

    • set(index,e)????O(1)
  • 查找操作

    • get(index)??? O(1)
    • contains(e)??O(n)
    • find(e) ? ? ? ? ? O(n)

均攤復雜度分析

由于 resize() O(n) 的存在,所以每次 addLastremoveLast 時間復雜度依然是 O(n) ?

resize()復雜度分析

假設當前 capacity 為 8 ,并且每次添加操作都是 addLast , 9 次添加操作觸發一次 resize(), 總共進行了 9 次基本操作, n 次操作才會觸發一次時間復雜度為 O(n) 的操作,平攤到前 n 次操作,相當于前 n 次操作每次復雜度為 O(1+1)=O(1)

在這個例子中,這樣均攤計算,比計算最壞情況更有意義。

但是會出現這么一種情況:

復雜度振蕩

size 為 n 時,我們先后執行 addLast , removeLast , addLast , removeLast , 每一次的操作時間復雜度都是 O(n) 的,出現問題的原因在于 removeLastresize 過于著急 (Eager)

解決方案:Lazy

size == capacity / 4 時,才將 capacity 減半。

測試是否有內存泄露

使用 valgrind 測試測試用例是否存在內存泄露

valgrind --leak-check=full --show-reachable=yes --trace-children=yes ./ArraysTest

使用 Valgrind 分析 C++ 程序時,有一些問題需要留意。例如,這個程序并沒有發生內存泄漏,但是從 HEAP SUMMARY 可以看到,程序分配了 303 次內存,但卻只釋放了 303 次內存,為什么會這樣呢?
  實際上這是由于 C++ 在分配內存時,為了提高效率,使用了它自己的內存池。當程序終止時,內存池的內存才會被操作系統回收,所以 Valgrind 會將這部分內存報告為 reachable 的,需要注意,reachable 的內存不代表內存泄漏,例如,從上面的輸出中可以看到,有 72704 個字節是 reachable 的,但沒有報告內存泄漏。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448065.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448065.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448065.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

十分鐘入門 RocketMQ

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本文首先引出消息中間件通常需要解決哪些問題&#xff0c;在解決這些問題當中會遇到什么困難&#xff0c;Apache RocketMQ作為阿里開源的…

高智商孩子14個獨有的特點

每一位家長都希望自己的孩子具有高智商&#xff0c;但據專家分析孩子的智商一種是與生俱來的&#xff0c;另一種是在2歲之前還可以提高的&#xff0c;一起來看看怎樣才能提高孩子的智商? 智商高的孩子都具有哪些特點? 提高孩子智商的方法 1、改變兒童的飲食習慣。 提高孩…

Onvif2.6.1命名空間前綴對照

Onvif2.6.1命名空間前綴對照 tds http://www.onvif.org/ver10/device/wsdl tev http://www.onvif.org/ver10/events/wsdl tls http://www.onvif.org/ver10/display/wsdl tmd http://www.onvif.org/ver10/deviceIO/wsdl timg http://www.onvif.org/ver20/imaging/wsdl trt…

使用delegate類型設計自定義事件

在C#編程中&#xff0c;除了Method和Property&#xff0c;任何Class都可以有自己的事件&#xff08;Event&#xff09;。定義和使用自定義事件的步驟如下&#xff1a; &#xff08;1&#xff09;在Class之外定義一個delegate類型&#xff0c;用于確定事件程序的接口 &#xff0…

各種學習資源 文檔、手冊 (Docker 、springboot 、Guava、git、logback 、Linux 、MQ、vue、Axios)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. Docker 中文手冊 &#xff1a;https://yeasy.gitbooks.io/docker_practice/advanced_network/bridge.html 2. RESTful java with JA…

C語言的“編譯時多態”

typeof 在 kernel 中的使用 —— C 語言的“編譯時多態” C 語言本身沒有多態的概念&#xff0c;函數沒有重載的概念。然而隨著 C 語言編寫的軟件逐漸龐大&#xff0c;越來越多地需要引入一些其他語言中的特性&#xff0c;來幫助更高效地進行開發&#xff0c;Linux kernel 是一…

看臉色知體內各積毒 有效清潔內臟妙方

觀察下五臟六腑是否中毒。 淤血、痰濕、寒氣這些不能及時排出體外&#xff0c;危害健康和精氣神的物質&#xff0c;中醫稱之為毒素&#xff0c;在鏡子里你也可以看出它們。識別之后&#xff0c;你更需要有效的內臟清潔妙方! 癥狀一&#xff1a;面色青兩側長痘黃褐斑愁云滿面…

UTC Time

整個地球分為二十四時區&#xff0c;每個時區都有自己的本地時間。在國際無線電通信場合&#xff0c;為了統一起見&#xff0c;使用一個統一的時間&#xff0c;稱為通用協調時(UTC, Universal Time Coordinated)。UTC與格林尼治平均時(GMT, Greenwich Mean Time)一樣&#xff0…

解決:Unknown custom element: <myData> - did you register the component correctly? For recursive compon

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 引用一個組件報錯&#xff1a; Unknown custom element: <myData> - did you register the component correctly?For recursi…

無處不在的container_of

無處不在的container_of linux 內核中定義了一個非常精煉的雙向循環鏈表及它的相關操作。如下所示&#xff1a; struct list_head {struct list_head* next, * prev; };ubuntu 12.04 中這個結構定義在 /usr/src/linux-headers-3.2.0-24-generic/include/linux/types.h 中&…

程序員學習能力提升三要素

摘要&#xff1a;IT技術的發展日新月異&#xff0c;新技術層出不窮&#xff0c;具有良好的學習能力&#xff0c;能及時獲取新知識、隨時補充和豐富自己&#xff0c;已成為程序員職業發展的核心競爭力。本文中&#xff0c;作者結合多年的學習經驗總結出了提高程序員學習能力的三…

時間,數字 ,字符串之間的轉換

package com.JUtils.base;import java.sql.Timestamp; import java.text.SimpleDateFormat;/*** 轉換工具類<br>* 若待轉換值為null或者出現異常&#xff0c;則使用默認值**/ public class ConvertUtils {/*** 字符串轉換為int*** param str * 待轉換的字符串* param …

宏定義及相關用法

宏定義及相關用法 歡迎各位補充 目錄 一些成熟軟件中常用的宏定義&#xff1a;使用一些內置宏跟蹤調試&#xff1a;宏定義防止使用時錯誤&#xff1a;宏與函數 帶副作用的宏參數 特殊符號&#xff1a;’#’、’##’ 1、一般用法2、當宏參數是另一個宏的時候 __VA_ARGS__與##…

解決:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 轉化為 2.x)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 vue項目原本是用0.x版本的vue-router&#xff0c;但是去報出&#xff1a;Cannot read property component of undefined 這是因為版本問…

AMD Mantle再添新作,引發下代GPU架構猜想

摘要&#xff1a;今年秋天即將發布的《希德梅爾文明&#xff1a;太空》將全面支持AMD Mantle API&#xff0c;如此強大的功能背后離不開強大的CPU、GPU支持。上周AMD爆出了下一代海盜島R9 300系列&#xff0c;據網友猜測海盜島家族可能用上速度更快的HBM堆棧式內存。 小伙伴們…

不作35歲的程序員?

程序員三部曲--不作35歲的程序員?摩西2000 在中國&#xff0c;程序員不能超過35歲&#xff0c;似乎已經是不爭的事實&#xff0c;軟件開發工作就是青春飯&#xff0c;頂多靠畢業這十年的時間&#xff0c;超過這個年齡&#xff0c;要不成功躍身成為管理者&#xff0c;要不轉…

linux下使用TC模擬弱網絡環境

linux下使用TC模擬弱網絡環境 模擬延遲傳輸簡介 netem 與 tc: netem 是 Linux 2.6 及以上內核版本提供的一個網絡模擬功能模塊。該功能模塊可以用來在性能良好的局域網中,模擬出復雜的互聯網傳輸性能,諸如低帶寬、傳輸延遲、丟包等等情 況。使用 Linux 2.6 (或以上) 版本內核…

CDN 是什么 、CDN 引入

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 CDN 的全稱是 Content Delivery Network&#xff0c;即內容分發網絡。 CDN的基本原理是廣泛采用各種緩存服務器&#xff0c;將這些緩存…

長壽的人會有的8個健康理念

長壽的人會有的8個健康理念。年輕的時候總是在揮霍身體健康&#xff0c;吸煙、喝酒沒有節制&#xff0c;到老了之后身體會出現各種問題。老年人如果想要身體健康、長壽的話&#xff0c;就要從日常生活習慣做起。下面小編就來介紹長壽的人會有的8個健康理念&#xff1a; 1、少…

Ubuntu下selenium+Chrome的安裝使用

Ubuntu下seleniumChrome的安裝使用 安裝 chrome 官網下載安裝包 sudo dpkg -i google-chrome-stable_current_amd64.deb whereis google-chrome 安裝selenium pip3 install selenium 下載chromedriver(火狐使用geckodriver)驅動 http://npm.taobao.org/mirrors/chromed…