第一章、绪论
1、一些概念
-
数据:描述客观事物的数值,字符,以及能输入机器且能被处理的各种符号的集合。
-
数据元素(行):数据元素是组成数据的基本单位,是数据集合的个体。
-
数据对象(列):数据对象是性质相同的数据元素的集合。
-
数据结构:数据结构是指相互之间存在一种或多种特定关系(存储关系、逻辑关系)的数据元素的集合。我们关心的数据元素之间的相互关系与组织方式,以及对其施加运算及运算规则。
-
数据类型:数据类型是一组性质相同的值集合以及定义在这个值集上的一组操作的总称。
-
C语言中的指针类型为原子类型还是
结构类型
数据抽象与抽象数据类型:
- 数据的抽象:高级语言中提供整形、实行、字符、记录、文件、指针等多种数据类型,课利用这些类型构造出像栈、队列、树、图等复杂的抽象数据类型。
- 抽象数据类型(ADT):特征:1. 数据抽象 2.信息隐蔽
2、数据结构的内容
数据元素之间的相互关系具体应包括三个方面:数据的逻辑结构、数据的存储结构、数据的运算集合
-
逻辑结构
- 集合结构:结构中数据元素同属于一个集合。
- 线性结构:结构中数据元素间存在一对一的线性关系。
- 树形结构:结构中数据元素间存在一对多的层次关系。
- 图形结构(网状结构):结构中数据元素间存在多对多的任意关系。
-
存储结构
存储结构主要分为顺序存储和链式存储两种方式。
-
运算集合(操作)
3、算法
算法的定义
算法是规则的有限集合,是为解决特定问题而规定的一系列操作。
算法的特性
-
输入:在算法中可以有零个或者多个输入
-
输出:在算法中至少有一个或者多个输出
-
有限性:在执行有限的步骤之后,自动结束不会出现无限循环并且每一个步骤在可接受的时间内完成
-
确定性:算法的每一个步骤都具有确定的含义,不会出现二义性
-
可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限的次数完成
4、时间复杂度
第二章、线性表
1、线性表的顺序存储
顺序表中插入元素(需知道具体过程)
顺序表中删除元素(需知道具体过程)
2、线性表的链式存储
1.初始化单链表
//存储结构定义
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList
/*
H是指向单链表的头指针的指针(二级指针)
*H相当于主程序中待初始化单链表的头指针变量(一级指针)
*/
InitList(LinkList *H)
{
*H = (Linklist)malloc(sizeof(Node)); //建立头结点
(*H) -> next = NULL; //建立空的单链表H
}
2.头插法建立单链表
void CreateFromHead(LinkList H)
{
Node *s;
char c;
int flag = 1 ;
while(flag)
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
s->next=H->next;//与插入类似,先连接后面的链
H->next=s;
/*
插入操作:
s->next=pre->next;
pre->next=s;
*/
}
else flag=0;
}
}
3.尾插法建立单链表
void CreateFromTail(LinkList H)
{
Node *s,*r;
int flag = 1;
r=H;
while(flag)
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
r->next=s;
r=s;
}
else flag=0;
r->next=NULL;
}
}
4.在单链表L中查找第i个结点
1)按序号查找
Node *Get(LinkList L,int i)
{
int j;
Node *p;
p=L;j=0;
while(p->next!=NULL&&j<i)
{
p=p->next;
j++;
if(i==j) return p;
else return NULL;
}
}
2)按值查找
Node *Locate(LinkList L,ElemType key)
{
Node *p;
p=L->next; //从表中第一个元素比较
while(p!=NULL)
{
if(p->data!=key)
{
p=p->next;
}
else break;
return p;
}
}
3、循环链表
概念:循环链表是单链表的另一种形式,是一个首尾相接的链表。其特点为将单链表最后一个结点的指针域由NULL改为指向头结点或线性表相接的链表。
判断普通链表与循环链表是否为空:
- 普通链表(带头结点):p->next==NULL
- 循环链表:p->next=L || (L->nxet=L)
4、双向链表
必须知道的:p->prior->next = p = p->next->prior
双向链表前插操作:
//核心代码
s->data = e;
s->prior =p->prior;//1
p->prior->next=s;
s->next=p;
p->prior=s;//4
//4不能在1前面
5、顺序表和链表的比较
1)顺序表:顺序表的特点是逻辑上相邻的数据元素,物理存储位置也相邻,并且顺序表的存储空间需要预先分配。
它的优点是:
a) 方法简单,各种高级语言中都有数组,容易实现。
b) 不用为表示节点间的逻辑关系而增加额外的存储开销。
c) 顺序表具有按元素序号随机访问的特点。
缺点:
a) 在顺序表中做插入、删除操作时,平均移动表中的一半元素,因此n较大的顺序表效率低。
b) 静态分配,程序执行之前必须明确规定存储规模预先分配足够大的存储空间,估计过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。
2)链表:
在链表中逻辑上相邻的数据元素,物理存储位置不一定相邻,它使用指针实现元素之间的逻辑关系。并且,链表的存储空间是动态分配的。
优点:
插入、删除运算方便。
缺点:
a) 要占用额外的存储空间存储元素之间的关系,存储密度降低。存储密度是指一个节点中数据元素所占的存储单元和整个节点所占的存储单元之比。
b) 链表不是一种随机存储结构,不能随机存取元素。
习题
-
顺序表中 ,逻辑上相邻的元素,其物理位置( 一定)相邻,在单链表中,逻辑上相邻的元素,其物理位置( 不一定)相邻。
-
在带头结点的非空单链表中,头结点的存储位置由( 头指针 L )指示,首元素存储结点的位置由( L->next )指示,除首元素结点外,其他任一元素结点的存储位置由( next )指示。
-
描述头指针,头结点,首元素结点这三个概念的区别
1)头指针是指向链表中第一个结点的指针。若链表中附设头结点,则不管线性表是否为空表,头指针均不为空,否则表示空表的链表的头指针为空。
2)头结点是为了操作方便,在链表的首元结点之前附设的一个结点,该结点的数据域中不存储线性表的数据元素,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理。
3)首元结点是指链表中存储线性表中第一个数据元素a1的结点。
-
在头指针为L的单循环链表中,判定只有一个结点的条件是( L->next=L )
-
在一个单链表中删除p所指结点时,应执行以下操作:
q=p->next;
p->data=p->next->data;
p->next=( q->next 或 p->next->next );
free(q);
第三章:栈和队列
1、顺序栈
通常以top=-1表示空栈
2、队列
1)链队列
1.链队列定义:
#define TRUE 1
#define FALSE 0
#define QueueElementType char
typedef struct Node
{
QueueElementType data;
struct Node *next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode *front;//队头指针
LinkQueueNode *rear; //队尾指针
}LinkQueue;
2.初始化操作
int InitQueue(LinkQueue *Q)
{
Q->front=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(Q->front!=NULL)
{
Q->rear=Q->front;
Q->front->next=NULL;
return(TRUE);
}else return(FALSE);
}
3.入队操作(*)
int EnterQueue(LinkQueue *Q,QueueElementType x)
{
LinkQueueNode *NewNode;
NewNode=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(NewNode!=NULL){
NewNode->data=x;
NewNode->next=NULL;
Q->rear->next=NewNode;
Q->rear=NewNode;
return(TRUE);
}else return(FALSE);
}
4.出队操作(*)
int DeleteQueue(LinkQueue *Q,QueueElementType *x)
{
LinkQueueNode *p;
if(Q->front==Q->rear)
return(FALSE);
p=Q->front->next;
Q->front->next=p->next;
if(Q->rear==p)
Q->rear=Q->front;
*x=p->data;
free(p);
return(TRUE);
}
2)循环队列
判空条件:rear=front
入队操作时,队尾指针变化是:rear = (rear+1)mod MAXSIZE
入队操作时,队头指针变化是:front = (front+1)mod MAXSIZE
循环队列真正队满的条件是:(rear+1) mod MAXSIZE= front
若进栈次序为123
则出栈 次序可为 123 132 213 231 321五种
第四章、串
1、串的定义及基本操作
串是零个或多个字符组成的有限序列,一般记为S。
n是串中字符的个数,称为串的长度。n=0时的串称为空串
由一个或多个空格组成的串称为空格串
基本操作:
- StrAsign(S,chars):生成一个值等于chars的串S
- StrInsert(S,pos,T): 1<=pos<=StrLength(S)+1;在第pos个字符之前插入串T
- StrDelete(S,pos,T): 从串中删除第pos个字符起长度为len的子串
- StrCat(S,T): 将串T的值连接在串S的后面
- StrIndex(S,pos,T):若串S中存在与串T相同的子串,返回他在串S中第pos个字符之后第一次出现的位置,否则返回0.
- StrReplace(S,T,V):用V替换串S中出现的所有与T相等的不重复的子串。
- SubString(Sub,S,pos,len):用sub返回串S的第pos个字符起长度为len的子串。
2、定位函数
StrIndex(SString s,int pos,SString t)
{
int i,j,start;
if(t.len==0) return(pos);//主串的任意位置都可以找到与空串匹配的空串
start = pos; i = start; j = 0;//主串从pos开始,模式串从头(0)开始
while(i<s.len&&j<t.len)
{
if(s.ch[i]==t.ch[j]){i++;j++}
else
{
start++; //当前对应字符不相等时,修改起始位置,重新开始匹配
i = start;
j=0;
}
if(j>=t.len) return(start);
else return(-1);
}
}
第五章、数组和广义表
1、二维数组任意元素地址计算
2、特殊矩阵的压缩存储
1)三角矩阵
下三角矩阵:i<j时 有 aij=0;
上三角矩阵:i>j时 有 aij=0;
对称矩阵:所有元素均满足 aij=aji;
下三角矩阵压缩为一维数组
Loc[i,j](位置) = Loc[1,1] + i(i-1)/2 + j - 1;
- i(i-1)/2 为前 i -1 行元素个数
- j-1 为第i行aij前非零元素的个数
对于上三角矩阵,将下三角矩阵的ij互换位置即可。
2)带状矩阵
- 前 i-1 行非零元素 : 3*(i-1)-1 + (j-1) +1
- Loc[i,j] = Loc[1,1] + 2(i-1) + j - 1
3)稀疏矩阵
- 原则上不属于特殊矩阵
3、广义表
1)广义表的表头和表尾
- 表头:第一个元素
- 表尾:除表头外的元素并且外面+()。并且广义表的表尾一定是一个表。
eg:
- D=() 空表; 长度为0。 表头表尾均为空
- A=(a,(b,c)) Head(A) = a, tail(A) = ((b,c))
- B=(A,A,D) Head(B) = A, tail(A,D) = (A,D)
- C=(a,C) Head© = a, tail( C ) = ( C )
2)广义表的深度
-
广义表的深度,可以通过观察该表中所包含括号的层数间接得到,如下示例,该广义表的深度为2
3)广义表的长度
广义表的长度,指的是广义表中所包含的数据元素的个数。
计算元素个数时,广义表中存储的每个原子算作一个数据,同样每个子表也只算作是一个数据。
- LS = {a1,a2,…,an} 的长度为 n;
- 广义表 {a,{b,c,d}} 的长度为 2;
- 广义表 {{a,b,c}} 的长度为 1;
- 空表 {} 的长度为 0。
4)广义表存储结构
1.头尾链表存储结构![在这里插入图片描述](https://img-blog.csdnimg.cn/51945546420b4dd58bea2cd988da14f4.png)
2.同层结点存储结构(扩展链存储结构)
第六章、树和二叉树
1、一些重要概念(性质)
- 树的深度=总层数 高度=深度-1
- 二叉树第i层上至多有2^(i-1)个结点(i >= 1)
- 深度为k 的二叉树至多有2^k-1个结点(k>=1)
- 任意一颗二叉树T,若终端结点数为n0,而其度为2的结点数为n2,则n0=n2+1.
- n=B+1 (总结点=分支数+1)
- 具有n个结点的完全二叉树的深度为log 2(底) n (向下取整) +1
2、二叉树,树,森林的转换
1)树转换为二叉树
- 兄弟相连留长子(树转换为二叉树,根节点无右子树)
2)二叉树转换为树
- 左孩右右连双亲
- 去掉原来右孩线
3)森林转换为二叉树
- 树变二叉根相连(森林转换为二叉树,根节点可能有右子树)
4)二叉树转换为森林
- 去掉全部右孩线
- 孤立二叉再还原
3、由遍历序列确定二叉树
1)先序遍历+中序遍历
2)后序遍历+中序遍历
4、哈夫曼树和哈夫曼编码
1)什么是哈夫曼树?
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,则称该二叉树为哈夫曼树,也被称为最优二叉树。
根据树的带权路径长度的计算规则,我们应该尽可能地让权值大的叶子结点靠近根结点,让权值小的叶子结点远离根结点,这样便能使得这棵二叉树的带权路径长度达到最小。
构建哈夫曼树就是反复选择两个最小的元素进行合并,直到只剩下一个元素为止。
2)哈夫曼编码的生成
对于任意一棵二叉树来说,把二叉树上的所有分支都进行编号,将所有左分支都标记为0,所有右分支都标记为1。我们就以7、5、4、2构建的哈夫曼树为例。
那么对于树上的任何一个结点,都可以根据从根结点到该结点的路径唯一确定一个编号。
对于哈夫曼树上的叶子结点,根据从根结点到该叶子结点的路径所确定的一个编号,就是该叶子结点的哈夫曼编码。
例如,上图中:
叶子结点7的哈夫曼编码为:0
叶子结点5的哈夫曼编码为:10
叶子结点4的哈夫曼编码为:111
叶子结点2的哈夫曼编码为:110
第七章、图
1、一些重要概念(性质)
- 不管是有向图还是无向图,所有的顶点的度相加之和等于边的二倍。
- 无向图中的极大连通子图称为该无向图的连通分量。
- 有向图中的极大连通子图称为该有向图的强连通分量。
- 一个连通图的生成树是指一个极小连通子图。
- 含有n个顶点,n-1条边的树不一定是生成树。
2、图的存储结构
1)邻接矩阵表示法
无向图:
- 无向图的邻接矩阵一定是一个对称矩阵。 因此,在实际存储邻接矩阵时只需存储上(或下)三角矩阵的元素。
- 对于无向图,邻接矩阵的第 i 行(或第 i 列)非零元素(或非∞元素)的个数正好是第 i 个顶点的度 TD(vi)。比如顶点 v 1的度就是 1 + 0 + 1 + 0 = 2
- 求顶点 v i的所有邻接点就是将矩阵中第i行元素扫描一遍, A [ i ] [ j ]为 1就是邻接点。
有向图:
- 主对角线上数值依然为0。但因为是有向图,所以此矩阵并不对称。
- 有向图讲究入度与出度,顶点 v 1的入度为1,正好是第 v 1 列各数之和。顶点 v 1的出度为2,即第 v 1 行的各数之和。
- 与无向图同样的办法,判断顶点 v i 到 v j是否存在弧,只需要查找矩阵中 A [ i ] [ j ] 是否为1即可。
对于带权图而言,若顶点 vi和 vj之间有边相连,则邻接矩阵中对应项存放着该边对应的权值。
2)邻接表
无向图:
有向图:
比较:
在邻接表中,给定一顶点,能很容易地找出它的所有邻边,因为只需要读取它的邻接表。在邻接矩阵中,相同的操作则需要扫描一行,花费的时间为 O ( n ) 。但是,若要确定给定的两个顶点间是否存在边,则在邻接矩阵中可以立刻查到,而在邻接表中则需要在相应结点对应的边表中查找另一结点,效率较低。
3)十字链表
十字链表是有向图的一种链式存储结构。
4)邻接多重表
邻接多重表是无向图的另一种链式存储结构。
3、图的遍历
1)DFS
2)BFS
4、图的连通性问题
构造最少生成树:
1)普里姆(Prim)算法(选择点)
2)克鲁斯卡尔(Kruskal)算法(选择边)
最小生成树可能不唯一
判断:
- 对于无向连通图,所有顶点的度之和为偶数。(√)
- 对于无向联通图,边数大于顶点个数减1。(×) e>=n-1
- 对于无向联通图,至少有一个顶点的度是1. (×) (环中所有的顶点的度都为2)
- 回路是简单路径。(×) 回路:起始点和终止点相同
选择:
此题需选判断是有向图还是无向图
第八章
1、概述
查找的基本方法可以分为两大类:比较式查找法和计算式查找法
1.比较式查找法:(基于线性表的查找法)
- 顺序查找法
- 折半查找法
- 分块查找法
比较式查找法:(基于树的查找法)
- 二叉排序树
- 平衡二叉排序树计算式查找法:
- HASH(哈西)法
第九章、内部排序
1、分类
- 插入类排序
- 直接插入排序
- 折半插入排序
- 表插入排序
- 希尔排序
- 交换类排序
- 冒泡排序
- 快速排序
- 选择类排序
- 简单选择排序
- 树形选择排序
- 堆排序
- 归并排序
- 分配类排序
- 多关键字排序
- 链式基数排序
- 基数排序的顺序表结构