数据结构总结

本文详细介绍了数据结构的基础概念,包括线性表、栈、队列、树和图的逻辑结构和存储结构。讨论了顺序存储和链式存储的优缺点,以及在不同操作下的性能。此外,还涵盖了排序和查找算法,如快速排序、简单选择排序和哈希查找。最后,阐述了二叉树和图的特性及其在最小生成树问题中的应用,如普利姆算法和克鲁斯卡尔算法。
摘要由CSDN通过智能技术生成

数据结构总结
本文章结合第四版教材、网上资料和期末老师所画重点和考试范围对教材基础概念和重点知识点进行总结和串联,针对人群主要是学生和初学者。文中不免含有遗漏处及误点,欢迎大家批评交流。希望各位读者都能从此文有所收获,我们一起加油!
第一章 绪论 2
1.数据结构的研究内容 2
2.基本概念和术语 2
3.算法和算法分析 2
第二章 线性表 3
1.线性表的定义和特点 3
2.线性表的顺序存储表示 3
2.5顺序表的实现和应用(通过代码) 3
3.单链表的定义和表示 6
3.5链式表的应用和实现 6
4.顺序表与链式表的比较 10
第三章 栈和队列 10
1.栈和队列的定义和特点 10
2.顺序栈的表示和实现 10
3.链栈的表示和实现 12
4.栈与递归 13
5.循环队列 13
6.链队列 13
第五章 树和二叉树 15
1.树的定义和基本术语 15
2.二叉树的定义 16
3.二叉树的5个性质 16
4.n个结点的二叉树的形态 16
5.树、二叉树和森林的相互转换 16
6.线索二叉树 17
7.哈夫曼树及其应用 18
第六章 图 18
1.图的定义和基本属于 18
2.邻接矩阵(图的存储结构) 18
3.邻接表(图的存储结构) 19
4.深度优先搜索(图的遍历) 19
5.普利姆算法(图的应用:最小生成树) 19
6.克鲁斯卡尔算法(图的应用:最小生成树) 19
7.拓扑排序(图的应用) 19
第七章 查找 19
1.查找的基本概念 19
2.线性表的查找 20
3.二叉排序树(树表的查找) 20
4.散列表的查找 20
5.各查找方法的特点及使用情况 21
第八章 排序 21
1.排序的基本概念 21
2.快速排序(交换排序) 21
3.简单选择排序(选择排序) 21
4.不同排序的复杂度 21

第一章 绪论
1.数据结构的研究内容
数据结构是介于数学,计算机硬件和软件三者之间的一门核心课程。
分清数据的内在联系,合理的组织数据,才能对他们进行有效的处理,设计出高效的算法。如何合理的组织数据,高效的处理数据是数据结构研究的主要问题。
2.基本概念和术语
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。包括逻辑结构和存储结构两个层次。
------“逻辑结构”------
是从逻辑关系上描述数据,与数据的存储无关,是独立于计算机的。数据的逻辑结构可看作是从具体问题抽象出来的数学模型。其含有两个要素:一是数据元素,二是关系。根据数据元素的不同特性,通常有四类基本结构:
集合结构:数据元素除了属于同一集合外无其他关系
线性结构:数据元素之间存在一对一的关系
树结构:数据元素之间存在一对多的关系
图结构:数据元素之间存在多对多的关系
数据的逻辑结构

