1、 概论
级数
算数级数:与末项平方同阶
T(n)=1+2+.....+n=n(n+1)/2=O(n^2)
幂方级数:比幂次高出一阶
T2(n)=1^2+2^2+......+n^2=O(n^3)
几何级数:与末项同阶
Ta(n)=a^0+a^1+a^3+.....+a^n=O(a……n)
收敛级数:O(1)
1/3+1/7+1/8+.....+....=1=O(1)
未必收敛,但是长度有限
调和级数:h(n)=1+1/2+1/3+......+1/n=O(logn)
对数级数:log1+log2+log3+.....+logn=log(n!)=O(nlogn)
迭代和递归
减而治之
分而治之
(f)动态规划
2、向量vector
(a)接口和实现
size() //返回元素总数
get(r) //获取标号为r的元素
put(r,e) //用e替代标号为r的数值
insert(r,e) //在r位置插入元素e,原后续元素后移
remove(r) //删除r位置的元素,返回该元素存放的对象
disordered() //判断所有元素是否按照非降序排列
sort() //非降序排序
find(e) //寻找目标元素e
search(e)
dedupicate()
uniquify()
traverse()
typedef 数据类型 新的名称
#define 新的名称 具体内容
vector模板类
typedef int Rank; //秩
#define DEFAULT_CAPACITY 3 //默认初始容量(实际可以设置较大)
template <typename T> class Vector{//向量模板类
private:Rank_size;int_capacity;T*_elem;
protected:
//内部函数
public:
//构造函数
//析构函数
//只读接口
//可写接口
//遍历函数
};
析构
typedef int Rank; //秩
#define DEFAULT_CAPACITY 3 //默认初始容量(实际可以设置较大)
template <typename T> class Vector{//向量模板类
private:Rank_size;int_capacity;T*_elem;
protected:
//内部函数
public:
//构造函数
//析构函数
//只读接口
//可写接口
//遍历函数
};
析构和构造
Vector(int c=DEFAULT_CAPACITY) //默认
{
_elem=new T[_capacity=c];
_size=0;
}
Vector(T const *A,Rank lo,Rank hi) //数组区间复制
{
copyFrom(A,lo,hi);
}
Vector(Vector<T> const& V,Rank lo,Rank hi)//向量区间复制
{
copyFrom(V._elem,lo,hi);
}
Vector(Vector<T> const& V) //向量整体复制
{
copyFrom(V._elem,0,V.size())
}
~Vector(){delete[]_elem;} //释放内部空间
基于复制的构造
template <typename T>//T为基本类型
void Vector<T>::copyFrom(T* const A,Rank lo,Rank hi){
_elem=new T[_capacity=2*(hi-lo)]; //分配空间,两倍是预留空间,实际只用了一倍
_size=0; //规模清零
while(lo<hi)
_elem[_size++]=A[lo++];
}
(b)可扩充向量
_capacity:总容量
_size:当前的实际规模
若采用静态空间管理策略,容量_capacity固定,则有明显的不足
1、上溢
2、下溢
扩充算法实现
template <typename T>
void vector<T>::expand(){//向量空间不足扩充
if(_size<_capacity) reurn;
_capacity=max(_capacity,DEFAULT_CAPACITY);//不低于最小容量
T* oldElem=_elem;_elem=new T[_capacity<<=1]; // _capacity<<=1容量加倍
for(int i=0;i<_size();i++) //复制内容
{
_elem[i]=oldElem[i]; //T为基本类型,或已经重载操作符'='
}
delete [] oldElem; //释放原空间
}
扩充策略对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P1AOYowZ-1606376285860)(C:\Users\DELL\Desktop\01.PNG)]
平均分析 和 平摊分析
©无序向量
元素访问:
template <typename T> //0<= r< _size
T & Vector<T>::operator[](Rank r)
{
return _elem[r];
}
插入
template <typename T> //e为秩为r的元素插入,0<=r<_size
Rank Vector<T>::insert(Rank r,T const & e) //O(n-r)
{
expand(); //若有必要,扩充
for(int i=_size;i>r;i--)
{
_elem[i]=_elem[i-1];
}
_elem[r]=e;
_size++;
reutrn r;
}
区间删除
template <typename T> //删除区间[lo,hi),0<=lo<=size
int Vector<T>::remove(Rank lo,Rank hi)
{
if(lo==hi) return 0;
while(hi<_size) _elem[lo++]=_elem[hi++];
_size=lo; shrink();
return hi-lo;
}
查找
template <typename T> //0<= lo< hi<=_size
Rank Vector<T>::find(T & const e,Rank lo,Rank hi) const
{
while(lo<hi-- && e!=_elem[hi]); //逆向查找
return hi;
}
单元素删除
可以视作区间删除操作的体例:[r]=[r,r+1)
template <typename T>
T Vector<T>::remove(r)
{
T e=_elem[r]; //备份删除的数据
remove(r,r+1); //调用区间删除算法
return e; //返回删除元素
}
唯一化
template <typename T> //删除重复元素,返回删除元素的个数
int vector<T>::depulicate(){
int oldsize=_size; //保存原大小
Rank i=1;
while(i<_size)
{
(find(_elem[i],0,i)<0)?i++:remove(i);
}
return oldsize-_size;
}
遍历
//利用函数指针,只读和局域性修改
template <typename T>
void Vector<T>::traverse(void (*visit)(T&)) //函数指针
{
for(int i=0;i<_size;i++)
visit(_elem[i]);
}
//利用函数对象机制,可全局性修改
template <typename T>
template <typename VST>
void vector<T>::traverse(VST& visit) //函数对象
{
for(int i=0;i<_size;i++)
visit(_elem[i]);
}
(d1)有序向量:唯一化
有序性及其甄别
template <typename T>
int Vector<T>::disoredered() const
{
int n=0; //计数器
for(int i=1;i<_size();i++) //逐一检查
n+=(_elem[i-1]>_elem[i]);
return n;
}
无序向量经过预处理为有序向量后,相关算法可以优化
唯一化(低效算法)
template <typename T>
int Vector<T>::uniquify()
{
int oldsize=_size;
int i=0;
//若雷同,则删除后者
while(i<_size-1)
(_elem[i]==_elem[i+1])?remove(i+1):i++;
return oldsize-_size;
}
唯一化(高效)
template <typename T>
int Vector<T>::uniquify()
{
Rank i=0,j=0;
while(++j<_size)
if(_elem[i]!=_elem[j])
_elem[++i]=_elem[j];
_size=++i;shrik();
return j-i;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8oxE7VCP-1606376285866)(C:\Users\DELL\Desktop\02.PNG)]
(d2)有序向量:二分查找
template <tyename T>
Rank Vector<T>::(T const & e,Rank lo,Rank hi)const
{
return (rand()%2==0)? //随机选50%
binsearch(_elem,e,lo,hi) //二分查找
:fibsearch(_elem,e,lo,hi); //Fibonacci查找算法
}
版本A:实现
template <typename T> //在有序向量区间[lo,hi)内查找
static Rank binSearcg(T* A,T const& e,Rank lo,Rank hi){
while(lo<hi)
Rank mi=(lo+hi)>>1; //>>就是除以二
if(e<A[mi])
hi=mi;
else if(A[mi]<)
lo=mi;
else
return mi;
return -1;
}
(d3)有序向量:Fibonacci查找
改进思路:判断的时候,往左是一次判断;往右是两次判断;目的是使左右两边的判断次数总和接近。
template <typename T>
static Rank fibSearch(T* A,T const& e,Rank lo,Rank hi){
Fib fib(hi-lo);
while(lo<hi){
while(hi-lo<fib.get())
fib.prev();
Rank mi=lo+fib.get()-1;
if(e<A[mi])
hi=mi;
else if(A[mi]<e)
lo=mi;
else
return mi;
}
return -1
}
总结:两种算法大体是一样的,主要是取mi的不同,二分法是取中间,Fibonacci查找是取黄金分割比。一个是0.5,一个是0.618…
(d4)有序向量:二分查找(改进)
改进思路:每次迭代只做一次的关键码比较
版本B:
template <typename> static binSearch(T*A,T const &e,Rank lo,Rank hi){
while(1<hi-lo)
{
mi=(lo+hi)>>1;
(e<A[mi])?hi=mi:lo=mi; //[lo,mi)或[mi,hi)
}
return (e==A[lo])?lo:-1;
}
版本C:
template <typename> static binSearch(T*A,T const &e,Rank lo,Rank hi){
while(hi<lo)
{
mi=(lo+hi)>>1;
(e<A[mi])?hi=mi:lo=mi+1; //[lo,mi)或(mi,hi)
}
return --lo;
}
(e)起泡排序
template <typename T>
void Vector<T>::bubbleSort(Rank lo,Rank hi)
{
while(!bubble(lo,hi--));
}
改进:
template <typename T>
bool Vector<T>::bubble(Rank lo,Rank hi)
{
bool sorted=true;
while(++lo<hi)
if(_elem[lo-1]>_elem[lo]){
sort=false;
swap(_elem[lo-1],_elem[lo]);
}
return sorted;
}
再改进:
template <typename T>
void Vector<T>::bubbleSort(Rank lo,Rank hi)
{
while (lo<(hi=bubble(lo,hi)));
}
template <typename T>
Rank Vector<T>::bubble(Rank lo,Rank hi)
{
Rank last=lo;
while(++lo<hi)
if(_elem[lo-1]>_elem[lo])
{
last=lo;
swap(_elem[lo-1],_elem[lo]);
}
return last;
}
(f)归并排序
template <typename T>
void Vector<T>::mergeSort(Rank lo,Rank hi){
if(hi-lo<2) return;
int mi=(lo+hi)>>1;
mergeSort(lo,mi);
mergeSort(mi,hi);
merge(lo,mi,hi);
}
二路归并:基本实现
template <typename T>
void Vector<T>::merge(Rank lo,Rank mi,Rank hi)
{
T*A=_elem+lo; //位置移动到lo
int lb=mi-lo;
T*B=new T[lb]; //复制B的缓存
for(Rank i=0;i<lb;B[i]=A[i++]);
int lc=hi-mi;
T*C=_elem+mi;
for(Rank i=0,j=0,k=0;(j<lb)||(k<lc);)
{
if((j<lb)&&(lc<=k||(B[j]<C[k]))) A[i++]=B[i++];
if((k<lc)&&(j<=lb||(c[k]<B[j))) A[i++]=C[K++];
}
delete []B;
}
3、列表List
接口与实现
动态与静态
*根据是否修改数据结构,所有操作大致分为两类
1)静态:仅读取,数据结构的内容及组成一般不变:get\search
2)动态:需写入,数据结构的局部或者整体将改变:insert\remove
*与操作方式相对应的,数据元素的存储和组织方式也分两种
1)静态:数据空间整体创建或者销毁
数据元素的物理存储次序与其逻辑次序严格一致
可支持高效的静态操作
比如向量Vector,元素的物理地址与其逻辑次序线性对应
2)动态:为各数据元素动态分地分配和回收的物理空间
逻辑上相邻的元素记录彼此的物理地址,在逻辑上形成一个整体
可支持高效的动态操作
列表节点:ADT接口
操作 | 功能 |
---|---|
pred() | 当前节点前驱节点的位置 |
succ() | 当前节点后继节点的位置 |
data() | 当前节点所存数据对象 |
insertAsPred(e) | 插入前驱节点,存入被引用对象e,返回新节点位置 |
insertAsSucc(e) | 插入后继节点,存入被引用对象e,返回新节点位置 |
列表节点:ListNode模板类
#define Posi(T) ListNode<T>* //列表节点位置
template <typename T>
struct ListNode{
T data; //数值
Posi(T) pred; //前缀
Posi(T) succ; //后继
ListNode(){} //针对header和trailer的构造
ListNode(T e,Posi(T),Posi(T) s=NULL):data(e),pred(p),succ(s){}
//默认构造器
Posi(T) insertAsPred(T const& e); //前插入
Posi(T) insertAsSucc(T const& e); //后插入
}
列表:ADT接口
操作接口 | 功能 | 适合对象 |
---|---|---|
size() | 报告列表当前的规模(节点总数) | 列表 |
first(),last() | 返回首、末节点的位置 | 列表 |
insertAsFirst(e),insertAsLast(e) | 将e当作首、末节点插入 | 列表 |
insertBefore(p,e),insertAfter(p,e) | 将e当作节点p的直接前驱、后继插入 | 列表 |
remove§ | 删除位置p处的节点,返回其引用 | 列表 |
disordered() | 判断所有节点是否已按非降序排列 | 列表 |
sort() | 调整各节点的位置,使之按非降序排列 | 列表 |
find(e) | 查看目标元素e,失败时返回NULL | 列表 |
search(e) | 查看e,返回不大于e且秩最大的节点 | 有序列表 |
deduplicate(),uniquify() | 剔除重复节点 | 列表/有序列表 |
traverse() | 遍历列表 | 列表 |
列表:List模板类
#include "ListNode.h" //引入列表节点
template <typename T>
class List{
private:
int _size; //规模
Posi(T) header;
Posi(T) trailer; //头尾哨兵
protected:
public:
}
构造
template <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; //记录规模
}
(b) 无序列表
秩到位置
template <typename T>
T List<T>::operator[](Rank r) const
{
Posi(T) p=first(); //从首节点出发
while (0<r--) p=p->succ; //顺序第r个节点即可
return p->data; //目标节点
}
查找
//在节点的n个前驱中,找到等于e的最后者
template <typename T>
Posi(T) List<T>::find(T const & e,int n, Posi(T) p) const
{
while(0<n--)
if(e==(p=p->pred)->data) return p;
return NULL;
}
插入
template <typename 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;
}
删除
template <typename T> //删除合法位置p处的节点,返回其数值
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;
}
唯一化
template <typename T>
int List<T>::deduplicate()
{
if(_size<2) return 0;
int oldsize=_size;
Posi(T) p==first(); Rank r=1;
while(trailer !=(p=p->succ))
{
Posi(T) q=find(p->data,r,p);
q?remove(q):r++;
}
return oldsize-_size;
}
©有序列表
唯一化
template <typename T>
int List<T>::uniquify()
{
if(_size<2) return 0;
int lodsize=_size;
ListNodePosi(T) p=first();
ListNodePosi(T) q;
while(trailer != (q=p->succ))
if(p->data!=q->data) p=q;
else remove(q);
return oldsize-_size;
}
查找
template <typename T>
Posi(T) List<T>::search(T const & e,int n,Posi(T) p) const
{
while(0<=n--)
if(((p=p->pred)->data)<=e) break;
return p;
}
(d)选择排序
实现:selectionSort()
template <typename T>
void List<T>::selectionSort(Posi(T)p,int n)
{
Posi(T) head=p->pred;
Posi(T) tail=p;
for(int i=0;i<n;i++) tail=tail->succ;
while(1<n)
{
insertBefore(tail,remove(selectMax(head->succ,n)));
tail=tail->pred;n--;
}
}
new,delete操作为普通的100倍时间
实现:selectMax()
template <typename T>
Posi(T) List<T>::selectMax(Posi(T) p,int n)
{
Posi(T) max=p;
for(Posi(T) cur=p;1<n;n--)
if(!lt((cur=cur->succ)->data,max->data)) //!lt(,)判断大于等于
max=cur;
return max;
}
(e)插入排序
实现
template <typename T>
void List<T>::insertionSort(Posi(T) p,int n)
{
for(int r=0;r<n;r++)
insertAfter(search(p->data,r,p),p->data);
p=p->succ;remove(p->pred);
}
4、栈与队列
(a)栈接口与实现
5、二叉树
(a)树
(b)树的表示
接口:
节点 | 功能 |
---|---|
root() | 根节点 |
parent() | 父节点 |
firstChild() | 长子 |
nextSibling | 兄弟 |
insert(i,e) | 将e作为第i个孩子插入 |
remove() | 删除第i个孩子(及其后代) |
traverse() | 遍历 |
©二叉树概述
(d)二叉树实现
BinNode模板类:
#define BinNodePosi(T) BinNode<T>* //节点位置
template <typename T>
struct BinNode{
BinNodePosi(T) parent,lChild,rChild; //父亲,孩子
T data;
int height; //高度
int size(); //子树规模
BinNodePosi(T) insertAsLC( T const &); //作为左孩子插入新节点
BinNodePosi(T) insertAsRC( T const &); //作为右孩子插入新节点
BinNodePosi(T) succ(); //(中序遍历意义下)当前节点的直接后继
template <typename VST> void travLevel(VST &);//子树层次遍历
template <typename VST> void travPre(VST &);//子树先序遍历
template <typename VST> void travIn(VST &);//子树中序遍历
template <typename VST> void travPost(VST &);//子树后序遍历
}
BinNode接口实现
template <typename T>
BinNodePosi(T)::insertAsLC(T const & e)
{
return lChild=new BinNode(e,this);
}
template <typename T>
BinNodePosi(T)::insertAsRC(T const & e)
{
return rChild=new BinNode(e,this);
}
template <typename T>
int BinNode<T>::size(){//后代总数,以此为根的子树规模
int s=1;//记入本身
if(lChild) s+=lChild->size(); //递归计入左子树规模
if(rChild) s+=rChild->size(); //递归计入右子树规模
return s;
}
BinTree模板类
template <typename T>
class BinTree{
protected:
int _size(); //规模
BinNodePosi(T) _root; //根节点
virtual int updateHeight(BinNodeposi(T) x); //更新节点x的高度
void updateHeightAbove(BinNodePosi(T) x); //更新x及祖先的高度
public:
int size() const {return _size;} //规模
bool empty() const {return !_root;} //判空
BinNodeposi(T) root() const {return _root;} //树根
/*.......其他接口.......*/
}
高度更新
#define stature(p) ((p)?(p)->height:-1) /节点高度,约定空树高度为-1
template <typename T> //更新节点X的高度,具体规则因树不同而异
int Bintree<T>::updateHeight(BinNodePosi(T) x){
return x->height =1+=max(stature(x->lChild),stature(x->rChild));
}
template <typename T> //更新v以及历代祖先的高度
void BinTree<T>::updateHeightAbove(BinNode(T) x){
while(x)
{updateHeight(x);x=x->parent;}
}
节点插入
template <typename T>
BinNodePosi(T) BinTree<T>::insertAsRC(BinNodePosi(e),T const & e)
{
_size++;
x->insertAsRC(e); //x的祖先高度可能增加,其余节点必然不变
updateHeightAbove(x);
return x->rChild;
}
(e1)先序遍历
遍历:
先序,中序,后序
先序:递归实现
template <typename T,typename VST>
void traverse(BinNodePosi(T) x,VST & visit){
if(!x) return;
visit(x->data);
traverse(x->lChild,visit);
traverse(x->rChild,visit);
}//T(n)=O(1)+T(a)+T(n-a-1)=O(n)
迭代1:实现
template <typename T,typename VST>
void travPre_I1(BinNodePosi(T) x,VSY & visit){
Stack <BinNodePosi(T)> S;
if(x) S.push(x); //根节点
while(!S.empty()){ //在栈变空之前反复循环
x=S.pop(); visit(x->data); //弹出并访问当前节点
if(HasRChild(*x)) S.push(x->rChild); //右孩子先入后出
if(HasLChild(*x)) S.push(x->lChild);
//左孩子后入先出
}
}
迭代2:实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zO61PSXW-1606376285869)(E:\360MoveData\Users\Administrator\Desktop\数据结构和算法.assets\捕获1.PNG)]
template <typename T,typename VST> //分摊O(1)
static void visitAlongLeftBrancg(
BinNodePosi(T) x,
VST & visit,
Stack <BinNodePosi(T) & S)
{
while(x){ //反复地
visit(x->data); //访问当前节点
S.push(x->rChild); //右孩子(右子树)入栈(将来逆序出栈)
x=x->lChild; //沿左侧链下行
}//只有右孩子、NULL可能入栈---增加判断以删除是否值得
}
template <typename T,typename VST>
void travPre_I2(BinNodePosi(T) x,VST & visit)
{
Stack <BinNodePosi(T)> S;
while(true){
visitAlongLeft(x,visit,S); //访问子树x的左侧链,右子树入栈缓冲
if(s.empty()) break; //栈空即退出
x=S.pop(); //弹出下一个子树的根
}
}
(e2)中序遍历
递归实现
template <typename T,typename VST>
void traverse(BinNodePosi(T) x,VST & visit){
if(!x) return;
traverse(x->lChild,visit);
visit(x->data);
traverse(x->rChild,visit);
}
改进实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qF7Qy1nO-1606376285873)(E:\360MoveData\Users\Administrator\Desktop\数据结构和算法.assets\捕获2.PNG)]
template <typename T>
static void goAlongLeftBranch(BinNodePosi(T) x,Stack <BinNodePosi(T)> & S)
{
while(x) {S.push(x);x=x->lChild;}
//反复地入栈,沿左分支深入
}
template <typename T,typename V>
void travIn_I1(BinNodePosi(T) x,V & visit)
{
Stack <BinNodePosi(T)> S;
while(true){
goAlongLeftBranch(x,S); //当前节点出发,逐批入栈
if(S.empty()) break; //
x=S.pop(); //x的左子树或为空,或已遍历
visit(x->data);
x=x->rChild; //转向右子树
}
}
(e4)层次遍历
实现
template <typename T>
template <typename VST>
void BinNode<T>::travLevel(VST & visit){//二叉树层次遍历
Queue<BinNodePosi(T)> Q; //引入辅助队列
Q.enqueue(this); //根节点入栈
while(!Q.empty()){ //队列为空之前,反复迭代
BinNodePosi(T) x=Q.dequeue(); //取出队首节点,并随机
visit(x->data); //访问之
if(HasLChild(*X)) Q.enqueue(x->lChild); //左孩子入队
if(HasRChild(*X)) Q.enqueue(x->rChild); //右孩子入队
}
}
(e5)
先序+后序(真树)
(先序/后序)+中序