線性結構——列表

接口和實現

從靜態到動態
根據是否修改數據結構,操作大致分爲兩類:
1)靜態:讀取,數據結構的內容和組成一般不變:get, search
2) 動態:寫入,數據結構的局部或者整體改變: insert,remove
與操作方式相對應,數據元素的存儲與組織方式也分爲兩種
1) 靜態:數據空間整體創建或銷燬
數據元素的物理儲存次序和邏輯次序嚴格一致,因此支持高效的靜態操作 (比如向量) getO(1),search O(logn);而寫入最少要O(n),爲了改變動態操作不足 採用動態的結構(列表)
2) 動態: 爲各數據元素動態分配和回收物理空間
邏輯上相鄰的元素記錄彼此的物理空間,在邏輯上形成一個整體,支持高效的動態操作

從向量到列表
L={a,b,c}
列表(List)是典型的動態儲存結構,其中各元素稱爲節點, 各節點通過指針或者引用彼此連接,在邏輯上構成線性序列,除了首末元素外均有前驅後繼

從秩到位置
向量支持循秩訪問 ,V[i]=V+i*s, 如此快的訪問 能否被列表沿用?
列表也是線性結構,的確可以通過秩來訪問,但是循序漸進的 訪問成本高
因此,改爲循位置訪問 即利用節點間的相互引用,找到特定節點

實現
節點作爲列表的基本元素,需要首先實現封裝

**列表節點:ListNode模板類**
#define Posi(T) ListNode<T>*//定義類型
template <typename>//簡潔起見,完全開放而不過度封裝
struct ListNode		//列表節點模板類(雙向鏈表形式實現)
{
	T date;//數值
	Posi(T) pred;//前驅
	Posi(T) succ;//後繼
	ListNode () {}//針對header和trailer的構造
	ListNode(T e,Posi(T) p= NULL,Posi(T) s=NULL)
		:date(e),pred(p),succ(s){}//默認構造器
	Posi(T) insertAsPred(T const& e);//前插入
	Posi(T) insertAsSucc(T const& e);//前插入
};

**列表:List模板類**
include "listNode.h"//引入列表節點類
template <typename T>class List
{
	private: int _size;//規模
		Posi(T) header;Poai(T) trailer://頭,尾哨兵
	protected:   //內部函數
	public:     		//構造函數 析構函數 只讀函數 可寫接口 便利節後
};
**構造**
temolate <typename T> void List<T>::init()//初始化,創建列表時統一調用
{
	header=new ListNode<T>;//創建頭哨兵節點
	trailer =new ListNode<T>;//創建尾哨兵節點
	header->succ =trailer; header->pred=NULL;//互聯
	trailer->pred =header;trailer->succ=NULL;
	_size=0;
}

無序列表

秩到位置
可否模仿向量的循秩訪問方式?

**重載下標操作符**
template <typename>
T List<T>::operator[](Rank r )const//對列表重載操作符[],訪問第r個元素
{
	Posi(T)p=first();//從首節點出發
	while(0<r--) p=p->succ;//順數找到第r個節點
	return p->date;//目標節點
}//節點的秩即前驅總數

**複雜度**:算術級數 每個節點訪問概率是1/n,所以複雜度爲O(n)

查找
在p節點的前驅中尋找e,注意對比,命中則停止

template <typename T>//從外部調用時,0<=n<=rank(p)<_size
Posi(T) List<T>::find(T const& e,int n,Posi(T) p) const
{
	while(0<n--)
		if(e==(p=p->pred)->date) return p;//直至命中或範圍越界
	return NILL;
}

插入與複製

template <typemlate T> Posi(T) List<T>::insertBefore(Posi (T) p,T const & e)
{_size++;return p->insertAsPred(e);}//e作爲p的前驅插入
template <typename T> //前插入算法
Posi(T) ListNode<T>::insertAsPred(T const& e)
{
	Posi(T)x=new ListNode(e,pred,this);//創建新節點
	pred->succ =x;pred=x;return x;//建立鏈接返回新結點,並對前後節點做調整
}//哨兵的建立使p即使是首節點仍合理

基於複製的構造

template <typename T>//基本接口 O(n)
void List <T>::copyNodes(Posi<T> p,int n)
{
	init();//創建哨兵並初始化
	while(n--)//將自p節點的n項依次作爲末節點插入
	{insertAsLast(p->data);p=p->succ;}//insertAsLast相當於insertBefore(trailer)!!!!!!!!!!!!!!
}

刪除與析構

template <typename T>//刪除合法位置p節點,返回其數值 O(1)
T List<T>::remove (Posi(T))
{
	T e=p->data;
	p->pred->succ=p->succ;
	p->succ->pred=p->pred;
	deleta p; _size--; return e;
}

析構把對外可見的節點刪除,再刪除哨兵

template <typename T>List <T>::~List()//列表析構
{clear ();deleta header;delete trailer;}//清空列表,釋放頭尾哨兵
template <typename T>int List <T>::clear()
{
	int ols_size=_size;
	while(o<_size)//反覆刪除節點
		 remove(header->succ);
	return oldSize;
}//O(n),線性正比於列表規模

唯一化
大致分爲三部分:一部分不重複節點,待考察節點,待考察後綴

{
	if(_size<2) return 0;//排除平凡列表
	int olsSize =_size;//記錄原規模
	Posi(T) p=first();Rank r=1;//p從首節點起
	while(trailer !=(p=p->succ))
	{
		Posi(T) q=find(p->data,r,p);
		q?remove(q):r++;//存在則刪除,否則秩遞增(因爲有p->succ操作所以非remove(p))
	}
}

有序列表

唯一化 構思
有序向量唯一化比無序向量完成更快,那列表呢?
有序向量相同元素彼此相鄰,只保留一個元素,仿照此:p節點與後繼節點對比,相同則remove(q) ,繼續對比知道發現下一個不同節點 ,則將p指向並保留該不同節點
唯一化實現

temolate <typename T> int List <T>::uniquify()
{
	if(_size<2) return 0;
	int olsSize =_size;
	ListNodePosi(T) p=first(); ListNodePosi(T) q;//p爲各區段起點,
	while(trailer !=(q=p->succ))//q爲其後繼
		if (p->data!=q->data) p=q;//前後節點互異則轉向下一個
		else remove(q);//相同則刪除
	return oldSize -_size;//返回規模變化量
}//只需遍歷一遍,O(n)

查找

template <typename T>//在有序列表節點p的n個前驅中,返回不大於e的最後者
Posi (T) List<T>::search(T const &e,int n,Posi(T) p) const
{
	while(0<=n--)//對於p最近的n個節點,從左到右
		if(((p=p->pred)->data)<=e) break;//逐個比較
	return p;//直至命中或範圍越界
}//

Vector rank(秩): List posi(位置)
Ram模型對應循秩訪問方式
圖靈機模型對應循位置訪問

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章