--------“存储结构”--------
数据对象在计算机中的存储表示称为数据的存储结构,也成为物理结构。两种基本存储结构为顺序存储结构和链式存储结构。
3.算法和算法分析
算法分析的两个主要方面是分析算法的时间复杂度和空间复杂度,一般情况下,鉴于运算空间较为充足,故将时间复杂度作为分析的重点。
-------时间复杂度
是说执行算法需要消耗的时间长短,越快越好。
T(n)=O(f(n))
比如:
Int a=1;int b=3;int c=4;O(n)=1//时间复杂度为o(1)//O代表算法执行的时间,代表代码执行时间的增长变化趋势。
for(i=1;i<=n;i++){j=I;j++;}//O(n)//时间复杂度是o(n)
for(x=1;i<n;i++)
{for(i=1;i<n;i++){
j=i;
j++
}}//O(n^2)
Int i=1;while(i<n){i=i*2;}//O(logn)//因为底数对于研究程序运行效率不重要,写代码是要考虑数据规模n对程序的运行效率的影响,常数部分则忽略。
-------空间复杂度
是说执行当前算法所需要消耗的存储空间大小,越小越好。
Int i=1;int j=2;j++;i++;int m=i+j;//S(n)=O(1)//因为I,j,m所分配的空间不随数据处理而变化。
Int[] m=new int[n];
for(……){……}//s(n)=o(n)//第一行分配了一个空间,后虽有循环但无并分配空间。
第二章 线性表
线性表,栈,队列,串和数组都属于“线性结构”。
1.线性表的定义和特点
数据特性相同的元素构成的有限序列称为线性表。
线性结构的基本特点是://除第一个元素无直接前驱;//最后一个元素无直接后继之外;//其他每个数据元素都有一个前驱和后继。且存在唯一的被称作“第一个”和“最后一个”的数据元素。
2.线性表的顺序存储表示
线性表的顺序存储表示指的是用一组地址连续存储的存储单元依次存储线性表的数据元素,这种表示也称作线性表的顺序存储结构或顺序映像。其也称为顺序表。
特点是:逻辑上相邻的数据元素,其物理次序也是相邻的。
2.5顺序表的实现和应用(通过代码)
#include<iostream.h>
#define MaxSize 100
//定义student结构体
typedef struct
{
int id;
char name[10];
int score;
}Student;
//定义顺序表的存储结构
typedef struct
{
Student *elem;//存储空间的基地址
int length;
}SqList;

//顺序表的初始化
bool InitList(SqList &L)
{
L.elem=new Student[MaxSize];//为顺序表分配一个大小为MaxSize的存贮空间
if(!L.elem)
return false;
L.length=0;
return true;
}

//顺序表的取值
bool GetElem(SqList L,int i,Student &e)
{
if(i<1 || i>L.length)
{
return false;
}
e=L.elem[i-1];
return true;
}

//顺序表的查找
int LocateElem(SqList L,Student e)
{
for(int i=0;i<L.length;i++)
{
if(L.elem[i].id==e.id)
{
return i+1;//如果查找成功返回序号i+1
}
return 0;//否则返回0
}
}

//顺序表的插入
bool ListInsert(SqList &L,int i,Student 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;//将新元素放到第i个位置
++L.length;
return true;
}

//顺序表的删除
bool ListDelet(SqList &L,int i)
{
if((i<1)||(i>L.length+1))
return false;
for(int j=i;j<L.length;j++)
L.elem[j-1]=L.elem[j];
–L.length;
return true;
}

//输出语句函数
void DisPlayList(SqList L)
{
for(int i=0;i<=L.length-1;i++)
cout<<“学号:”<<L.elem[i].id<<“姓名:”<<L.elem[i].name<<“分数:”<<L.elem[i].score;
}

int main()
{
SqList L;
Student s1={19010102,“zhangsan”,95};
Student s2={19010103,“lisi”,100};
InitList(L);
ListInsert(L,1,s1);
ListInsert(L,1,s2);
ListDelet(L,1);
DisPlayList(L);
return 0;
}

