数据结构与算法-1
2022.10.14
一、绪论
(1)数据:数据是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称;
(2)数据元素:数据元素是数据的基本单位,数据项是数据不可分割的最小单位,一个数据元素由若干个数据项组成;如:一本书的书目信息为一个数据元素,而书目信息中的每一项(如书名、作者名等)为一个数据项。
(3)数据对象:数据对象是性质相同的数据元素的集合,是数据的一个子集;如:正整数的数据对象是集合N={0,1,2,3,…}
(4)数据结构:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。根据数据元素之间关系的不同特性,通常有下列四类基本结构:
a.集合 结构中的数据元素之间除了“同属于一个集合”的关系外,别无其他的关系;
b.线性结构 结构中的数据元素之间存在一个对一个的关系;(线性表、栈、队列、串)
c.树形结构 结构中的数据元素之间存在一个对多个的关系;
d.图状结构或网状结构 结构中的数据元素之间存在多个对多个的关系;
(5)数据元素之间的关系在计算机中有两种不同的表示方法:a.顺序映像 b.非顺序映像;由此得到两种不同的存储结构:
a.顺序存储结构:存放的地址是连续的 b.非顺序存储结构:存放的地址是任意的;
(6)数据类型:数据类型是一个值的集合和定义在这个值集上的一组操作的总称;
(7)基本操作有两种参数:1.赋值操作:只为操作提供输入值;2.引用参数:以&打头,除可提供输入值外,还将返回操作结果。
(8)语句频度与时间复杂度
二、线性表
线性表是最基本的显性结构的一种抽象,主要的基本元素那有插入、删除和查找,长采用顺序存储和链式存储两种存储方式来实现。
(1)定义:一个线性表是n个元素构成的有限序列(n>= 0),可表示为(a1,a2,a3…,an)。对于长度大于0的线性表,其特点是:
a.存在唯一的一个称作“第一个”的元素。
b.存在唯一的一个称作“最后一个”的元素。
c.除第一个元素外,序列中的每个元素均只有一个直接前驱。
d.除最后一个元素外,序列中的每个元素均只有一个直接后继。
注:线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同特性,即属同一数据对象,相邻数据元素之间存在着序偶关系。
1.线性表的顺序存储结构
线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素,从而使逻辑上相邻的两个元素在物理位置上也相邻。
一般地,以LOC(a1)表示线性表中第一个元素的存储位置,d表示每个元素所占存储单元的个数,则在顺序存储结构中,第i个元素ai的存储位置为: LOC(a1)=LOC(a1)+(i-1)×d 在这种存储方式下,按照序号对表中任一元素进行访问是,先根据上市计算元素的存储位置,然后直接访问元素。
线性表采用顺序存储的优点是:可以随机存取表中的元素,按序号查找元素的速度很快;缺点是:插入和删除操作需要移动元素,插入前要移动元素以挪出空的存储单元,然后再插入元素;删除时同样需要移动元素,以填充被删除的元素空出来的存储单元。
(1)顺序存储结构的插入和删除
在表长为n的线性表中插入一个新元素时,共有n+1个可能的插入位置。在位置1(元素a1所在的位置)插入是需要移动n个元素,在位置n+1(元素an所在位置之后)插入时不需要移动元素,因此,等概率下插入一个元素是平均移动的元素数目为n/2;
在表长为n的线性表中删除一个元素是,共有n个可删除的元素,删除a1时需要移动其后的n-1个元素,删除an时不需要移动元素,因此,等概率下删除一个元素是平均的移动元素数目为(n-1)/2。
主要思路
(1)插入:
-
判断插入元素位置是否合理
-
判断表容量是否足够
-
从最后一个元素开始遍历,分别将元素往后移一位
-
修改指定位置的元素
(2)删除
-
判断删除元素位置是否合理
-
在末尾开始遍历,分别向前移动一个位置
-
删除指定位置的元素
(2)代码实现
//举例 在1到10十个数之间 第二个位置上分别插入和删除数字22
typedef struct{
int *elem; //指针变量类型的确定根据它所存储的元素类型 基地址存储的是线性表中第一个数据元素a1的地址
int length;
int listsize;
}sqlist;
main(){
sqlist list;//定义顺序表list(不存在)
int i,j,e;
int *p,*q; //指向插入元素,插入的元素为int型
list.elem=(int *)malloc(100*sizeof(int));
list.length=0;
list.listsize=100; //构建空表list
for(i=1;i<=10;i++) {list.elem[i-1]=i;list.length++;}
for(i=1;i<=list.length;i++)printf("%5d",list.elem[i-1]);//输出顺序表中的所有元素,检验十个数是否放进去
j=2;e=22; //j为插入位置,e为插入元素
if(j<1||j>list.length+1)return 0; //判断j插入的位置是否合法 判断是否有足够的空间插入listsize
q=&list.elem[j-1]; //q为插入元素的地址
//for(m=list.length;m>=j;m--)list.elem[m]=list.elem[m-1];
for(p=list.elem+list.length-1;p>=q;p--)*(p+1)=*p;//从最后一个元素开始往后移 到被插入的位置结束
*q=e;
list.length++;
printf("\n");
for(i=1;i<=list.length;i++)printf("%5d",list.elem[i-1]);//检验插入是否成功
//首先判断删除的元素是否存在
for(p=&list.elem[j],q=list.elem+list.length-1;p<=q;p++) *(p-1)=*p;
list.length--;
printf("\n");
for(i=1;i<=list.length;i++)printf("%5d",list.elem[i-1]);
}//检验是否删除
(3)实现效果
2.线性表的链式存储结构
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素ai与其直接后继数据元素a(i+1)之间的逻辑关系,对数据元素ai来说,除了存储器本身的信息之外,还需存储一个指示其后继数据元素a(i+1)之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素ai的存储映像,称为结点。
在链式存储方式下,用结点来存储数据元素,表中元素的结点地址可以连续,也可以不连续,因此,数据元素和元素之间的逻辑关系都要存储。链表中结点的基本结构如下:
其中数据域用于存储数据元素的值,指针域则存储当前元素的直接前驱或(和)直接后继的位置信息,指针域中的信息称为指针(或链)。
(1)概念区分
头指针:头指针指示链表中第一个结点(即第一个数据元素的存储映像)的存储位置;
头结点:假设的一个结点,数据域不存储任何信息,指针域指向第一个数据元素结点(首元结点)的存储位置;
首元结点:第一个数据元素结点(a1);
第一个元素结点:如果单链表带着头结点,则第一个结点为头结点;如果单链表不带着头结点,则第一个结点为首元结点;
(2)单链表的插入和删除
在链式存储结构中,只要得到指向第一个结点的指针(头指针Head),就可以顺序访问到表中的任意一个元素。实现单链表时,为了简化对链表状态的判定和处理,引入一个头结点,将其作为链表的第一个结点并令头指针指向该结点(头结点的数据域一般不用,或者用来存储链表长度的信息)。在链式存储方式下进行插入和删除,其是指都是对相关指针进行修改。
主要思路
插入:在单链表中,指针p指向某个结点时,p->next表示p所致结点的指针域(实质上是一个指针变量),p->data表示p所指结点的数据域(实质上是一个整型变量)。
在单链表p所指结点后插入新的元素节点(s节点)时,操作如下:
①s->next=p-next; /s所指结点的指针域改为指向p所指结点的后继结点
②p->next=s; /p所指及诶点的指针域改为指向s所指结点
删除:若需删除元素b,则令p结点的指针域指向其后继的后继结点(即图中c所在结点),从而将元素b所在的结点从链表中摘除。当链表中的结点不需要是,应将其所占空间归还给系统,较为完善的操作序列如下:
q=p->next; /备份被删除结点的指针
p->next=p->next->next; /修改结点间得到链接关系,从链表中摘除要删除的结点
free(q); /释放被删除结点的空间
(3)代码实现
//举例 不带头结点的单链表在1到10之间分别在第1个位置上插入和删除 1
#define NULL -1
typedef struct Lnode{
int data;
struct Lnode *next;
}Lnode,*Linklist; //定义结点类型
main(){
Linklist list,p,q,s;//等价于struct Lnode *list,p,q,s;或者Lnode *list,p,q,s;
int i,j,e;
list=NULL;//定义头指针list,并创建了一个不带头结点的空链表
p=(Linklist)malloc(sizeof(Lnode));//申请一个结点的空间
p->data=1;
p->next=NULL;
list=p;//头指针指向首元结点
for(i=2;i<=10;i++){
q=p;//q始终指向当前链表的尾结点
p=(Linklist)malloc(sizeof(Lnode));
p->data=i;
p->next=NULL;
q->next=p;//不带头结点的单链表里面存有1-10个数
}
printf("\n");
p=list;//链表操作从头开始
while(p!=NULL){
printf("%5d",p->data);
p=p->next;//输出不带头结点的单链表
}
//插入
i=1;e=1;//i为插入结点的位置,e为插入元素,s为新插入的结点
s=(Linklist)malloc(sizeof(Lnode));
s->data=e;
s->next=NULL;
if(i==1){
s->next=list;
list=s;
}else{
p=list;j=1;//j为计数器
while(p!=NULL&&j<i-1){
p=p->next;j++;
}
s->next=p->next;
p->next=s;
}
printf("\n");
p=list;
while(p!=NULL){
printf("%5d",p->data);
p=p->next;
}
//删除
i=1;//i为删除结点的位置
if(i==1) {
list=list->next;//头指针直接指向原来链表的第二个结点
}else{
p=list;j=1;//j为计数器
while(p->next!=NULL&&j<i-1){
p=p->next;
j++;
}
p->next=p->next->next;
}
printf("\n");
p=list;
while(p!=NULL){
printf("%5d",p->data);
p=p->next;
}
}
(4)实现效果
代码编辑器:Codeblocks。