《数据结构与算法(DSACPP)》学习笔记(三):向量和列表

目录

    • 抽象数据类型
    • 向量
    • 接口
    • 实现
      • 1. 构造析构
      • 2. 空间管理
      • 3. 静态操作
      • 4. 动态操作
    • 列表
    • 列表节点
    • 接口
    • 实现
      • 1. 构造析构
      • 2. 静态操作
      • 3. 动态操作

抽象数据类型

抽象数据类型(ADT, Abstract Data Type):数据模型 + 相关操作。
数据机构(DS, Data Structure):实现ADT的一套算法。

抽象数据类型内部使用数据结构 实现(implementation),封装为统一的 接口(interface) 函数,提供外部用户接口手册以 应用(application) 于实际问题。

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第1张图片
接口操作可分为 静态操作动态操作:静态操作仅读取,数据结构内容和组成一般不变,如[]find();动态操作需写入,数据结构的局部或整体将改变,insert()remove()

数据存储和组织方式也分为 静态存储动态存储:静态存储的向量,整体创建与销毁数据空间,逻辑和物理存储均连续,支持高效静态操作;动态存储的列表,动态地为数据元素分配和回收物理空间,逻辑连续、物理可不连续,支持高效动态操作。

向量

数组:有限的元素序列。数组 A [ n ] A[n] A[n] = { a 0 , a 1 , . . . a i . . . a n − 1 a_0, a_1,...a_i...a_{n-1} a0,a1,...ai...an1},其中 a i − 1 a_{i-1} ai1 a i a_i ai 的前驱元素, a 0 a_0 a0 ~ a i − 1 a_{i-1} ai1 构成 a i a_i ai 前缀。
在这里插入图片描述
向量:向量基于数组实现,向量长度 _ s i z e size size 可动态变化,元素可定制为复杂的数据结构。数组元素编号和向量的秩等价,秩 ( R a n k ) (Rank) (Rank) 是向量元素的前驱个数,取值范围是 [ 0 , [0, [0, _ s i z e − 1 ] size-1] size1]

typedef int Rank;	// 秩

接口

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第2张图片

#define DEFAULT_CAPACITY 3 // 默认初始容量
template <typename T>
class Vector {
     			   // 向量模板类
	public:
		// 构造析构
		explicit Vector( int c = DEFAULT_CAPACITY );
		~Vector();
		
		// 静态操作
		T & Vector<T>::operator[](Rank r) const;	// []重载
		Rank Vector<T>::find(const T & e, Rank lo, Rank hi) const;	// 区间[lo,hi)查找e
		// 动态操作	
		Rank insert( Rank r, const T & e ); // e作为秩为r元素插入
		int remove( Rank lo, Rank hi );		// 删除区间[lo, hi)
		
	private:
		Rank _size;	 int _capacity;	// 规模、容量
		T* _elem;					// 数据区
};

实现

1. 构造析构

  • 构造函数
template <typename T>
Vector<T>::Vector( int c = DEFAULT_CAPACITY )
 {
      _elem = new T[ _capacity = c ];	_size = 0; }
  • 析构函数
template <typename T>
Vector<T>::~Vector() 
 {
     delete [] _elem; }	

2. 空间管理

静态空间管理 是开辟数组 _ e l e m [ ] elem[ ] elem[] 并使用一段地址连续的物理空间存储元素,其中 _ c a p a c i t y capacity capacity 表示总容量, _ s i z e size size 表示当前实际容量。

在这里插入图片描述
静态空间管理的容量 _ c a p a c i t y capacity capacity 固定,容易出现上溢、下溢等问题,同时实际开发时难以预测空间的需求量,因此引出空间的动态管理需求。

动态空间管理 比较当前的规模 _ s i z e size size 和容量 _ c a p a c i t y capacity capacity,动态地伸缩数组的容量。

  • 扩容
template <typename T> 
void Vector<T>::expand() {
     	
	if (_size < _capacity) return ;		// 尚未满,不扩容
	_capacity = max( _capacity, DEFAULT_CAPACITY );	// 保证不低于最小容量
	
	T* oldElem = _elem;					// 保存原向量
	_elem = new T[ _capacity << 1 ];	// 容量加倍
	for (int i = 0; i < _size; i++)		// 复制原向量内容
		_elem[i] = oldElem[i];		
	delete [] oldElem;					// 释放原向量
}
  • 收缩
template <typename T> 
void Vector<T>::shrink() {
     
	if ( _size << 2 > _capacity ) return ;				// 规模 < (容量/4) 开始收缩
	if ( _capacity < DEFAULT_CAPACITY <<= 1) return ;	//不致收缩到DEFAULT_CAPACITY以下

	T* oldElem = elem;					// 保存原向量
	_elem = new T[ _capacity >> 1 ];	// 容量减半
	for (int i = 0; i < _size; i++)		// 复制原向量内容
		_elem[i] = oldElem[i];			
	delete [] oldElem;					// 释放原向量
}

3. 静态操作

  • 按秩查找
template <typename T>
T & Vector<T>::operator[](Rank r) const {
     
	return _elem[r];
} 