3.单链表的定义和表示
线性表链式存储结构的特点是:用一组任意存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成的数据元素ai的存储映像,称为“结点”。
它包括两个域:存储信息的区域称为数据域;存储直接后继存储位置的域称为指域(其中存储的信息称为指针或链)。
由于链表的每个结点中只包含一个指针域,故又称线性链表或单链表。
头指针指示链表中第一个结点(也称首元结点),同时最后一个数据元素没有直接后继,其指针为NULL。
用单链表表示线性结构时,数据元素之间的逻辑关系是由结点中的指针指示的。
注:
单链表可用头指针的名字来命名(头指针名L则简称该链表为表L)
区分LinkList p和LinkList *p,p表示某结点的指针变量(表示该结点的地址),*p为对应的结点变量,表示该结点的名称。
首元结点是链表存储第一个数据元素ai的结点。
头结点是首元结点之前副设的一个结点,其指针域指向首元结点(数据域可无信息,也可有元素类型相同的附加信息)。
若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。
3.5链式表的应用和实现
#include<iostream.h>
#define MaxSize 100
//定义student结构体
typedef struct
{
int id;
int score;
}Student;
//定义LNode结构体—单链表的存贮结构
typedef struct LNode
{
Student data;//结点的数据域
struct LNode *next;//结点的指针域
}LNode,*LinkList;//LinkList为指向LNode的指针类型(important)
//单链表的初始化
bool InitList(LinkList &L)
{
L=new LNode;//生成的新结点作为首元结点----
L->next=NULL;//头节点的指针域为空------->就是为了创捷一个首元节点
return true;
}
//单链表的取值
bool GetElem(LinkList L,int i,Student &s)
{
LNode *p;
p=L->next;
int j=1;//初始化P指向首元节点
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i)
return false;
s=p->data;//取第i个结点的数据域
return true;
}
//单链表的按值查找
LNode *LocateElem(LinkList L,Student s)
{
LNode *p=L->next;//初始化,p只想首元节点
while(p&&p->data.id!=s.id&&p->data.score!=s.score)
p=p->next;
return p;
}
//单链表的插入
bool ListInsert(LinkList &L,int i,Student s)
{
LNode *p=L;int j=0;
while(p&&(j<i-1))//查找a(i-1)个元素
{
p=p->next;//p只想a(i-1)
j++;
}
if(!p||j>(i-1))
return false;
LNode *a;
a=new LNode;
a->data=s;//生成一个新的结点a
a->next=p->next;
p->next=a;//将结点p的指针与指向新的结点a
return true;
}
//单链表的删除
bool ListDelete(LinkList &L,int i)
{
LNode *p=L;int j=0;
while((p->next)&&(j<i-1))
{
p=p->next;j++;
}
if(!p||j>(i-1))
return false;
LNode *q;
q=p->next;//临时保存被删结点的地址以备释放
p->next=q->next;//p->next只想p->next->next(核心)
delete q;//删除临时结点(只改变p的指针即可,与q无关)
return true;
}
//输出所有学生的信息
void DisplayList(LinkList L)
{
LNode *p=L->next;
while§{
cout<<"----学号:"<data.id<<"-----分数:"<data.score;
p=p->next;
}
}
int ListLength(LinkList L)
{
LinkList p=L;
int i=0;
while(p->next!=NULL)
{
p=p->next;
i++;
}
return i;
}
//头插法创建列表
void CreatList_H(LinkList &L,int n)
{
L=new LNode;
L->next=NULL;//创建一个头节点
for(int i=0;i<n;++i)
{
LNode *p;
p=new LNode;
cin>>p->data.id;//输入元素赋给新节点的数据
cin>>p->data.score;
p->next=L->next;
L->next=p;//将p插入头结点之后
}
}
//链式线性表LA和LB的融合
void MergeList(LinkList &LA,LinkList LB)
{
int m;
int n;
m=ListLength(LA);
n=ListLength(LB);//获取LA,LB的长度
for(int i=1;i<=n;i++)
{
GetElem(LB,i,LB->data);//获取LB中第i个数据赋给e
if(!LocateElem(LA,LA->data))//在LA中找是否有和e相同的元素
ListInsert(LA,++m,LA->data);//将E插在LA的最后
}
}
//返回链式线性表的元素和数目
int main()
{
LNode *LA,*LB;
InitList(LA);
CreatList_H(LA,2);
DisplayList(LA);
cout<<"\n";

InitList(LB);
CreatList_H(LB,2);
DisplayList(LB);
cout<<"\n";

MergeList(LA,LB);
DisplayList(LA);

return 0;

}

