您好,登錄后才能下訂單哦!
A.鏈式存儲的定義
為了表示每個數據元素與直接后繼元素之間的邏輯關系;數據元素除了存儲本身的信息外,還需要存儲其直接后繼的信息
圖示
B鏈式存儲邏輯結構
基于鏈式存儲結構的線性表中,每個結點都包含數據域和指針域
1.數據域:存儲數據元素本身
2.指針域:存儲相鄰結點的地址
圖示
C鏈表中的基本概念
1.頭結點--鏈表中的輔助結點,包含指向第一個數據元素的指針(方便插入和刪除)
2.數據結點--鏈表中代表數據元素的結點,表現形式為:(數據元素,地址)
3.尾節點--鏈表中的最后一個數據結點,包含的地址信息為空
代碼表示為
struct Node:public Object
{
T value;
Node* next;//指向后繼節點的指針
};
單鏈表的內部結構
頭結點在單鏈表中的意義是:輔助數據元素的定位,方便插入個刪除操作;因此,頭結點不存儲實際的數據元素
D插入與刪除的實現
a.插入數據元素
1.從頭結點開始,通過一個current指針定位到目標位置
2.從堆空間申請新得Node結點
3.執行操作
圖示
b.刪除操作
1.從頭結點開始,通過current指針定位到目標位置
2.使用toDel指針指向需要刪除得結點
3.執行操作
圖示
A.抽象類List的代碼如下
#include "Object.h"
namespace MyLib
{
//List抽象類
template <typename T>
class List:public Object
{
protected:
List(const List&);
List& operator=(const List&);//避免賦值操作
public:
List(){}
virtual bool insert(const T&e)=0;//鏈表的插入
virtual bool insert(int i,const T&e)=0;//重載版本
virtual bool remove(int i)=0;//鏈表的刪除
virtual bool set(int i,const T&e)=0;//
virtual int find(const T&e)const=0;
virtual bool get(int i,T&e)const=0;
virtual int length()const=0;
virtual void clear()=0;
};
}
B.LinkList設計要點
1.類模板,通過頭結點訪問后繼結點
2.定義內部結點類型,用于描述數據域和指針域
3.實現線性表的關鍵操作(增,刪,查,等)
LinkList的定義,代碼如下
template<typename T>
class LinkList:public List<T>
{
protected:
struct Node:public Object
{
T value;
Node* next;
};
Node m_header;
int m_length;
public:
LinkList();
.......
};
LinkList的實現
template<typename T>
class LinkList:public List<T>
{
protected:
struct Node:public Object
{
T value;
Node* next;
};
mutable Node m_header;
int m_length;
public:
LinkList()
{
m_header.next=NULL;
m_length=0;
}
bool insert(const T& e)
{
return insert(m_length,e);
}
bool insert(int i,const T& e)
{
bool ret=((0<=i)&&(i<=m_length));
if(ret)
{
Node* node=new Node();
if(node!=NULL)
{
Node* current=&m_header;
for(int p=0;p<i;p++)
{
current=current->next;
}
node->value=e;
node->next=current->next;
current->next=node;
m_length++;
}
else
{
THROW_EXCEPTION(NoEoughMemoryException,"No ...");
}
}
return ret;
}
bool remove(int i)
{
bool ret=((0<=i)&&(i<=m_length));
if(ret)
{
Node* current=&m_header;
for(int p=0;p<i;p++)
{
current=current->next;
}
Node* toDel=current->next;
current->next=toDel->next;
delete toDel;
m_length--;
}
return ret;
}
bool set(int i,const T&e)
{
bool ret=((0<=i)&&(i<=m_length));
if(ret)
{
Node* current=&m_header;
for(int p=0;p<i;p++)
{
current=current->next;
}
current->next->value=e;
}
return ret;
}
int find(const T&e) const
{
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;
}
virtual T get(int i)const
{
T ret;
if(get(i,ret))
{
return ret;
}
else
{
THROW_EXCEPTION(indexOutOfBoundsException,"...");
}
return ret;
}
bool get(int i,T&e)const
{
bool ret=((0<=i)&&(i<=m_length));
if(ret)
{
Node* current=&m_header;
for(int p=0;p<i;p++)
{
current=current->next;
}
e=current->next->value;
}
return ret;
}
int length()const
{
return m_length;
}
void clear()
{
while(m_header.next)
{
Node* toDel=m_header.next;
m_header.next=toDel->next;
delete toDel;
}
m_length=0;
}
~LinkList()
{
clear();
}
在編譯器的實現結果如圖所示
效率的深度分析:
a.插入和刪除
1.順序表:涉及大量數據對象的復制操作
2.單鏈表:只涉及指針操作,效率與數據對象無關
b.數據訪問
1.順序表:隨機訪問,可直接定位數據對象
2.單鏈表:順序訪問,必須從頭訪問數據對象,無法直接定位
工程開發中的選擇:
順序表:
1.數據元素的類型相對簡單,不涉及拷貝
2.數據元素相對穩定,訪問操作遠多于插入和刪除操作
單鏈表:
1.數據元素的類型相對復雜,復制操作相對耗時
2.數據元素不穩定,需要經常插入和刪除,訪問操作較少
總結:
1.線性表中元素的查找依賴于相等比較操作符
2.順序表適用于訪問需求量較大的場合(隨機訪問)
3.單鏈表適用于數據元素頻繁插入刪除的場合(順序訪問)
4.當數據類型相對簡單時,順序表和單鏈表的效率不相上下
a.代碼的優化
在單鏈表的實現中有代碼的重復
mutable struct:public Object//沒有類型名的結構
{
char reserved[sizeof(T)];
Node* next;
} m_header;//頭節點 輔助定位元素
Node* position(int i) const//程序優化
{
Node* ret=reinterpret_cast<Node*(&m_header);//reinterpret_cast強制類型轉換
for(int p=0;p<i;p++)
{
ret=ret->next;
}
return ret;
}
Node* create()
{
return new Node();
}
void destroy(Node* pn)
{
delete pn;
}
插入部分的修改如圖所示
b.單鏈表的遍歷設計思路
當前實現的單鏈表類不能以線性的時間復雜度完成單鏈表的遍歷,所以需要重新設計一種思路
1.在單鏈表的內部定義一個游標(Node* m_current)
2.遍歷開始前將游標指向位置為0的數據元素
3.獲取游標指向的數據元素
4.通過結點中的next指針移動游標
c.遍歷函數原型設計
bool move(int i,int step=1);//step每次結點的移動
bool end();
T current();
bool next();
代碼實現如下
/*遍歷函數的實現*/
virtual bool move(int i,int step=1)
{
bool ret= (0<=i)&&(i<m_length)&&(step>0);
if(ret)
{
m_current=position(i)->next;
m_step=step;
}
return ret;
}
virtual bool end()
{
return (m_current==NULL);
}
virtual T current()
{
if(!end())
{
return m_current->value;
}
else
{
THROW_EXCEPTION(InvalidOperationException,"...");
}
}
virtual bool next()
{
int i=0;
while((i<m_step)&& (!end()))
{
m_current=m_current->next;
i++;
}
return (i==m_step);
}
最終的實現如下圖所示
小結:
1.單鏈表的遍歷需要在線性時間內完成
2.在單鏈表內部定義游標變量,通過游標遍歷提高效率
3.遍歷相關的成員函數是相互依賴,相互配合的關系
4.封裝結點的申請和刪除操作更有利于增強擴展性
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。