结合模板和重载 [ ] [] [],可以实现下标读取和修改向量元素。以 V [ r ] V[r] V[r] 为例,返回值 T T T 使得 V [ r ] V[r] V[r] 可以作为右值给其他对象赋值,引用 & \& & 使 V [ r ] V[r] V[r] 可以作为左值被修改。

  • 按值查找

在这里插入图片描述

//  查找
template <typename T>
Rank Vector<T>::find(const T & e, Rank lo, Rank hi) const {
     
	while ((lo < hi--) && (e != _elem[hi]));	// 逆向查找
	return hi;			// hi < lo 表示查找失败
} 

4. 动态操作

  • 插入

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第3张图片

template <typename T>
Rank Vector<T>::insert( Rank r, const T & e ) {
     
	expand(); //空间已满,扩容
	for ( int i = _size; i > r; i-- ) _elem[i] = _elem[i-1]; //自后向前,后继元素顺次后移一个单元
	_elem[r] = e; _size++; //置入新元素并更新容量
	
	return r; //返回秩
}
  • 删除

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第4张图片

// 区间删除
template <typename T>
int Vector<T>::remove ( Rank lo, Rank hi ) {
     
	if ( lo == hi ) return 0;	
	while ( hi < _size ) _elem[lo++] = _elem[hi++];		// [hi, _size)顺次前移hi - lo个单元
	_size = lo; //更新规模,直接丢弃尾部[lo, _size = hi)区间
	shrink(); // 规模 < (容量/4) 开始收缩
	return hi - lo; //返回被删除元素的数目
}
// 删除单个元素
template <typename T>
T Vector<T>::remove( Rank r) {
     
	T e = _elem[r]; 	// 备份
	remove(r, r+1);		// 区间删除
	return e;			// 返回被删除元素
}

列表

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第5张图片
列表:采用动态存储策略,元素称为节点,各节点通过指引或引用连接,逻辑构成线性序列。相邻节点互称前驱和后继,前驱和后继存在则唯一,无前驱节点称为首节点,无后继节点称为末节点。

列表节点

在这里插入图片描述

// ListNode.h
#define Posi(T) ListNode*
template <typename T> 
struct ListNode {
     
	T data;
	Posi(T) pred;	// 前驱
	Posi(T) succ;	// 后继
	
	ListNode() {
     };	// 构造header和trailer
	ListNode(T e, Posi(T) p = NULL, Posi(T) s = NULL)
		: data{
     e}, pred{
     p}, succ{
     s} {
     };	// 默认构造器	
	
	Posi(T) insertAsPred(T const& e);	// 前插
	Posi(T) insertAsSucc(T const& e);	// 后插
};

接口

#include "ListNode.h" 		//引入列表节点类
template <typename T> 
class List {
      //列表模板类
	public:
		// 构造析构
		List();
		~List(); 
		
		// 静态操作
		T& operator[] ( Rank r ) const;	 	// 按秩查找
		Posi(T) find ( T const& e ) const;	// 按值查找
		
		// 动态操作
		Posi(T) insertA ( Posi(T) p, T const& e );	// e当作p的后继插入(After) 
		Posi(T) insertB ( Posi(T) p, T const& e );	// e当作p的前驱插入(Before)
		T remove ( Posi(T) p ); // 删除节点p
	
	private:
		int _size;	// 规模
		Posi(T) header;	 Posi(T) trailer;	// 哨兵 
};

实现

1. 构造析构

  • 构造函数

《数据结构与算法(DSACPP)》学习笔记(三):向量和列表_第6张图片

template <typename T> 
List<T>::List() {
     
	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>::~List() {
     
	while ( 0 < _size ) remove ( header->succ ); //反复删除首节点,直至列表变空
    delete header;	delete trailer;		// 释放头尾哨兵
}

2. 静态操作

  • 按秩查找
template <typename T>
T & List<T>::operator[](Rank r) const {
     
	Posi(T) p = first();			// 从首节点出发
	while (0 < r--) p = p->succ;	// 顺数第r个节点
	return p->data;
}
  • 按值查找
template <typename T>
Posi(T) List<T>::find(T const & e, int n, Posi(T) p) cosnt {
     
	while (0 < n--)		// 自后向前逐个比对
		if (e == ( p = p->pred )->data) 
			return p;	
	return NULL;		// 失败
}

3. 动态操作

  • 前插

在这里插入图片描述

template <typename T>
Posi(T) List<T>::insertBefore(Posi(T) p, T const &e) 
{
      _size++;	return p->insertAsPred(e);	}

template <typename T> 
Posi(T) ListNode<T>::insertAsPred ( T const& e ) {
      // this节点是p
	Posi(T) x = new ListNode ( e, pred, this ); // x->succ=p; 	x->pred=p->pred;
	pred->succ = x; pred = x; 					// p->pred->succ=x;		p->pred=x;
	return x; 						// 返回新节点的位置
}
  • 删除

在这里插入图片描述

template <typename T>
T List<T>::remove(Posi(T) p) {
     
	T e = p->data;
	p->pred->succ = p->succ;
	p->succ->pred = p->pred;
	delete p; _size--;	return e;
}

你可能感兴趣的:(数据结构,数据结构,向量,列表)