4.顺序表与链式表的比较
——从空间性能
当线性表的长度变化不大,易于事先确定其大小时,为了节约存储空间,易采用顺序表作为存储结构。
——从时间性能
主要操作是和元素位置紧密相关的这类取值操作,很少做插入或删除时,宜采用顺序表做存储结构。
对于频繁进行插入或删除操作的线性表,宜采用链表作为存储结构。
—顺序表的长度变化不大,且能事先确定变化的范围。常按元素序号位置访问元素。
链式表长度变大较大,频繁进行插入删除操作。
—顺序表可借助数组来确定下表进行存取元素。
链式表依靠指针,其节点存取都要从头指针开始,顺链而行。
第三章 栈和队列
1.栈和队列的定义和特点
栈是限定在表尾进行插入或删除操作的线性表。表尾端称为栈顶,表头端称为栈底。不含元素的空表称为空栈。栈的修改是按“后进先出”的原则进行。
队列和栈相反,是一种“先进先出”的线性表,它只允许在表的一段插入,而在另一端删除元素。在队列中允许插入的一端称为队尾,允许删除的一端称为对头。
2.顺序栈的表示和实现
顺序栈是指利用顺序存储结构实现的栈,即利用一组连续的地址单元依次存放到栈顶的数据元素。top=0/top=base为空栈。
栈底指针base始终指向栈底的位置,若base的值为null,表示栈结构不存在。top为栈顶指针,其初值指向栈底。每当插入新的栈顶元素时,指针top增1;删除栈顶元素时,指针top减一。栈非空时,top始终指向栈顶元素的上一位置。
顺序栈的初始化操作就是为顺序栈动态分配一个预定义大小的数组空间。
2.5#include<iostream.h>
#define MaxSize 100
typedef struct
{
char data;
}ElemType;

typedef struct
{//顺序栈的存贮结构
ElemType *base;//栈底指针
ElemType *top;//栈顶指针
int stacksize;//站可用的最大容量
}SqStack;

bool InitStack(SqStack &s)
{//顺序栈的初始化
s.base=new ElemType[MaxSize];//为顺序栈分配一个最大容量为MaxSize的存贮空间
if(!s.base)
return false;
s.top=s.base;//初始空战
s.stacksize=MaxSize;
return true;
}

bool EmptyStack(SqStack &s)//判断是否为空栈
{
if(s.base==s.top)
return true;
return false;
}

bool Push(SqStack &s,ElemType e)
{//入栈
if(s.top-s.base==s.stacksize)//沾满
return false;
*s.top++=e;//准备将e往上压,top指针++
return true;
}

bool Pop(SqStack &s,ElemType &e)
{//出栈
if(s.top-s.base==s.stacksize)//沾满
return false;
e=*–s.top;//栈顶元素给e,栈顶指针-1
return true;
}

3.链栈的表示和实现
链栈是指采用链式存储实现的栈。通常链栈用单链表表示。
栈的主要操作是再栈顶的插入和删除,显然以链表的头部作为栈顶是最方便的。
3.5typedef struct
{//队列的存贮结构
ElemType *base;
int front,rear;
}SeQueue;

bool InitQueue(SeQueue &Q)
{
Q.base=new ElemType[MaxSize];
if(!Q.base)
return false;
Q.front=0;
Q.rear=0;//头尾指针都为0,对列为空
}

bool EmptyQueue(SeQueue &q) //判断是否为空列
{
if(q.rear==q.front)
return true;
return false;
}

bool EnQueue(SeQueue &Q,ElemType e)
{//入队
if((Q.rear+1)%MaxSize==Q.front)
return false;
Q.base[Q.rear]=e;//新元素加在队尾
Q.rear=(Q.rear+1)%MaxSize;//队尾指针+1
return true;
}

bool DeQueue(SeQueue &Q,ElemType &e)
{//出队
if(Q.front==Q.rear)
return false;
e=Q.base[Q.front];//新元素插入队尾
Q.front=(Q.front+1)%MaxSize;//队尾指针+1
return true;
}

