数据结构总结
- 第一章 概述
- 1.1 基本概念和术语
- 1.2 数据结构
- 1. 2.1 逻辑结构
- 1.2.2 存储结构
- 1.3 数据类型和抽象数据类型
- 1.3.1 数据类型
- 1.3.2 抽象数据类型
- 1.4 算法和算法分析
- 1.4.1 算法的定义及特性
- 1.4.2 评价算法优劣的基本标准
- 1.4.3 算法的时间复杂度
- 1.4.4 算法的空间复杂度
- 第二章 线性表
- 2.1 线性表的定义
- 2.2 线性表的顺序表示和实现
- 2.2.1 顺序表中的基本操作的实现
- 2.3 线性表的链式表示和实现
- 2.3.1 单链表的定义和表示
- 2.3.2 循环链表
- 2.3.3 双向链表
- 2.4 顺序表和链表的比较
- 第三章 栈和队列的定义和特点
- 3.1 栈的定义和特点
- 3.2 顺序栈的表示和实现
- 3.3链栈的表示和实现
第一章 概述
现在的计算机存储着处理字符、表格和图像等具有一定结构的数据,但是呢,要分清数据的内在联系,合理地组织数据,高效地处理数据,这就是“数据结构”主要研究的问题。
1.1 基本概念和术语
-
数据
是客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。 -
数据元素
是数据的基本单位,在计算机中通常作为一个主题进行考虑和处理。有些情况下,数据元素也称为元素、记录。 -
数据项
是组成数据元素的、有独立含义的、不可分割的最小单位。 -
数据对象
是性质相同的数据元素的集合,是数据的一个子集。
1.2 数据结构
数据结构是相互之间存在一种或几种特定关系的数据元素的集合。这样子理解,数据结构是带“结构”的数据元素的集合,“结构”就是数据元素之间存在的关系。结构可分为逻辑结构和物理结构。
1. 2.1 逻辑结构
数据的逻辑结构有两个要素:一是数据元素,二是关系。数据元素的含义如前所述,关系是指数据元素之间的关系。
通常有四类基本结构:
- 集合结构
- 线性结构
- 树结构
- 图结构或网状结构
其中集合结构、树结构和图结构都属于非线性结构。
1.2.2 存储结构
数据对象再计算机中的存储表示称为数据的存储结构,也称为物理结构。
- 顺序存储结构
- 链式存储结构
1.3 数据类型和抽象数据类型
1.3.1 数据类型
数据类型是高级程序设计语言中的基本概念,是一个值的集合和定义在这个值集上的一组操作的总称。
数据类型反映了程序设计语言的数据描述和处理能力。
1.3.2 抽象数据类型
抽象是抽取出实际问题的本质。
抽象数据类型一般指由用户定义的、表示应用为标题的数学模型,以及定义在这个模型上的一组操作的总称。具体包括三个部分:数据对象、数据对象上的关系的集合以及对数据对象的基本操作的集合。
ATD 抽象数据类型名{
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}ATD 抽象数据类型名
1.4 算法和算法分析
在涉及运算时,总要联系到该算法处理的对象和结果的数据。为了描述实习某些操作,常常需要设计算法,因而算法时研究数据结构的重要途经。
1.4.1 算法的定义及特性
算法是为了解决某类问题而规定的一个有限长的操作序列。
五个特性:
- 有穷性
- 确定性
- 可行性
- 输入
- 输出
1.4.2 评价算法优劣的基本标准
- 准确性:在合理的数据输入下能够在有限的运行时间内得到准确的结果。
- 可读性:便于人们理解和相互交流,方便调试和修改
- 健壮性:当输入的数据非法时,能够做出准确的反应或进行处理
- 高效性:高效性包括时间和空间两个方面
1.4.3 算法的时间复杂度
算法效率分析的目的是看算法实际是否可行,并在同一问题存在多个算法中可进行时间和空间性能上的比较,以便从中挑选出较忧算法。
- 问题的规模是算法求解问题输入量的多少,是问题大小的本质。
- 语句频度是指一条语句的重复次数。
一般情况下,算法中基本语句重复执行的次数是问题规模 n 的某个函数 f(n),算法的时间度量记作:
T(n) = O(f(n))
表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度。
称算法在最好情况下的时间复杂度为最好的时间复杂度,指的是算法计算量可能达到最小值;
称算法在最坏情况下的式按键复杂度为最坏时间复杂度,指的是算法的计算量是最大值;
1.4.4 算法的空间复杂度
类似于算法的时间复杂度,采用渐近空间复杂度作为算法所需存储空间的度量,简称空间复杂度。记作:
S(n) = O(f(n))
一般情况下,一个程序在机器上执行时,除了需要寄存本身所用的指令、常数
变量和输入数据外,还需一些对数据进行操作的辅助存储空间。
第二章 线性表
线性结构的基本特点时除了第一个元素无直接前驱,最后一个元素无直接后继之外,其他元素都有一个前驱和一个后继。
2.1 线性表的定义
由n(n>0)个数据特性相同的元素构成的有限序列称为线性表
特点有:
- 存在唯一的一个被称为“第一个”的数据元素
- 存在唯一的一个被称为“最后一个“的数据元素
- 除了第一个之外,结构中的每一个数据元素均只有一个前驱
- 除了最后一个之外,结构中的每一个数据元素均只有一个后继
2.2 线性表的顺序表示和实现
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,这种也称作线性表的顺序存储结构或顺序映像。线性表的特点是,逻辑上相邻的数据元素,其物理次序也是相邻的
//顺序表的存储结构
#define MAXSIZE 100
typedef struct
{
ElemType *elem; //存储空间的基地址
int length; //当前长度
}SqList; //顺序表的结构类型为SqList
2.2.1 顺序表中的基本操作的实现
- 初始化
顺序表的初始化操作就是构造一个空的顺序表- 为顺序表L动态配一个预定义大小的数组空间,使elem指向这段空间的基地址
- 将表的当前长度设为0
Status InitLIst(SqList & L)
{ //构造一个空的顺序表
L.elem = new ElemType[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的数组空间、
if(!elem) exit (OVERFLOW); //存储失败退出
L.length = 0; //空表长度为0
return ok;
}
- 取值
取值操作时根据指定的位置序号 i ,获取顺序表中第i 个数据元素的值- 判断指定的位置序号 i 值是否合理(1 <= i <= L.length),若不合理,则返回ERROR
- 若i值合理,则将第i个数据元素L.elem[i-1]赋值给参数e,通过e返回第i个元素的传值。
Status GetElem(SqList L,int i,ElemType &e)
{
if (i<1 || i>L.length) return ERROR; //判断i值是否合理,
e = L.elem[i-1]; //elem[i-1]单元存储第i个数据元素
return ok;
}
- 查找
查找操作时根据指定元素值e,查找顺序表中第1个与e相等的元素,若查找成功,则返回该元素在表中的位置序号,- 重第一个元素其,依次和e1相比较,若找到与e1相等的元素L.elem[i],则查找成功,则返回该元素的序号i + 1
- 若查遍了这个顺序表都没有找到,则返回0
int LocatElem(SqList L,ElemType e)
{
for(i=0;i<length;i++)
if(L.elem[i == e) rteurn i+1;
return o;
}
- 插入
在表中的第i 个位置插入一个新的数据元素e,使长度为n的线性表变成长度为n+1的线性表- 判断插入位置i是否合法,(1<= i <= n+1),若不合法则返回ERROR,
- 判断顺序表的存储空间是否已满,若满则返回ERROR,
- 将第n个元素至第i个位置的元素一次向后移动一个位置,空出第i个位置
- 将要插入的新元素e放入第i个位置
- 表长加1
Status ListInser(SqList &L,int i,ElemType e)
{
if((i<1)||(i>L.length+1)) return ERROR; //i值不合法
if(L.length == MAXSIZE) return ERROR; //当前存储空间已满
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1] =L.elem[j]; //插入位置及之后的元素后移
L.elem[i-1e; //将新元素e放入第i个元素
++L.length; //表长加1
return ok;
}
- 删除
删除操作是将表的第i个元素删除,将长度为n的线性表变成长度n-1的线性表- 判断删除位置i是否合法
- 将第i+1个至第n个元素依次向前移动一个位置
- 表长减1
Status ListDelete(SqList &L,int i)
{
if((i<1)|| (i>L.length)) return ERROR;
for(j=i;j<=.length-1;j++)
L.elem[j-1] = L.elem[j];
--L.elemth;
return ok;
}
2.3 线性表的链式表示和实现
2.3.1 单链表的定义和表示
线性表链式存储结构的特点:用一组任意的存储单元存储线性表的数据元素。
- 初始化
构造一个空表- 生成新结点作为头结点,用头指针L指向头结点
- 头结点的指针域置空
Status IntList(LinkList &L)
{
L = new LNode;
L->next = NULL; //生成新结点作为头结点
return ok; //头结点的指针置空
}
- 取值
和顺序表不同,链表中逻辑相邻的结点并没有存储在物理相邻的单元中,所以只能从链表的首元结点出发,顺着链域next依次向下访问。- 用指针p指向首元结点,用j做计数器初始化赋为1
- 从首元结点开始依次顺着链域向下访问,只要当前结点的指针p不为空,则循环执行操作。
- 退出循环。
Status GetElem(LinkList L,int i, ElemType &e)
{
p = L->next ; //初始化,p指向首元结点,
j=1; //计数器j初值赋为1
while(p&&j<i) //顺链域向后扫描,直到p为空或p指向第i个元素
{
p=p->next; //p指向下一个节点
++j; //计数器加1
}
if(!p||j>i) return ERROR; //i值不合法i>n或i<0
e = p->data; //去第i个节点的数据域
return ok;
}
- 查找
从链表的首元节点出发,依次将结点值和指定值e进行比较,返回查找结果- 用指针p指向首元结点
- 从首元结点开始依次顺着链域next向下查找,只要p部位空就会一直循环。
- 返回p
LNode *LocateElem(LinkList L,ElemType e)
{
p = L->next; //初始化,p指向首元结点
while(p && p->data!=e) //顺着链域扫描,直到p为空或p所指的的数据域等于e
p=p->next; //p指向下一个结点
return p;
}
- 插入
s->next = p->next; p->next=s- 查找结点a(i-1)并由指针p指向该结点。
- 生成一个新结点*s
- 将新结点*s的指针域置为e
- 将新结点*s的指针域指向结点a(i)
- 将结点p1的指针域指向新结点s
Status ListInsert(LinkList &L,int i ,ElemType e)
{
p=L;j=0;
while(p&& (j<i-1))
{
p = p ->next; //查找第i-1个结点,p指向该结点
++j;
}
if(!p || j>i-1) return ERROR; //i > n+1 或者 i<1
s = new LNode; //生成新结点*s
s ->data = e; // 将结点*s 的数据域置为e
s ->next = p->next; //将结点*s的数据域指向结点a(i)
p ->next =s; //将结点*p的指针域指向结点*s
return ok;
}
- 删除
同插入一样,首先找到该位置的前驱结点,修改语句如下:
p ->next = p ->next ->next;- 查找结点a(i)-1并由指针p指向该结点
- 临时保存待删除结点a(i)的地址在q中,以备释放
- 将结点*p的指针域指向a(i)的直接后继节点
- 释放节点啊(i)的空间
Status ListDelete (LinkList &L,int i)
{
p = L;j=0;
while((p ->next) && (j<i-1)) //查找第i-1个结点,p指向该结点
{
p =p ->next; ++j;
}
if(!
(p ->next) || (j>i-1)) rteurn ERROR;
q = p ->next;
delete q;
return ok;
}
- 创建单链表
根据结点插入位置不同,链表的创建方法可分为前插法和后插法
前插法:创建一个只有头结点的空链表,
根据待创建链表包括的元素个数n,循环n次执行操作:生成一个新结点,p,输入元素赋值给新结点p的数据域,将新结点p插入到头结点之后。
后插法:创建一个只有头结点的空链表,尾指针r初始化,指向头结点,根据链表包括的元素个数n,循环n次执行操作:生成一个新结点p,输入元素赋值给新结点p的数据域,将新结点p插入到尾结点,尾指针r指向新的尾结点*p
2.3.2 循环链表
循环链表市另一种形式的链式存储结构,其特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环,语句:
p = B ->next ->next;
B ->next = A ->next;
A ->next =p;
2.3.3 双向链表
在双向链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱
2.4 顺序表和链表的比较
比较项目 | 顺序表 | 链表 |
---|---|---|
存储空间 | 预先分配,会导致空间闲置或溢出现象 | 动态分配,不会导致空间闲置或溢出现象 |
存储密度 | 不用为表示结点间的逻辑关系而增加额外的存储开销,存储密度等于1 | 需要借助指针来体现元素间的逻辑关系,存储密度小于1 |
存储元素 | 随机存取,按位置访问元素的时间复杂度为O(1) | 顺序存储,按位置访问元素时间复杂度为O(n) |
插入、删除 | 平均移动约表中一半元素,时间复杂度为O(n) | 不需要移动元素,确定插入、删除位置后时间复杂度为O(1) |
适用情况 | 表变化不大,且 能事先确定变化的范围;很少进行插入或删除操作,经常按元素位置序号访问数据元素。 | 长度变化比较大,频繁进行插入或删除操作 |
第三章 栈和队列的定义和特点
3.1 栈的定义和特点
栈是限定于仅在表尾进行插入和删除操作的线性表,表尾称栈顶,表头叫栈尾,栈的修改是按先进先出的原则进行的。
3.2 顺序栈的表示和实现
顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。
- 初始化
为顺序栈动态分配一个预定义大小的数组空间- 为顺序栈动态分配一个容量为MAXSIZE的数组空间,使base指向这段空间的基地址,即栈底
- 栈顶指针top初始化为base,表示栈为空
- 置为栈的空间容量MAXSIZE
Statys IntStack(SqStack &s)
{
s.base = new SElemTyoe[MAXSIZE]; //动态分配数组
if(!S.base) exit(OVERFLOW); //存储动态失败
S.top = S.base; //top初始为base,空栈
S.stacksize = MAXSIZE; //栈的容量为,MAXSIZE
return ok;
}
- 入栈
在栈顶插入数据元素- 判断是否满,若满则返回ERRROR
- 将新元素压入栈顶,栈顶指针加1
Status Push(SqStack &S, SElemType e)
{
if(S.top-S.base == S.atacksize) ERROR;
*S.top++=;
return ok;
}
- 出栈
指将栈顶元素删除- 判断栈是否为空,
- 栈顶指针减1,栈顶元素出栈
Status Pop(SqStack &S,SElemTyep &e)
{
if(S.top ==S.base) return ERROR;
e = *--S.top;
return ok;
}
- 取栈元素
SElemType GetTop(SqStack S)
{
if(S.top!= S.base)
return *(S.top-1);
}
3.3链栈的表示和实现
采用链式存储结构实现的栈。
- 初始化
就是构造一个空栈。
Status IntStack(LinkStack &s)
{
S= NULL;
return ok;
}
- 入栈
链栈入栈前不需要判断栈是否满,只需要动态分配一个结点空间,- 为入栈元素e分配空间,用指针p指向
- 将新结点数据域置为e
- 将新结点插入栈顶
- 修改栈顶指针为p
Stataus Push(LinkStack &S,SElemType e)
{
p = new StackNode; //生成新结点
p -> data =e; //将新结点数据域置为e
p ->next = S; //将新结点插入栈顶
S = p; //修改栈顶元素指针为p
return ok;
}
- 出栈
- 判断栈是否为空
- 将栈顶元素赋给e
- 临时保存栈顶元素的空间,以备释放
- 修改栈顶指针,指向新的栈顶元素
- 释放原栈顶元素的空间
Stataus Push(LinkStack &S,SElemType e)
{
if(S == NULL) RETURN ERROR;
e = S ->data;
p = S ;
S =S ->next;
delete p;
return ok;
}
ps:现在先写到这,后面会继续完成编写工作,谢谢各位点进来学习交流。