数据结构2 - 列表


一、基本概念

列表的核心是一个双向链表,双向开口,可在头、尾两个方向进行元素的插入和删除

  • Vector是连续的容器,而List是非连续的容器,即Vector将元素存储在连续的容器中,而List存储在不连续的容器中
  • Vector的插入和删除是比较麻烦的,需要大量的时间来移动元素,而链表克服了这个问题
  • 在List中遍历速度很慢,因为List元素是按顺序访问的,而Vector支持随机访问。
二、接口与实现
1、接口
操作接口功能
pred()当前节点前驱节点的位置
succ()当前节点后继节点的位置
data()当前节点所存数据对象
insertAsPred(e)插入前驱结点,存入被引用对象e,返回新节点位置
insertAsSucc(e)插入后继节点,存入被引用对象e,返回新节点位置
2、实现
  • 查找
template <typename T> //在无序列表内节点p(可能是trailer)的n个(真)前驱中,找到等于e的最后者
ListNodePosi(T) List<T>::find ( T const& e, int n, ListNodePosi(T) p ) const {
    while ( 0 < n-- ) //(0 <= n <= rank(p) < _size)对于p的最近的n个前驱,从右向左
		if ( e == ( p = p->pred )->data ) return p; //逐个比对,直至命中或范围越界
			return NULL; //p越出左边界意味着区间内不含e,查找失败
} //失败时,返回NULL
  • 插入
template <typename T> ListNodePosi(T) List<T>::insertAsFirst ( T const& e )
{ _size++; return header->insertAsSucc ( e ); } //e当做首节点插入

template <typename T> ListNodePosi(T) List<T>::insertAsLast ( T const& e )
{ _size++; return trailer->insertAsPred ( e ); } //e当做末节点插入

template <typename T> ListNodePosi(T) List<T>::insertA ( ListNodePosi(T) p, T const& e )
{ _size++; return p->insertAsSucc ( e ); } //e当做p的后继插入(After)

template <typename T> ListNodePosi(T) List<T>::insertB ( ListNodePosi(T) p, T const& e )
{ _size++; return p->insertAsPred ( e ); } //e当做p的前驱插入(Before)
  • 删除
template <typename T> T List<T>::remove ( ListNodePosi(T) p ) { //删除合法节点p,返回其数值
	T e = p->data; //备份待删除节点的数值(假定T类型可直接赋值)
	p->pred->succ = p->succ; p->succ->pred = p->pred; //后继、前驱
	delete p; _size--; //释放节点,更新规模
	return e; //返回备份的数值
}
  • 唯一化
template <typename T> int List<T>::deduplicate() { //剔除无序列表中癿重复节点
	if ( _size < 2 ) return 0; //平凡列表自然无重复
	int oldSize = _size; //记录原规模
	ListNodePosi(T) p = header; Rank r = 0; //p从首节点开始
	while ( trailer != ( p = p->succ ) ) { //依次直到末节点
		ListNodePosi(T) q = find ( p->data, r, p ); //在p的r个(真)前驱中查找雷同者
		q ? remove ( q ) : r++; //若的确存在,则删除;否则秩加一
	} //assert: 循环过程中的任意时刻,p的所有前驱互不相同
	return oldSize - _size; //列表规模变化量,即被删除元素总数
}
三、排序
1、插入排序

始终将整个序列视为并切分为两部分:有序的前缀,无序的后缀;通过迭代,反复地将后缀的首元素转移至前缀中。

template <typename T> //列表的插入排序算法:对起始于位置p的n个元素排序
void List<T>::insertionSort ( ListNodePosi(T) p, int n ) { //valid(p) && rank(p) + n <= size
	for ( int r = 0; r < n; r++ ) { //逐一遍历各节点
		insertA ( search ( p->data, r, p ), p->data ); //查找适当的位置并插入
		p = p->succ; remove ( p->pred ); //转向下一节点
	}
}
/*
	为什么不用二分查找?
		因为不是数组或向量,不能用二分查找。。。(为什么会有学生问这个问题)
*/
2、选择排序

与插入排序类似,该算法也将序列划分为无序前缀和有序后缀两部分:此外,还要求前缀不大于后缀。如此,每次只需从前缀中选出最大者,并作为最小元素转移至后缀中,即可使有序部分的范围不断扩张。

template <typename T> //列表的选择排序算法:对起始于位置p的n个元素排序
void List<T>::selectionSort ( ListNodePosi(T) p, int n ) { //valid(p) && rank(p) + n <= size
	ListNodePosi(T) head = p->pred; ListNodePosi(T) tail = p;
	for ( int i = 0; i < n; i++ ) tail = tail->succ; //待排序区间为(head, tail)
	while ( 1 < n ) { //在至少还剩两个节点之前,在待排序区间内
		ListNodePosi(T) max = selectMax ( head->succ, n ); //找出最大者(歧义时后者优先)
		insertB ( tail, remove ( max ) ); //将其移至无序区间末尾(作为有序区间新的首元素)
		tail = tail->pred; n--;
    }
}
3、归并排序

和向量的归并排序几乎一致

4、游标实现

利用线性数组,以游标方式模拟列表

-elem[]: 对外可见的数据项

-link[]: 数据项之间的引用

link[]中存的是下一个数据在elem[]的下标(相当于succ、next)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值