4.栈与递归
栈的一个重要应用是在程序设计语言中实现递归。递归算法通常把一个大型复杂的问题描述和求解变得简洁清晰。
所谓递归是指,若在一个函数、过程或者数据结构定义的内部又直接(或间接)出现定义本身的应用,则称他们为递归。
5.循环队列
队列的操作与栈类似,不同的是,删除是在表的头部进行。队列也有两种存储方式,顺序表示和链式表示。
和顺序栈类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次有序存放从对列头到队列尾之外,尚需附设两个整型变量front和rear分别指队列头和队列尾的位置(即头指针和尾指针)。
初始化创建空队列时,令front=rear=0,每当插入新的队列尾元素时,尾指针rear+1,头指针front+1;因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置。
解决“假溢出”的方法—循环队列
少用一个元素空间,即队列空间大小为m时,有m-1个元素就认为是队满。
队空的条件:Q.front=Q.rear(头尾指针的值相同)
队满的条件:(Q.rear+1)%MAXSIZE=Q.front(尾指针在循环意义上+1等于尾指针)
6.链队列
指链式存储结构实现的队列。
**附上循环队列和栈实现递归的题目和代码:
3.在main函数中测试栈和队列:
先持续将’C’,’h’,’i’,’n’,’a’进行压栈;
然后循环直到栈不为空:
执行:Pop(S, e);EnQueue(Q, e);
然后循环直到队列部位空:
DeQueue(Q,e);cout<<e;
请截图显示输出的内容。

4.利用栈实现非递归方式来计算递归函数:
F(n)=F(n-1)*n 当n>1;
= 1 当n=1;

计算思路,参考练习8中的同内容填空题。提示:
(1)当n大于1时重复执行:则先将n压栈;然后n=n-1;
(2)当n=1时,记录F=1(对应于F(1)=1);
(3)重复执行当栈不为空时:出栈,设栈顶元素为e,则更新:F=F*e;

请实现上述代码。在main函数中,输出F(6)的值。
3.int main()
{
SqStack s;
ElemType e;
InitStack(s);
SeQueue q;
InitQueue(q);
e.data=‘c’;
Push(s,e);
e.data=‘h’;
Push(s,e);
e.data=‘i’;
Push(s,e);
e.data=‘n’;
Push(s,e);
e.data=‘a’;
Push(s,e);

while(!EmptyStack(s))
{
  Pop(s,e);
  EnQueue(q,e);
}

while(!EmptyQueue(q))
{
	DeQueue(q,e);
	cout<<e.data<<" ";
}
return 0;

}
4. int main()
{
SqStack s;
ElemType e;
InitStack(s);
int f,n;
e.data=6;
Push(s,e);
e.data=5;
Push(s,e);
e.data=4;
Push(s,e);
e.data=3;
Push(s,e);
e.data=2;
Push(s,e);
e.data=1;
Push(s,e);

f=1;
while(!EmptyStack(s))
{
	Pop(s,e);
	n=e.data;
	f=f*n;
}
cout<<f;
return 0;

}

第五章 树和二叉树
1.树的定义和基本术语
树结构是一类重要的非线性数据结构。
树是n(n≥0)个结点的有限集,它或为空树(n=0);或为非空树T:有且仅有一个称之为根的结点;除根结点外其余结点可分为m(m大于0)个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树。
结点:树中的一个独立单元
结点的度:结点拥有的子树称为结点的度
树的度:树的度是树内各结点度的最大值
叶子:度为0的结点称为叶子或终端结点
输的深度:树中结点的最大层次称为树的深度或高度
2.二叉树的定义
二叉树是n(n>=0)个结点所组成的集合,它或为空树(n=0);或为非空树,对于非空树T:有且仅有一个称之为根的结点;除根结点以外其余结点分为两个互不相交的子集T1,T2,分别称为T的左子树和右子树,且T1,T2本身又是二叉树。
二叉树与树一样具有递归性质,二叉树与树的区别主要为:
1. 二叉树每个结点至多只有两颗子树(即二叉树中不存在度大于2的结点)
2.二叉树的子树有左右之分,其次序不能任意颠倒。
3.二叉树的5个性质
①在二叉树的第i层上至多有2^(i-1)个结点
②深度为k的二叉树至多有2^k-1个结点(k>=1)
③对任何一个二叉树T,如果其终端结点数(叶子结点)为n0,度为2的结点数为n2,则n0=n2+1,
④具有n个结点的完全二叉树的深度为log[2n]+1
⑤如果对一棵n个结点的完全二叉树(其深度为[log2n]+1)的结点按层序编号,从第一层到第[log2n]+1层,每层从左到右,则对任一结点i,有:
①i=1,无双亲;i>1,双亲结点是i/2。
②2i>n,则结点i无左孩子。
③2i+1>n,则结点i无右孩子。
4.n个结点的二叉树的形态
Cn=(2n)!/n!(n+1)!
n=1,形态有1种
n=2,形态有2种
n=4,形态有14种
5.树、二叉树和森林的相互转换
将树转换成二叉树的步骤是:
(1)加线。就是在所有兄弟结点之间加一条连线;
(2)抹线。就是对树中的每个结点,只保留他与第一个孩子结点之间的连线,删除它与其它孩子结点之间的连线;
(3)旋转。就是以树的根结点为轴心,将整棵树顺时针旋转一定角度,使之结构层次分明。

