数据结构强化小结
1. 时间复杂度
链表合并
最坏情况下
t(n) = m+n-1
O(n) = O(max(m,n))
2. 数据结构
2.1 线性结构
2.1.1 线性表
- 顺序表(顺序存储、随机存取)
#define MaxSize 500 //宏定义
//定义
typedef struct{
ElemType *elem; //基地址
int length; //表长
}Sqlist;
//初始化
bool InitList(Sqlist &L){
L.elem = new ElemType[MaxSize]; //L.elem = (ElemType*)malloc(sizeof(ElemType)*InitSize);
if(!L.elem) return false;
L.length = 0;
return true;
}
//取值 O(1)
bool GetElem(SqList L,int i,ElemType e){
if(i<1 || i>L.length) return false;
e = L.elem[i-1];
return true;
}
//查找 O(n)
int LocateElem(SqList L,ElemType e){
for(int i = 0;i < L.length;i++){
if(L.elem[i] == e) return i+1;
}
return 0;
}
//插入 O(n)
bool ListInsert(SqList &L,int i,ElemType e){
if(i<1 || i>L.length+1) return false;
if(L.length == MaxSize) return false;
for(int j = L.length-1;j>=i-1;j--){
L.elem[j+1] = L.elem[j];
}
L.elem[i-1] = e;
L.length++;
return true;
}
//删除
bool ListDelete(SqList &L,int i){
if(i<1 || i>L.length) return false;
for(int j = i;j < L.length;j++{
L.elem[j-1] == L.elem[j];
)
L.length--;
return true;
}
/*注意;
&取址
*取值
*/
-
链表
- 单链表
//定义 typedef struct LNode{ ElemType data; Struct LNode *next; }LNode,*LinkList //初始化 bool InitList(LinkList L){ L = new LNode; L->next = null; return true; } //取值 bool GetElem(LinkList L,int i,ElemType &e){ LNode *p = L->next; int j = 1; while(p && j<i){ p = p->next; j++; } e = p->data; if(!p) return false; return true; } //查找 LNode *LocateElem(LinkList L,ElemType e){ LNode *p = L->next; while(p && p->next!=e) p = p->next; return p; } //插入 s = new LNode; s->data = e; s->next = p->next; p->next = s; //删除 q = p->next; p->next = q->next; free(q); // delete(q); //创建单链表 //头插法 L = new LNode; L->next = null; for(int i = 0;i<n;i++){ s = new LNode; s->data = x; s->next = L->next; L->next = s; } //尾插法 L = new LNode; L->next = null; r = L; for(int i = 0;i<n;i++){ s = new LNode; s->data = x; //cin>>s->data; s->next = null; r->next = s; r = s; }- 双链表
typedef struct DLNode{ ElemType data; struct DLNode *prior; struct DLNode *next; }DLNode,*DLinkList;-
循环链表
-
静态链表
#define MaxSize 500 typedef struct{ ElemType data; int next; }SLinkList[MaxSize];
2.1.2 栈
- 顺序栈
typedef struct{
ElemType data[MaxSize];
int top;
}SqStack;
//初始化
bool InitStack(SqStack &S){
S.top = -1;
return true;
}
//判栈空
bool StackEmpty(SqStack S){
if(S.top == -1) return true;
return false;
}
//入栈
bool Push(SqStack &S,ElemType e){
if(top == MaxSize-1) return false;
S.top++;
S.data[S.top] = e;
return true;
}
//出栈
bool Pop(SqStack &S,ElemType &e){
if(S.top == -1) return false;
e = S.data[S.top];
S.top--;
return true;
}
//读栈顶元素
bool GetTop(SqStack S,ElemType &e){
if(S.top == -1) return false;
e = S.data[S.top];
return true;
}
- 链栈
typedef struct StackNode{
ElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
//初始化
bool InitStack(LinkStack &S){
S = null;
return true;
}
//入栈
bool Push(LinkStack &S,ElemType e){
StackNode *p;
p = new StackNode;
p->data = e;
p->next = S;
S = p;
return true;
}
//出栈
bool Pop(LinkStack &S,ElemType e){
if(S == null) return false;
e = S->data;
StackNode *p;
p = S;
S = S->next;
delete p;
return true;
}
- 共享栈
2.1.3 队列
- 顺序队列
#define MaxSize 500
typedef struct{
ElemType data[MaxSize];
int rear,front;
}SqQueue;
//初始化
bool InitQueue(SqQueue &Q){
Q.front = Q.rear = 0;
return true;
}
//求队列长度
int QueueLength(SqQueue Q){
return (Q.rear-Q.front + MaxSize)%MaxSize;
}
//入队
bool EnQueue(SqQueue &Q,ElemType e){
if((Q.rear+1)%MaxSize == front) return false;
Q.data[Q.rear] = e;
Q.rear = (Q.rear+1)%MaxSize;
return true;
}
//出队
bool DeQueue(SqQueue &Q,ElemType &e){
if(Q.front == Q.rear) return false;
e = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;
return true;
}
-
链式队列
-
循环队列
2.1.4 串
2.2 非线性结构
2.2.1 树
结点数=边数+1 结点数=度数之和+1
满二叉树 第i层 2^(i-1)个结点
h层满二叉树 共有2^h-1个结点
结点数n = n0+n1+n2+...+nm = 1 n1+2 n2+...+m nm+1
二叉树 n0 = n2+1
完全二叉树 n1=0 或 n1=1
线索二叉树
存储:双亲表示法、孩子表示法、孩子兄弟表示法
2.2.2 图
完全图 无向图有n(n-1)/2条边 有向图有n(n-1)条弧
生成子图 V'=V E'包含于E
极大连通子图 极小连通子图
生成树 包含全部顶点的极小连通子图 边数=顶点数-1
有向树 一个顶点的入度为0,其余顶点的入度均为1的有向图
存储:邻接矩阵法、邻接表法、十字链表法、邻接多重法
2.2.3 集合
//初始化
void Initial(int *set,int n){
for(int i=0;i<n;i++){
set[i] = -1;
}
}
//查找
int find(int *set,int x){
while(set[x]>0){
x = set[x];
}
return x;
}
//合并
void merge(int set[],int x,int y){
x = find(set,x);
y = find(set,y);
set[x] += set[y];
set[y] = x;
}
3. 查找
3.1 顺序查找
平均查找长度ASL:成功(1+n)/2、失败n
3.2 折半查找
画出折半查找树,层数对应查找次数
ASL:成功每层结点数乘以层数求和后/n、失败每层失败结点个数乘以父结点所在层数求和后/失败结点总个数
注意:失败结点数=成功结点数n+1
3.3 BST
二叉排序树、二叉搜索树
中序遍历(左-根-右)得到有序序列
删除操作:替换为直接前驱或直接后驱之后到了叶子结点的位置,直接删除叶子结点
3.4 AVL
二叉平衡树:左右子树高度差小于等于1
Nh = Nh-1 + Nh-2 + 1(高度为h的平衡二叉树,最少需要Nh个结点)
(1)插入
LL:右旋 RR:左旋 LR:先左旋再右旋 RL:先右旋再左旋
(2)删除
叶子结点删除后是AVL直接删除,删除后不是AVL进行旋转
被删除结点只有左子树/右子树,删除后用左子树/右子树直接代替,调整至AVL
被删除结点既有左子树又有右子树,平衡因子为1(-1),用直接前驱(直接后驱)代替后删除,调整至AVL
3.5 红黑树
新插入结点默认为红色
空树或父黑:红色
父红且叔红:叔父染色,祖父染色
父红且叔黑/空:LL:父、祖父染色,祖父右旋;LR:父左旋,转换为LL型,父、祖父染色,祖父右旋;RR:父、祖父染色,祖父左旋;RL:父右旋,转换为RR型,父、祖父染色,祖父左旋
3.6 B树/B+树
m阶B树最多有m个子结点(m个分支)。除根和叶子外,每个结点至少有m/2向上取整个子结点。所有叶子结点都在同一层。关键字个数不超过m-1
n个关键字对应n+1个子结点
B+树支持顺序查找和随机查找,B树只支持随机查找
(1)插入
插入后关键字个数不超过m-1,直接插入
插入后关键字个数超过m-1,取关键字中间值放入父结点
(2)删除
被删关键字不在终端结点,用其直接前驱或直接后驱代替,转换为被删关键字在终端结点的情况
被删关键字在终端结点,该结点关键字个数大于等于m/2向上取整,直接删除;该结点关键字个数等于m/2向上取整 - 1且与其相邻的结点关键字个数大于等于m/2向上取整,父子换位;该结点关键字个数等于m/2向上取整 - 1且与其相邻的结点关键字个数等于m/2向上取整 -1,父子合并
3.7 散列表
堆积/聚集:线性探测法中,冲突的数据连成一大片连续区域
冲突:不同的关键字映射到同一位置(同义词)
装填因子 = 关键字个数 / 表长
产生冲突的原因呢:散列函数、处理冲突的方法、装填因子
查找成功的平均查找长度ASL:所有关键字探测次数之和 / 关键字个数
查找失败的平均查找长度ASL:函数值域内的每个地址的失败探测次数之和 / 可映射到的地址数
4. 排序
4.1 插入排序
4.1.1 直接插入排序
void InsertSort(ElemType A[],int n){
int i,j;
for(i=2;i<=n;i++){
if(A[i]<A[i-1]){ //有序和无序的分界处
A[0]=A[i]; //第一个无序的数字赋值给哨兵
for(j=i-1;A[0]<A[j];j--){ //将其插入至有序位
A[j+1] = A[j];
}
A[j+1]=A[0];
}
}
}
Best:数据已有序,比较n-1次,移动0次。O(n)
Worst:数据逆序,

4.1.2 折半插入排序
void InsertSort(ElemType A[],int n){
int i,j,low,high,mid;
for(i=2;i<=n;i++){
A[0] = A[i];
low = 1;
high = i - 1;
while(low<=high){ //二分查找,新加入元素应插入位置
mid = (low + high)/2;
if(A[mid]>A[0]) high = mid - 1;
else low = mid + 1;
}
for(j=i-1;j>=high+1;--j){ //将大于A[0]的值后移
A[j+1] = A[j];
}
A[high+1] = A[0]; //使A[0]值赋值给A[high+1]使有序
}
}
减少了比较元素的次数,约为O(n logn)
移动次数并未改变
4.1.3 希尔排序
先将原表分割成若干子表,对各个子表进行直接插入排序
void ShellSort(ElemType A[],int n){
for(dk=n/2;dk>=1;dk=dk/2){ //分割,调整步长。增量序列为互质、递减。先粗调后微调
for(i=dk+1;i<=n;++i){ //直接插入排序
if(A[i]<A[i-dk]){
A[0] = A[i];
for(j=i-dk;j>0 && A[0]<A[j];j-=dk){
A[j+dk] = A[j];
}
A[j+dk] = A[0];
}
}
}
}
4.2 交换排序
4.2.1 冒泡排序
void BubbleSort(ElemType A[],int n){
for(int i=0;i<n-1;i++){ //i是有序和无序的分界线,i也是趟数
bool flag = false;
for(int j=n-1;j>i;j--){ //从后往前简单交换,将较小值冒泡到靠前位置
if(A[j-1]>A[j]){
swap(A[j-1],A[j]);
flag = true;
}
}
if(flag == false) //某趟结束后没有交换,则已排序完成
return;
}
}
Best:数据已有序,比较n-1次,移动0次。O(n)
Worst:数据逆序,每趟比较(n-趟数)次,每比较1次移动3次。O(n平方)
4.2.2 快速排序
void QuickSort(ElemType A[],int low,int high){
if(low<high){
int pivotpos = Partion(A,low,high); //根据枢轴元素将原表分割成两个独立的子表
QuickSort(A,low,pivotpos-1); //递归排序
QuickSort(A,pivotpos+1,high);
}
}
int Partion(ElemType A[],int low,int high){ //使枢轴元素移动到最终的位置
ElemType pivot = A[low];
while(low<high){
while(low<high && A[high]>=pivot) --high;
A[low] = A[high];
while(low<high && A[low]<=pivot) ++low;
A[high] = A[low];
}
A[low] = pivot;
return low;
}
Best:每一趟排序后都能将元素序列均匀分割或两个长度大致相等的子表
4.3 选择排序
4.3.1 简单选择排序
void SelectSort(ElemType A[],int n){
for(i=0;i<n-1;i++){
min = i;
for(j=i+1;j<n;j++){
if(A[j]<A[min])
min = j;
}
if(min!=i)
swap(A[i],A[min]);
}
}
Best:不移动元素
Worst:移动3(n-1)次
无论初始排列如何,所需比较次数均为

4.3.2 堆排序
大根堆:L(i)>=L(2i)且L(i)>=L(2i+1)
小根堆:L(i)<=L(2i)且L(i)<=L(2i+1)
void BuildMaxHeap(ElemType A[],int len){
for(int i=len/2;i>0;i++){
HeapAdjust(A,i,len);
}
}
void HeapAdjust(ElemType A[],int k,int len){
A[0] = A[k];
for(i=2*k;i<=len;i*=2){
if(i<len && A[i]<A[i+1])
i++;
else{
A[k] = A[i];
k = i;
}
}
A[k] = A[0];
}
void HeapSort(ElemType A[],int len){
BuildMaxHeap(A,len);
for(int i=len;i>1;i--){
swap(A[i],A[1]);
HeapAdjust(A,1,i-1);
}
}
4.4 归并排序
ElemType *B = (ElemType*)malloc((n+1)*sizeof(ElemType));
void Merge(ElemType A[],int low,int mid,int high){ //二路归并
for(int k=low;k<=high;k++){
B[k] = A[k];
}
for(i=low,j=mid+1,k=i;i<=mid && j<=high;k++){
if(B[i]<=B[j])
A[k] = B[i++];
else
A[k] = B[j++];
}
while(i<=mid){
A[k++] = B[i++];
}
while(j<=high){
A[k++] = B[j++];
}
}
void MergeSort(ElemType A[],int low,int high){
if(low<high){
int mid = (low+high)/2; //递归分层
MergeSort(A,low,mid);
MergeSort(A,mid+1,high);
Merge(A,low,mid,high); //合并
}
}
时间复杂度O(n logn)
4.5 基数排序
最高位优先法
最低位优先法
4.6 外部排序
生成初始归并段+多路归并(s趟归并排序)
优化:增大归并路数k,减少初始归并段个数r
败者树
置换选择排序
最佳归并树:IO次数=2*WPL。(n0-1)%(k-1)=0 正好、=u 要补 k-u-1 个虚段
4.7 小结
比较次数与初始序列有关:快排、直接插入、冒泡、堆、希尔
比较次数与初始序列无关:归并、简单选择、基数
排序趟数与初始序列有关:冒泡、快排
排序趟数与初始序列无关:直接插入、折半插入、希尔、简单选择、归并、基数
不稳定排序:选择、快排、希尔、堆
5. 应用
5.1 并查集的应用
最小生成树、求无向图的连通性、求无向图的连通分量
压缩路径:优化Find操作,先找到根节点,再将查找路径上的所有结点都挂到根结点上
int Find(int s[],int x){
int root = x;
while(s[root]>=0){
root = s[root];
} //此时root为0
while(x!=root){
int t = s[x];
s[x] = root;
x = t;
}
return root;
}
5.2 哈夫曼树/哈夫曼编码
n个关键字构造的哈夫曼树包含的结点总数为2n-1
n0 = n2 + 1
WPL = 非根节点权值之和
5.3 最小生成树
包含全部顶点的一个极小连通图,具有最小权值
Prim(适合边稠密图):任选一顶点,选取距离顶点集合最近的顶点加入生成树
Kruskal(适合边稀疏而顶点多的图):保证不连通的前提下,选取权值最小的边加入生成树
5.4 最短路径
BFS
Dijkstra
Floyd
5.5 拓扑排序
拓扑序列
5.6 关键路径
ve(事件最早开始时间)、vl(事件最迟开始时间)、e(活动最早开始时间)、l(活动最迟开始时间)
活动的时间余量:vl - ve - l 。
5.7 矩阵压缩
6. 遍历
6.1 树的遍历
//递归
void InOrderTraverse(BiTree T){ //中序遍历
if(T){
InOrderTraverse(T->lchild);
cout<<T->data;
InOrderTraverse(T->rchild);
}
}
//非递归
void InOrderTraverse(BiTree T){ //中序遍历
InitStack(S);
p = T;
q = new BiTNode;
while(p || !StackEmpty(S){
if(p){
push(S,p);
p = p->lchild;
}
else{
pop(S,p);
cout<<q->data;
p = q->rchild;
}
}
}
6.2 DFS 深度遍历
bool visited[MVNum];
void DFS(Graph G,int v){
cout<<v;
visited[v] = true;
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)){
if(!visited[w]){
DFS(G,w);
}
}
}
//采用邻接矩阵 O(n方)
void DFS_AM(AMGraph G,int v){
cout<<v;
visited[v] = true;
for(w=0;w<G.vexnum;w++){
if(G.arcs[v][w]!=0 && (!visited[w])){
DFS_AM(G,W);
}
}
}
//采用邻接表 O(n+e)
void DFS_AL(ALGraph G,int v){
cout<<v;
visited[v] = true;
p = G.vertices[v].firstarc;
while(p!=null){
w = p->adjvex;
if(!visited[w]){
DFS_AL(G,W);
}
p = p->nextarc;
}
}
6.3 BFS 广度遍历
void BFS(Graph G,int v){
cout<<v;
visited[v] = true;
InitQueue(Q);
EnQueue(Q,v);
while(!QueueEmpty(Q)){
DeQueue(Q,u);
for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w)){
if(!visited[w]){
cout<<w;
visited[w] = true;
EnQueue(Q,w);
}
}
}
}
浙公网安备 33010602011771号