問題:如何判斷某個數據元素是否存在於線性表中?
- 能夠爲線性表(List)增長一個查找操做
int find(const T &e) const;node
參數:ios
- 待查找的數據元素
返回值編程
- >=0: 數據元素第一次在線性表中出現的位置
- -1: 數據元素不存在
LinkList<int> list; for (int i=0; i<5; ++i) { list.insert(0, i); } cout << list.find(3) << endl;
LinkList.hide
#ifndef LINKLIST_H #define LINKLIST_H #include "List.h" #include "Exception.h" namespace DTLib { template <typename T> class LinkList : public List<T> { public: LinkList() { m_header.next = nullptr; m_length = 0; } bool insert(const T &e) override // O(n) { return insert(m_length, e); } bool insert(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i <= m_length)); if (ret) { Node *node = new Node(); if (node != nullptr) { Node *current = position(i); node->value = e; node->next = current->next; current->next = node; ++m_length; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ..."); } } return ret; } bool remove(int i) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { Node *current = position(i); Node *toDel = current->next; current->next = toDel->next; delete toDel; --m_length; } return ret; } bool set(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { position(i)->next->value = e; } return ret; } T get(int i) const // O(n) { T ret; if (!get(i, ret)) { THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); } return ret; } bool get(int i, T &e) const override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { e = position(i)->next->value; } return ret; } int find(const T &e) override // O(n) { int ret = -1; int i = 0; Node *node = m_header.next; while (node) { if (node->value == e) { ret = i; break; } else { node = node->next; ++i; } } return ret; } int length() const // O(1) { return m_length; } void clear() // O(n) { while (m_header.next) { Node *toDel = m_header.next; m_header.next = toDel->next; delete toDel; --m_length; } } ~LinkList() // O(n) { clear(); } protected: struct Node : public Object { T value; Node *next; }; mutable struct : public Object { char reserved[sizeof (T)]; Node *next; }m_header; int m_length; Node *position(int i) const // O(n) { Node *ret = reinterpret_cast<Node*>(&m_header); for (int p=0; p<i; ++p) { ret = ret->next; } return ret; } }; } #endif // LINKLIST_H
文件:main.cpp函數
#include <iostream> #include "LinkList.h" using namespace std; using namespace DTLib; int main() { cout << "main begin" << endl; LinkList<int> list; for (int i=0; i<5; ++i) { list.insert(0, i); } for (int i=0; i<list.length(); ++i) { cout << list.get(i) << endl; } cout << "-----------" << endl; cout << list.find(3) << endl; cout << "main end" << endl; return 0; }
輸出:this
main begin 4 3 2 1 0 ----------- 1 main end
class Test { public: Test(int i = 0) { this->i = i; } private: int i; };
會發生什麼?
void main() { linkList<Test> list; }
編譯輸出:
error: no match for 'operator==' (operand types are 'Test' and 'const Test') if (node->value == e)
分析:
LinkList 模板類的 find 函數須要使用 '==' 運算符,而 Test 類沒有提供。spa
帶給使用者的疑惑:
僅僅定義LinkList<Test>類對象,而沒有進行查找操做,爲何會報錯呢?難道在使用LinkList管理自定義類類型時強制要求使用者重載'=='運算符?這是極不便利的。設計
解決方案:(兩全折中方案)
在頂層父類實現默認的 '==' 重載實現;
當自定義類類型時,將自定義類繼承自Object。
(以上僅保證編譯經過,當須要進行find時,還須要使用者提供自定義類類型的'=='重載實現)指針
文件:Object.hcode
#ifndef OBJECT_H #define OBJECT_H namespace DTLib { class Object { public: void *operator new (unsigned int size) noexcept; void operator delete (void *p); void *operator new[] (unsigned int size) noexcept; void operator delete[] (void *p); bool operator == (const Object &obj); bool operator != (const Object &obj); virtual ~Object() = 0; }; } #endif // OBJECT_H
文件:Object.cpp
#include "Object.h" #include <cstdlib> namespace DTLib { void *Object::operator new (unsigned int size) noexcept { return malloc(size); } void Object::operator delete (void *p) { free(p); } void *Object::operator new[] (unsigned int size) noexcept { return malloc(size); } void Object::operator delete[] (void *p) { free(p); } bool Object::operator == (const Object &obj) { return (this == &obj); } bool Object::operator != (const Object &obj) { return (this != &obj); } Object::~Object() { } }
文件:main.cpp
#include <iostream> #include "LinkList.h" using namespace std; using namespace DTLib; class Test : public Object { public: Test(int i = 0) { this->i = i; } bool operator== (const Test &t) { return (this->i == t.i); } private: int i; }; int main() { cout << "main begin" << endl; Test t1(1); Test t2(2); Test t3(3); LinkList<Test> list; list.insert(t1); list.insert(t2); list.insert(t3); cout << list.find(t2) << endl; cout << "main end" << endl; return 0; }
輸出:
main begin 1 main end
操做 | SqlList | LinkList |
insert | O(n) | O(n) |
remove | O(n) | O(n) |
set | O(1) | O(n) |
get | O(1) | O(n) |
find | O(n) | O(n) |
length | O(1) | O(1) |
clear | O(1) | O(n) |
有趣的問題
順序表的總體時間複雜度比單鏈表要低,那麼單鏈表還有使用價值嗎?
實際工程中,時間複雜度只是效率的一個參考指標
- 對於內置基礎類型,順序表和單鏈表的效率不想上下
- 對於自定義類型,順序表在效率上低於單鏈表
插入和刪除
- 順序表:設計大量數據對象的複製操做
- 單鏈表: 只設計指針操做,效率與數據對象無關
數據訪問
- 順序表: 隨機訪問,可直接定位數據對象
- 單鏈表: 順序訪問,必須從頭訪問數據對象,沒法直接定位
順序表
- 數據元素的類型相對簡單,不涉及深拷貝
- 數據元素相對穩定,訪問操做遠多於插入和刪除操做
單鏈表
- 數據元素的類型相對複雜,複製操做相對耗時
- 數據元素不穩定,須要警察插入和刪除,訪問操做較少
- 線性表中元素的查找依賴於相等比較操做符(==)
- 順序表適用於訪問需求量較大的場合(隨機訪問)
- 單鏈表適用於數據元素頻繁插入和刪除的場合(順序訪問)
- 當數據類型相對簡單時,順序表和單鏈表的效率不相上下
以上內容整理於狄泰軟件學院系列課程,請你們保護原創!
有關 "find的疑惑" 章節的相關說明一:
#include <iostream> #include "LinkList.h" using namespace std; using namespace DTLib; class Test : public Object { public: Test(int i) { this->i = i; } bool operator== (const Test &t) { return (this->i == t.i); } private: int i; }; int main() { cout << "main begin" << endl; LinkList<Test> list; cout << "main end" << endl; return 0; }
編譯輸出:
error: no matching function for call to 'Test::Test()'
當沒有對Test的構造函數提供默認參數,編譯時會報錯。
分析:
struct Node : public Object { T value; // 這裏致使!! Node *next; };
STL中的表現
#include <iostream> #include <list> using namespace std; class Test { int i; public: Test(int i) { this->i = i; } }; int main() // 編譯正常 { list<Test> list; return 0; }
由於二者採用不一樣的策略,STL更爲友好。
有關 "find的疑惑" 章節的相關說明二:
#include <iostream> #include "LinkList.h" using namespace std; using namespace DTLib; class Test { public: Test(int i = 0) { this->i = i; } private: int i; }; int main() { cout << "main begin" << endl; LinkList<Test> list; cout << "main end" << endl; return 0; }
編譯輸出:
error: no match for 'operator==' (operand types are 'Test' and 'const Test') if (node->value == e)
我的疑惑:
在C++類模板編程時,編譯器會進行兩次編譯,
第一次,對類模板自己語法語義檢查;
第二次,對實例化後的調用部分進行編譯。
那麼上面代碼沒有對 find 進行調用爲何會編譯報錯呢?
有明白這裏的同窗麻煩留言告知一下。
STL中的表現:
#include <iostream> #include <list> using namespace std; class Test { int i; public: Test(int i) { this->i = i; } }; int main() { list<Test> list; return 0; }
編譯經過
#include <iostream> #include <list> using namespace std; class Test { int i; public: Test(int i) { this->i = i; } }; int main() { list<Test> list; list.sort(); return 0; }
編譯輸出:
error: no match for 'operator<' (operand types are 'Test' and 'Test') if (*__first2 < *__first1) ~~~~~~~~~~^~~~~~~~~~~
STL 中 list<T> 符合二次編譯現象。