将森林转换为二叉树的步骤是:
(1)先把每棵树转换为二叉树;
(2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子结点,用线连接起来。当所有的二叉树连接起来后得到的二叉树就是由森林转换得到的二叉树。

二叉树转换为树是树转换为二叉树的逆过程,其步骤是:
(1)若某结点的左孩子结点存在,将左孩子结点的右孩子结点、右孩子结点的右孩子结点……都作为该结点的孩子结点,将该结点与这些右孩子结点用线连接起来;
(2)删除原二叉树中所有结点与其右孩子结点的连线;
(3)整理(1)和(2)两步得到的树,使之结构层次分明。

6.线索二叉树
若结点有左子树,则其lchild域指示其左孩子,否则令lchild指示其前驱;若结点有右子树,则其rchild域指示其右孩子,否则令rchild指示其后继;
其中指向结点前驱和后继的指针,叫做线索。加上线索的二叉树称之为线
索二叉树。对二叉树以某种次序遍历使其变为线索的过程叫做线索化0。
7.哈夫曼树及其应用
哈夫曼树又称最优树,是一类带权路径最短的树。
第六章 图
在线性表中,数据元素之间只有线性关系,每个数据元素只有一个直接前驱和一个直接后继;在树形结构中,数据元素之间有明显的层次关系,并且每一层的数据元素可能和下一层中的多个元素(及其孩子结点)相关,但只能和上一层中一个元素(及其双亲结点)相关;在图结构中,结点之间的关系可能是任意的,图中任意两个数据元素之间都可能相关。
1.图的定义和基本属于
对于图G,若边集E(G)为有向边的集合,则称该图为有向图;若边集E(G)为无向边的集合,则称改图为无向图。
权和网:在实际应用中,每条边可以标上具有某种含义的值,该数值称为该边上的权(比如这些权可以表示一个顶点到另一个顶点的距离或耗费)。这种带权的图通常称为网。
度、入度和出度:顶点v的度是指和v相关联的边的数目,记为TD(v)。入度是以顶点v为头的弧的数目,记为ID(v);出度是以顶点v为尾的弧的数目,记为OD(v)。
有向树和生成森林:有一个顶点的入度为0,其余顶点入度为1的图称为有向树。一个有向图的生成森林是若干个有向叔组成。
2.邻接矩阵(图的存储结构)
邻接矩阵表示顶点之间相邻关系的矩阵。
图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息

优缺点:便于判断两个顶点之间是否有变,即根据A[i][j]=0/1来判断;便于计算各个顶点的度。。。缺点:不便于增加和删除顶点;不便于统计边的数目,需要扫描邻接矩阵所有元素才能统计完毕,时间复杂度为O(n^2);空间复杂度高。
3.邻接表(图的存储结构)
邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。

优缺点:优点便于增加和删除结点;便于统计边的数目,按定点表顺序扫描所有的边表可得到边的数目,时间复杂度为O(n+e);空间效率高。。。缺点不便于判断顶点之间是否有边;不便于计算有向图各个顶点的度。
4.深度优先搜索(图的遍历)
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。
实现原理:
深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
实现过程:

图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束)
5.普利姆算法(图的应用:最小生成树)
附上csdn对应链接!:
https://blog.csdn.net/jiangguangchao/article/details/102696501?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160765076019725211939086%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160765076019725211939086&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-102696501.first_rank_v2_pc_rank_v29&utm_term=%E6%99%AE%E5%88%A9%E5%A7%86%E7%AE%97%E6%B3%95&spm=1018.2118.3001.4449
6.克鲁斯卡尔算法(图的应用:最小生成树)
附上csdn对应链接!:
https://blog.csdn.net/qq_41819988/article/details/100130549?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160765045919725222446011%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160765045919725222446011&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-100130549.first_rank_v2_pc_rank_v29&utm_term=%E5%85%8B%E9%B2%81%E6%96%AF%E5%8D%A1%E5%B0%94%E7%AE%97%E6%B3%95&spm=1018.2118.3001.4449
7.拓扑排序(图的应用)
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。
先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。
一直做改操作,直到所有的节点都被分离出来。
如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。
下面是算法的演示过程。

