背诵
线性表
-
前驱:
-
后继
-
表长:
-
空表:
-
首元结点:
-
头结点:
-
头指针
-
线性表的结构特点,除了第一个和最后一个元素外,每个节点都只有一个前驱和后继。
-
线性表的存储方式:
栈与队列
-
顺序栈
-
链栈
-
链队列
-
栈与队列存储数据
-
栈的应用:
-
循环列表判队空、队满条件,
串
- 串是一段有限长的字符序列,由一对单引号相扩
- 通常以串的整体进行操作
- 串的三种存储方式:定长存储、堆分配存储、块链存储
数组与广义表
- 一维数组:相同类型的数据元素的集合
- 二维数组顺序存储方式:行优先顺序、列优先顺序
- 特殊矩阵:对称矩阵、三角矩阵、稀疏矩阵
- 稀疏矩阵存储方式:三元顺序法,行逻辑链接的三元组、十字链表法
- 稀疏矩阵转置时间复杂度:\(\mathcal{O}(column*t)\)快速转置时间复杂度\(\mathcal{O}(column+t)\)
- 广义表:
树与二叉树
-
树是n个结点的有限集;有且仅有一个跟结点,除了根结点外其余结点可分为互不相交的有限集合。
-
叶子结点、终端结点:度为0 的结点;
度:结点拥有的子树个数;
树的结点:包含一个数据元素及若干指向其子树的分支;
非终端结点、分支结点:度为0的结点;
内部结点:除了根节点之外;
树的度是树内各结点度的最大值。
树的度是各结点度的最大值;
孩子,双亲;
兄弟;
祖先:从根结点到该结点所经分支的所有结点;
堂兄弟:双亲在同一层;
深度、层数
-
二叉树的性质:
- 第 i 层至多\(2^{i-1}\) 个结点;
-
存储结构:顺序存储结构、链式存储结构:二叉链表、三叉链表
-
二叉树遍历:先序,中序,后序
int preordertraverse(Bitree T,status(*visit)(Telemtype e)){ if(T){ if(visit(T->data)) if(preordertraverse(T->lchild,visit)) if(preordertraverse(T->rchild,visit)) return OK; return ERROR; }else return OK; } void Inorder(biTree T){ stack S; InitStack(&S); BiTreeNode *p=T; while(p!=NULL||!EmptyStack(&S)){ if(p->lchild){ push(&S,p); p=p->lchild; } else{ pop(&S,p); visit(p); p=p->rchild; } } return ok; }
-
线索二叉树
求得结点的一个线性序列;前驱、后继的指针;称为线索;
线索链表的结点:增加两个标志域,Lchild指针Link0;Lchild指针指向前驱/后继,线索Thread 1;
-
void inordertraverse_thr(Bitree T,void(*visit)()){
p=T->lchild;
while(p!=T){
while(p->LTag==Link){p=p->lchild;}
visit(p->data);
while(p->RTag==Tread&&p->rchild!=T){ p=p->rchild;visit(p);}
p=p->rchild;
}
}
node* pre=null;\\全局变量
void init(Bitree T){
BiTree Th = (BiTree)malloc(sizeof(BiTNode));
Th->ltag = Link;
Th->rtag = Thread;
Th->rchild = Th;
if (!T) Th->lchild = Th;
else {
Th->lchild = T;
pre = Th;
InOrderThreading(T, visit); // 开始线索化
// 最后处理,连接头节点
pre->rtag = Thread;
pre->rchild = Th;
Th->rchild = pre;
}
}
void inorder(Bitree T,status(*visit)(Telemtype e),node * p){
if(!p){
inordered(p->lchild,visit)
if(p->lchild==NULL) {
p->Ltag=Thread;
p->lchild=pre;
}
if(pre->rchild==NULL||pre->Ltag==Thread){
pre->Ltag=Thread;
pre->rchild=p;
}
pre=p;
inordered(p->rlchid,visit);
}
return ;
}
树的三种存储结构:双亲表示法、孩子链表表示法,二叉链表存储方式;
树的遍历:先根遍历、后跟遍历、按层次遍历;
森林 | 树 | 二叉树 |
---|---|---|
先序遍历 | 先根遍历 | 先序遍历 |
中序遍历 | 后根遍历 | 中序遍历 |
哈夫曼树:n-1次,二叉树,实现数据压缩,最小编码长度,
图
-
图是由一个顶点集V和一个弧VR构成的数据结构
-
网:弧或边带权的图分别称为有向网或无向网
-
子图:
-
完全图:含有e=n(n-1)/2的无向图叫做完全图;类似定义有向完全图
-
稀疏图:边或者弧的个数\(<n*logn\)稀疏图,反之则为稠密图
-
出度,入度
-
简单路径:序列中顶点不重复出现的路径;
-
简单回路:序列中第一个顶点和最后一个顶点相同的路径
-
强连通图:对有向图,若任意两个顶点之间都存在一条有向路径;
-
各个强连通子图成为强连通分量;
-
无向图:连通图,非连通图:各个连通子图成为连通分量
-
图的存储表示:邻接矩阵、邻接表、十字链表
-
图的两种遍历方式:深度遍历和广度遍历:
void DFS(Graph G,int v){ visit[v]=True; visitFunc(v); for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)){ if(!visit[w]) DFS(G,w); } } void DFStraverse(Graph G,Status(*visit)(int v)){ visitFunc=visit; for(v=0;v<G.vexnum;++v){ visit[v]=False; } for(v=0;v<G.vexnum;v++){ if(!visit[v]) DFS(G,v); } }
void BFS(Graph G,int v){ visited[v]=True; visitFunc(v); Enqueue(v,&Q); while(!isempty(&Q)){ EnQueue(&Q); for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)){ if(!visited(w)){ visit(w); visited[w]=False; Enqueue(&Q,w); } } } } void DFStraverse(Graph G,Status(*visit)(int v)){ visitFunc=visit; for(v=0;v<G.vexnum;++v){ visited[v]=False; } for(v=0;v<G.vexnum;v++){ if(!visited[v]) DFS(G,v); } }
-
最小生成树:在e条带权的边中选取n-1条边(不构成回路),使权值之和为最小。
-
生出树:不要求权值最小
-
prim算法
void MIniSpanTree_P(MGraph G,VertexType u){ bool visited[G.vertexnum]={False}; visited[u]=True; int dist[G.vertexnum]=G.adj[u]; int cnt=0; while(cnt<G.vertexnum){ cnt++; int min=-1; int dist_min=INT_MAX; for(int i=0;i<G.vexnum;i++){ if(!visited[i]&&(dist[i]<dist_min||!min=-1)) { min=j; dist_min=dist[i]; } dist[j]=dist_min; visited[min]=True; for(int i=0;i<G.vexnum;i++){ if(!visited[i]&&dist[i]<G.adj[i][min]) dist[i]=G.adj[i][min]; } } }
-
Kruskal算法
-
dijstra算法:\(\forall v\in V~~ dist[u]=min \{ dist[u],dist[v]+w_{v->u} \}\)
-
拓扑排序:检查是否出现
按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系;
CountInDegree(G,indegree);//求顶点入du InitStack(S); for(int i=0;i<G.vernum;i++){ if(!indegree[i]) Push(S,i); } count=0; while(!IsEmpty(S)){ Pop(S,v); count++; printf(v); for(w=Firstadj(v);w;w=Nextadj(G,v,w)){ indegree(w)--; if(!indegree[w]) Push(S,w); } } if(count<G.vexnum) printf("存在回路");
-
关键路径
-
ve(源点)=0;
最早发生时间:
\(ve(k)=Max\{ve(j)+dut(<j,k>)\}\)
最迟发生时间:
\(vl(j)=Min\{vl(k)-dut(<j,k>)\}\)
活动(弧)最早开始时间e(i)=ve(j);最晚开始时间l(i)=vl(k)-dut(<j,k>)
查找表
-
查找表是由同一类型的数据元素构成的集合
-
功能:查询、检索、插入、删除
-
查找表分为静态查找表:查询和检索功能的查找表
动态查找表:将查询结果不在查找表中的数据元素插入;或者删除;
-
数据元素或记录中某个数据项的值,用以标识一个数据元素;若此关键字可以识别唯一的一个记录,称之为主关键字;弱关键字能识别若干记录,称为次关键字
-
查找:确定一个其关键字等于给定值的数据元素或记录;
-
静态查找表
-
顺序表查找
int location(SqList L,ElemType& e,Status(*compare)(ElemType,ELemtype)){ k=1; p=L.elem; while(k<=L.length&&!(*compare)(*p++,e)) k++; if(k<=L.length) return k; else return 0; } int search_Seq(SSTable ST,KeyType key){ ST.elem[0].key=key; for(int i=ST.length;ST.elem[i].key!=key;i--); return i; }
-
折半查找
int Search_Bin(SSTable ST,KeyType key){ int low=1;high=ST.length; while(low<=high){ mid=(low+high)/2; if(key==ST.R[mid].key) return mind; else if(key<ST.R[mid].key) high=mid-1; else { low=mid+1; } } return 0; }
-
索引顺序表(分块查找)
查找过程:
-
由索引确定记录所在区间;
-
在顺序表的某个区间内进行查找;
特点:缩小区间
索引顺序查找的平均查找长度=查找索引的平均查找长度+查找顺序表的平均查找长度;
-
-
查找表的特性
查找 插入 删除 无序顺序表 \(\mathcal{O}(n)\) \(\mathcal{O}(1)\) \(\mathcal{O}(n)\) 无序线性链表 \(\mathcal{O}(n)\) \(\mathcal{O}(1)\) \(\mathcal{O}(1)\) 有序顺序表 \(\mathcal{O}(\log(n))\) \(\mathcal{O}(n)\) \(\mathcal{O}(n)\) 有序线性链表 \(\mathcal{O}(n)\) \(\mathcal{O}(1)\) \(\mathcal{O}(1)\)
-
-
动态查找表
-
二叉排序树(二叉查找树)
-
左子树若不空,则左子树上所有结点的值均小于根节点的值;
-
若右子树不空,则右子树上所有结点的值大于根节点的值
-
它的左右子树都为二叉排序树
Status SearchBST(BiTree &T,KeyType key,BiTree &f,BiTree &p){ if(!T){ p=f; return FALSE; } else if(EQ(key,T->data.key)){ p=T; return TURE; } else if(LT(key,T->data.key)) SearchBST(T->lchild,key,T,p); else SearchBST(T->rchild,key,T,p); }
void InsertBST(BSTree &T,ElemType e){ if(!SearchBST(T,e.key,NULL,p)){ if(!T){ S=new BSTNode; S->data=e; S->lchild=S->rchild=NULL; T=S; } else if(e.key<T->data.key) InsertBST(T->lchild,e); else if(e.key>T->data.key) InsertBST(T->rchild,e); } }
void CreatBST(BSTree &T){ T=NULL; cin>>e; while(e.key!=ENDFALG){ InsertBST(T,e); cin>>e; } }
Status DeleteBST(BiTree &T,KeyType key){ SearchBST(T,e.key,NULL,p); if(!T) return FALSE; else Delete(p); } void Delete(BiTree &p){ if(!p->rchild){ q=p; p=p->lchild; free(q); } else if(!p->lchild){ q=p; p=p->rchild; free(q); } else{ q=p;s=p->lchild; while(s->rchild){ q=s; s=s->rchild; } p->data=s->data; if(q!=p) q->rchild=s->lchild; else q->lchild=s->lchild; free(s); } }
时间复杂度: \(n*\mathcal{O}(\log(n))\)
-
-
二叉平衡树
- 二叉平衡树使二叉排序树的另一种形式,其特点为树中每个结点的左右子树深度之差绝对值不大于1;
- 深度为\(h\)的二叉平衡树中所含结点最小值\(N_{h}=F_{h+2}-1\)
-
-
哈希表
-
哈希表:记录在表中的位置和它的关键字之间不存在一个确定的关系
查找的过程为给定值依次和关键字集合中各个关键字进行比较,查找的效率取决于和给定值进行比较的关键字个数。
-
哈希函数是一个映射。关键字的集合映射到某个地址集合上,只要这个地址结合的大小不超过允许范围。
-
哈希函数是一个压缩映像,容易存在冲突现象。
-
哈希函数的构造方法:
-
直接定址法:适用于地址集合大小等于关键字集合的大小。
\(H(key)=key~~or~~H(key)=a*key+b\)
-
数字分析法:分析关键字集中的全体,并从从中提取分布均匀的若干位或他们的组合作为地址。适用于:能预先估计出全体关键字的每一位上各种数字出现的频度。
-
平方取中法:以关键字的平方值的中间几位作为存储地址。适合于关键字中的每一位都有某些数字重复出现频度很高的现象。
-
折叠法:将关键字分割成若干部分,然后取它们的叠加和为哈希地址。适用于数字位数特别的的情况
-
除留余数法
-
随机数法\(H(key)=Random(key)\)
-
-
处理冲突的方法:为冲突的地址寻找下一个哈希地址
-
开放定址法:
-
\(H_{0}~H_{1}\dots H_{s}\)
\(H_{i}=(H(key)+d_{i})~~mod(m)\)
\(d_{i}\)三种取法:
-
线性探测再散列
\(d_{i}=c*i\)
-
平方探测再散列
\(d_{i}=1^{2},-1^{2},2^{2},-2^{2}\dots\)
-
随机探测再散列
\(d_{i}\)是一组伪随机数列或者\(d_{i}=i*H_{i}(key)\)
注意\(d_{i}\)应该具备完备性;\((m,d_{i})=1\)
-
-
-
链地址法
-
哈希表的查找
int hashsize[]={997,...}; typedef struct{ ElemType *elem; int count; int sizeindex; }HashTable; #define SUCCESS 1 #define UNSUCCESS 0 #defint DUPLICATE -1 status SearchHash(HashTable H,KeyType K,int &p,int &c){ p=Hash(K); while(H.elem[p]!=NULLKEY&&!EQ(K,H.elem[p].key)) return SUCCESS; else return UNSUCCESS; } Status InsertHash (HashTable &H,Elemtype e){ c=0; if(HashSearch(H,e.key,p,c)==SUCCESS) return DUPLICATE; else if(c<hashsize[H.sizeindex]/2){ H.elem[p]=e;H.count++; return OK; } else RecreateHashTable(H); }//inserthash
-
装载因子:$\alpha=n/m $
-
-
排序
-
排序:将无序的记录序列调整为有序的记录序列
-
外部排序:不需要访问外存便可以完成,此类问题成为内部排序
-
内部排序:不可能只在内存中
-
内部排序方法:插入类、交换类、选择类、归并类、其他方法
-
- 插入类:将无序子序列的一个或几个记录插入到有序序列中
- 交换类:通过交换无序序列中的记录从而得到其中关键字最小或最大的记录,并将它加入到有序子序列中
- 选择类:从记录的无序子序列中选择关键字最小或最大的记录,并将它加入到有序子序列中
- 归并类:通过归并两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度
-
实现一趟插入排序:
-
方法:直接插入排序、折半插入排序、希尔排序
-
希尔排序:
基本思想:对待排序记录序列先作宏观调整,再做微观调整;将记录序列分为若干个子序列,分别对每个子序列进行插入排序。
void ShellInsert(SqList &L,int dk){ for(int i=dk+1;i<n;i++){ if(L.r[i]<L.r[i-dk].key){ L.r[0].L.r[i]; for (j=i-dk; j>0&&(L.r[0].key<L.r[j].key); j-=dk) L.r[j+dk] = L.r[j]; // 记录后移,查找插入位置 L.r[j+dk] = L.r[0]; // 插入 } } } void ShellSort (SqList &L, int dlta[], int t) { // 增量为dlta[]的希尔排序 for (k=0; k<t; ++t) ShellInsert(L, dlta[k]); //一趟增量为dlta[k]的插入排序 } // ShellSort
-
交换排序:
-
起泡排序,一趟快速排序,快速排序
-
起泡排序
void bubblesort(Elem R[],int n){ int i=n; while(i>1){ int lastxchangeIndex=1; for(int j=1;j<i;j++){ if(R[j+1].key<R[j].key){ swap(&R[j+1].key,&R[j].key); lastExchangeIndex=j; } i=lastExchangeIndex; } } }
-
一趟快速排序
目标:找一个目标,以他的关键字为枢轴,凡其关机子小于枢轴的记录均移动到该记录之前,反之,移动掉该记录之后。
int partition(RedType R[],int low,int high){ R[0]=R[low];pivotkey=R[low].key; while(low<high){ while(low<high&&R[high].key>=pivotkey) --high; R[low]=R[high]; while(low<high&&R[low].key<=pivotkey)++low; R[high]=R[low]; } R[low]=R[0]; return low; }
void QSort(RedType &R[],int s,int i){ if(s<t){ pivotloc=Partition(R,s,t); Qsort(R,s,pivotloc-1); Qsort(R,s,pivotloc+1,t); } }
-
-
选择排序
-
简单选择排序
-
堆排序
void HeapAort(HeapType &H){ for(i=H.length/2;i>0;i--){ HeapAdjust(H.r,i,H.length); } for(i=H.length;i>1;i--){ swap(H.r[1],H.r[i]) HeapAdjust(H.r,1,i-1); } } void HeapAdjust(RcdType &R[],int s,int m){ rc=R[s]; for(int j=2*s;j<=m;j*2){ if(j<m&&R[j].key<R[j+1].key) j++; if(rc.key>=R[j].key) break; R[s]=R[j]; s=j; } R[s]=rc; }
-
-
归并排序
void Merge(RcdType SR[],RcdType TR[],int i,int m,int n){ for(int j=m+1,int k=i;i<m&&j<=n;++k){ if(SR[i].key<=SR[j].key) TR[k]=SR[i++]; else TR[k]=SR[j++]; } while(i<=m) TR[k]=SR[i++]; while(j<=n) TR[k]=SR[j++]; for(int i=m;i<=n;i++){ SR[i]=TR[i]; } } void Msort(RcdType SR[],RcdType TR1[],int t){ if(s==t) TR1[s]=SR[s]; else{ m=(s+t)/2; Msort(SR,TR1,s,m); Msort(SR,TR2,m+1,t); Merge(SR,TR1,s,m,t); } } void MergeSort(SqList &L){ Msort(L.r,L.r,1,L.length); }
-
-
时间复杂度 空间复杂度 稳定性 直接插入排序 \(\mathcal{O}(n^{2})\) \(\mathcal {O}(1)\) 稳定 快速排序 \(\mathcal{O}(n*\log n)\) \(\mathcal {O}(\log n)\) 不稳定 起泡排序 \(\mathcal{O}(n^{2})\) \(\mathcal {O}(1)\) 稳定 归并排序 \(\mathcal{O}(n*\log n)\) \(\mathcal {O}(n)\) 稳定 简单选择排序 \(\mathcal{O}(n^{2})\) \(\mathcal {O}(1)\) 不稳定 堆排序 \(\mathcal {O}(n*\log n)\) \(\mathcal {O}(1)\) 不稳定 待排序序列已经有序时,直接插入排序和起泡排序达到\(\mathcal{O}(n)\)
快速排序为\(\mathcal{O}(n^2)\)
简单选择排序、堆排序、归并排序时间性能不随记录序列关键字分布而改变
希尔排序不稳定;
基于关键字的排序方法最快\(\mathcal{O}(n*\log n)\)
标签:结点,排序,复习,int,查找,key,mathcal,数据结构 From: https://www.cnblogs.com/lzy-sleepy/p/18639707