线性结构——列表

接口和实现

从静态到动态
根据是否修改数据结构,操作大致分为两类:
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模型对应循秩访问方式
图灵机模型对应循位置访问

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