第七章 查找
1.查找的基本概念
“查找表”是由同一类型的元素(或记录)构成的集合。
“关键字”是数据元素(或记录)中某个数据项的值,用它可以表示一个数据元素(或记录)。
“查找”是指根据给定的某个值,在查找表中确定一个其关键字等于给定值的记录或数据元素。
若在查找的同时对表做修改操作(如插入和删除),则相应的表称之为”动态查找表”;否则称之为“静态查找表”
为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值,称之为查找算法在查找成功时的“平均查找长度”。
2.线性表的查找
①顺序查找
过程为:从表的一端开始,依次将记录的关键字和给定值进行比较,若某个记录的关键字和给定值相等,则查找成功;反之,若扫描整个表后,仍未找到关键字和给定值相等的记录,则查找失败。
顺序查找方法既适用于线性表的顺序存储结构,又适用于线性表的链式存储结构。
②折半查找
过程为:从表的中间记录开始,如果给定值和中间记录的关键字相等,则查找成功;如果给定值大于或小于中间记录的关键字,则在表中大于或小于中间记录的那一半中查找,这样重复操作,直到查找成功,或者在某一步中查找区间为空。
折半查找也称二分查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采取顺序存储结构,而且表中元素按关键字有序排列。
③分块查找
又称索引顺序查找,这是一种性能介于顺序查找和折半查找之间的一种查找方法。
3.二叉排序树(树表的查找)
又称二叉查找树,它是一种对排序和查找都很有用的特殊二叉树。
其性质:若它左子树不空,则左子树上所有
结点的值均小于它根结点的值;
若它右子树不空,则右子树上所有
结点的值均大于它根结点的值;
它的左右子树也分别为二叉排序树
由定义可得出:中序遍历一颗二叉树时可以得到一个结点值递增的有序序列
4.散列表的查找
如果能在元素的存储关系和其关键字之间建立某种直接关系,那么在进行查找时,就无需比较很多次比较,按照这种关系直接由关键字找到相应的记录。这就是散列查找法的思想,他通过对元素关键字进行某种运算,直接求出元素的地址,即使用关键字到地址的直接转换方法,而不需要反复比较。
在记录的存储位置p和其关键字key之间建立一个确定的对应关系系H,使p=H(key),称这个对应关系H为”散列函数”,p为”散列地址”。
”散列表”为一个有线的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标。
5.各查找方法的特点及使用情况

第八章 排序
1.排序的基本概念
排序是按关键字的非递减或非递增顺序对一组记录重新进行排列的操作。
排序的一个主要目的是便于查找,有序的顺序表可以使用查找效率较高的折半查找,又如创建树表的过程本身就是一个排序的过程

2.快速排序(交换排序)

3.简单选择排序(选择排序)

4.不同排序的复杂度
排序方法 时间复杂度 空间复杂度
直接插入排序 O(n^2) O(1)
折半插入排序 O(n^2) O(1)
希尔排序 O(n^1.3) O(1)
冒泡排序 O(n^2) O(1)
简单选择排序 O(n^2) O(1)
快速排序 O(nlog2n) O(log2n)
堆排序 O(nlog2n) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值