![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
template<class T> void SeqList<T>::reSize(int newsize) { if(newSize!=maxSize) { T *newarray=new T[newSize]; /* 开辟大小为newSize的内存空间 数据类型为T */ if(newarray==NULL) { cerr<<"分配存储失败!"<<endl; exit(1); } } int n=last+1; T *scrptr=data; T *destptr=newarray; while(n--) *destptr++=*srcptr++; delete []data; data=newarray; maxSize=newSize; } template<class T> int SeqList<T>::search(T &x) const { for(int i=0;i<=last;i++) { if(data[i]==x) return i+1; } } /* 在第i个位置插入x 插入前:a1,....,ai-1,ai,...,an 插入后:a1,...,ai-1,x,ai,...,an ai-1和ai之间的逻辑发生了变化 表满:last>=MaxSize-1 last是最后一个元素数组下标 1<=i<=last+2 插入元素时移动的越少效率越高 移动元素的个数不仅和表长有关而且与插入位置有关 最好情况(i=n+1)基本语句执行0次,时间复杂度O(1) */ /* 顺序表的实现——删除 每个往前移一位 相当于覆盖掉了 首先要判断删除位置是否合法 1<=i<=last+1 */ template<class T> bool SeqList<T>::Remove(int i,T &x) { if(last==-1||i<1||i>last+1) return false; x=data[i-1];//第i个元素数组下标是i-1 for(int j=i;j<=last;j++) { data[j-1]=data[j];//后面覆盖前面 } last--; //表长-1 } //顺序表的输入算法 template<class T> void SeqList<T>::input() { cout<<"输入元素个数"; while(1) { cin>>last; if(last<=maxSize-1) break; cout<<"个数输入有误"; } for(int i=0;i<=last;i++) { cin>data[i]; cout<<i+1<<endl; } } //顺序表的输出算法 template<class T> void SeqList<T>::output() { cout<<"当前元素的最后位置为"<<last<<endl; for(int i=0;i<=last;i++) { cout<<"#"<<i+1<<":"<<data[i]<<endl; } } //顺序表的应用 //并运算 void Union(SeqList<int>&A,SeqList<int>& B) { int n=A.length(),x; int m=B.length(); for(int i=1;i<=m;i++) { B.getData(i,x);//在B中取一个元素 int k=A.Search(x);//在A中搜索它 if(k==0) //若未找到插入到A的最后 把并的结果放入A中(若重新开空间则浪费) {A.Insert(n,x);n++;} } } //交运算 void Intersection(SeqList<int>&A,SeqList<int>&B) { int n=A.Length(); int m=B.Length();int i=1,x; while(i<=n) { A.getData(i,x); int k=B.search(x); if(k==0) A.Remove(i,x),n--; else i++; } } //顺序表的优点: /* 无需为表示表中元素之间的逻辑关系而增加额外的存储空间 随机存取 可以快速地存取表中任一位置的元素 顺序表的缺点: (1)插入和删除操作需要移动大量元素 (2)表的容量难以确定,表的容量难以扩充 (3)造成存储空间的碎片 链表非常适合频繁地插入或删除、存储空间需求变化很大地情形 */ /* 单链表是最简单地链表,也叫线性链表,它用指针表示节点间地逻辑关系 Node 存有数据域和指针域 线性结构first为头指针 结点可以连续可以不连续存储 99.99%不连续存储 节点的逻辑顺序和物理顺序可以不一致 表扩充很方便 */ /* 顺序表 容量是固定 单链表 不固定 */ /* 多个类表达一个概念(单链表) 链表节点(ListNode)类 链表(List)类 */ // 1.复合类1 class List;//链表类定义(复合方式) class LinkNode { friend class List;//链表类为其友元类 private: int data; LinkNode *link; }; class List { private: LinNode *first; }; //友元类不具有对称性?? //复合类2:用struct定义linkNode类 struct LinkNode { int data: LinkNode *link; }; class List { private: LinkNode *first; public: }; /* 虽然结构使List失去了封装性 但是所有属于LIst对象地LinkNode结点只能用first进行访问。 */ //2、嵌套类 class List { } //3、继承方式 class LinkNode { protected: int data; LinkNode *link; }; class List { } //写不完了 自己去看PPT吧。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 单链表的链接存储结构及实现 单链表: 头指针:指向第一个结点的地址 尾标志:终端节点的指针域为空 空表:first=NULL 非空表:构造一个头结点 插入: (1)第一个结点前插入: newnode->link=first; first=newnode; (2)在中间插入: 用p代表ai 先修改后继指针 newnode->link=p->link p->link=newnode (3)在链表末尾插入: newnode->link=p->link p->link=newnode 删除: (1)删除表中第一个元素 del=first; first=first->link;delete del ; (2)在单链表中或表尾 用p指向被删结点的前一个结点 del指向被删结点 p->link=del->link delete del; 头结点:为了统一操作,在表最前端设置附加头结点, 本身不带数据,仅标纸为表头 */ //附加头节点的单链表类 template<class T> struct LinkNode { T data; LinkNode<T> *link; LinkNode(LinkNode<T>*ptr=NULL) { link=ptr; }//构造函数 LinkNode(const T& item,LinkNode<T>*ptr) { // } } template<class T>//链表类 class List:public LinearList<T> { protected: LinkNode<T>*first;//头指针 public: List(){first=new LinkNode<T>;} List(const T&x) { first=new LinkNode<T>(x); } List(List<T>&L); ~List(){makeEmpty();} void MakeEmpty(); //??????? } LinkNode<T>*getHead()const{return first;} LinkNode<T>*Search(T x);//返回LinkNode型指针 //搜索含数据x的元素 LinkNode<T>*Locate(int i); //搜索第i个元素的地址 bool GetData(int i,T&x); //取出第i个元素的值 bool Insert(int i,T &x); //将x插在第i个元素后 bool Remove(int i,T &x); //删除第i个元素,x返回该元素的值。 bool IsEmpty()const {return first->link==NULL?true:false;} bool IsFull()const{return false;} //链表不会满的 void Sort(); void input(); void output(); List<T>& operator=(List<T>& L); //链表赋值直接用等号 template<class T> List<T>::List(List<T>&L) //复制构造函数 { T value; LinkNode<T>*srcptr=L.getHead(); LinkNode<T>*destptr=first=new LinkNode<T>; while(srcptr->link!=NULL) { value=srcptr->link->data; destptr->link=new LinkNode<T>(value); destptr=destptr->link; srcptr=strptr->link; } destptr->link=NULL; }//????/ template<class T>9 void List<T>::MakeEmpty { //删去除附加头节点外的所有其他结点 LinkNode<T>*q; while(first->link!=NULL) { q=first->link; first->link=q->link; //将表头结点后第一个结点q从 //链中摘下 delete q;//释放它 } } template<class T> int List<T>::Length()const { LinkNode<T>*p=first->link; int count=0; while(p!=NULL) { p=p->link; count++; } return count; } template<class T> LinkNode<T>*List<T>::Locate(int i) { //定位函数 返回第i个元素的地址 if(i<0) return NULL; LinkNode<T>*p=first; int k=0; while(p!=NULL&&k<i) { p=p->link;k++;//k为计数器 } return p; } //----------------------------- //插入 先修改后继 s=new Node<T>; s->data=x; s->link=p->link; p->link=s; /* 有了表头 每个结点都有了前驱结点 直接插 插入伪代码“ 1、工作指针p初始化 2、找到第i-1个结点并使工作指针p指向该结点 3、若查找不成功,则插入位置不合理, ... */ template<class T> bool LinkList<T>::Insert(int i,T &x) { //在第i个位置后插入元素x //????????????? //i到底是数组下标还是第i个数据 LinkNode<T> *p=Locate(i-1); if(p==NULL) return false; else { s=new LinkNode<T>(x); s->link=p->link; p->link=s; return true; } } //删除是直接删除第i个元素 //用临时变量保存被删结点、 //取值,摘链 //删除不能删最后一个元素 /* 表尾的特殊情况: 虽然被删结点不存在 但其前驱结点却存在 最后一个结点虽然有前驱 但是不存在 */ bool List<T>::Remove(int i,T &x) { /* q=p->link; x=q->data; p->link=q->link; delete q; */ } /* 1、初始化p 2查找第i-1个结点并使p指向该结点 3.若p不存在或p不存在后继结点,则不可删/ 3.1暂存被删元素 3.2摘链 3.3释放被删结点 3.4返回被删元素值 */ //删除代码 template<class T> bool List<T>::Remove(int i,T &x) { LinkNode<T>*p=Locate(i-1); if(p==NULL||p->link==NULL) return dalse; LinkNode<T>*q=p->link; x=q->data; p->link=q->link; delete q; return true; } //两个链表直接重载赋值 template<class T> List<T>&List<T>::Operator=(List<T>&L) { T value; LinkNode<T> *srcptr=L.getHead(); LinkNode<T> *destptr=first=new LinkNode<T>; while(srcptr->link!=NULL) { value=srcptr->link->data; destptr->link=new LinkNode<T>(value); destptr=destptr->link; srcptr=strptr->link; } destptr->link=NULL; return *this; } //输入链表 //前插法 物理顺序和逻辑顺序相反 template<class T> void List<T>::inputFront(T endTag) { LinkNode<T>*newNode;T val; makeEmpty();cin>>val; while(val!=endTag) { newNode=new LinkNode<T>(val); //new 可能申请不成功 所以下面要判断 if(newNode==NULL) { cerr<<"error"<<endl; exit(1); } newNode->link=first->link; first->link=newNode; cin>>val; } } //后插法 template<class T> void List<T>::inputRear(T endTag) { LinkNode<T>*newNode,*last,T val; makeEmpty();cin>>Val;last=first; while(val!=endTag) { newNode=new LinkNode<T>(val); if(newNode==NULL) { cerr<<"error"<<endl; exit(1); } last->link=newNode; last=newNode; cin>>val; } } /* 算法设计的一般步骤 1.确定入口(已知条件) 出口(结果) 2.画示意图 3. (1)正向思维: 选定一个思考问题的起点 提出问题 解决问题 (2)逆向思维:从结论出发 (3)正逆结合 4.写出顶层较为抽象算法,分析边界情况 5.验证第四步的算法 6.写出具体的算法 7.进一步验证 手工运行 */ /* 单链表中: (1)除非已知first否则无法找到其他所有节点 (2)单链表里 找后继容易 找前驱难 为了解决: 1.单向循环链表 循环链表 首尾相连 最后一个结点的后继为第一个结点 空表:first->link=first 开始节点:first->link; 终端节点:将单链表扫一遍 O(n) p!=NULL // p!=first(循环链) p->link!=NULL//p->link!=first 带尾指针的循环链表: 开始结点: last->link->link 终端节点:last */ template<class T> class CircList:public LinearList<T> { private: CircListNode<T>*first,*last; public: CircList(const T&x); CircList(CircList)<T> //-------------- } //循环链表插入 bool CircList<T>::Insert(int i,T x) { p=first;count=0; while(p!=first&&count<i-1) { p=p->next; count++; } if(p==NULL) return false; else { s=new CircLinkNode<T>; s->data=x; s->link=p->link; p->link=s; } } 2.双向循环链表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 2、从被调用函数返回调用函数之前,应该完成“ 1)保存被调用函数的计算结果 2)释放被调用函数的数据区 3)依照被调用函数保存的返回地址将控制转移到调用函数 当多个函数嵌套调用时,由于函数运行的规则是:后调用先返回, 因此各个函数的存储管理应实行"栈式管理“ 总之:函数调用后调用先返回,实行栈式管理。 */ int main() { A(5,6); return 0; } void A(int a,int b) { B(); } void B() { } /* 下面是一个栈: 函数B占有存储区 ----栈顶 函数A占有存储区 主函数占有存储区 ---栈顶 */ /* 2.1递归算法 递归:直接或间接调用自身的算法。 递归函数:如 N!,Fibonacci,Ackerman函数 递归数据结构:单链表,二叉树,图等。 递归时,问题的本质没有发生变化,变化的是问题的规模。 递归的分类: (1)直接调用自身 A() {A()} (2)间接调用自身 B() {C()} 递归的优缺点: (1)优点:算法简明,正确性易证明,是分析、设计的有利工具。 (2)缺点:执行效率不高,堆栈空间耗费。 原因:参数、局部量、返回点的保存。大量的重复计算。 例一:阶乘函数: n!=1 n(n-1)! int f(int n) { sum=1; for(int i=1;i<=n;i++) sum=sum*i; return sum; } int f(int n) { if(n==0) return 1; return n*f(n-1); } 递归算法的设计 对原问题f(s)进行分解,给出合理的较小问题f(s'); 假设f(s')是可解的,给出f(s)与f(s')之间的关系。 确定一个特定情况的解,由此作为递归出口。 含一个递归调用的递归过程的一般形式 递归定义包括两项: */ /* 栈的应用:递归 1、函数是递归定义的 阶乘函数 long Factorial(long n) { if(n==0)return 1; else return n*Factorial(n-1); } 递归调用 回归求值 2、数据结构是递归的--单链表 搜索链表最后一个结点并打印其数值 template<class T> void Print(ListNode<T>*f) { if(f->link==NULL) cour<<f->data<<endl; else Print(f->link); } 3、问题的解法是递归的 汉诺塔解法: 如果n=1,则将这一个盘子直接从A柱移到C柱上。 否则,执行以下三步: ①:用C柱做过渡,将A柱上的(n-1)个盘子移到B柱上。 ②:将A柱上最后一个盘子直接移到C柱上 ③:用A柱做过渡,将B柱上的(n-1)个盘子移到C柱上。 A(n)--> C--> B A(1) B(N-1) C A B(n-1) C(1) B(n-1)-->A-->C(1) B A C(n) 递归:自顶向下,逐步分解的策略。 */ /* 递归过程改为非递归过程 递归过程简洁、易懂、易编 计算斐波那契额数列。 Fib(n)=n Fib(n-1)+Fib(n-2), */ /* 递归与回溯 对一个包含有许多结点,且每个结点有多个分支的问题,可以先选择一个分支进行搜, 当搜素到某一结点,发现无法再继续搜索下去时,可以沿搜索路径回退 到前一结点,沿另一分支继续搜索。 如果回退之后没有其他选择,再沿搜索路径回退到更前结点。 依次执行,直到搜索到问题的解。 回溯算法是一种深度算法。 */ /* 3.迷宫问题(穷举法) 寻找一条从入口到出口的通路。 4、N皇后问题。 n个皇后不能在同一行,同一列,同一对角线上。 */