数据结构笔记

本文深入探讨了算法的基本特性,包括有穷性、确定性和可行性,并阐述了算法设计的正确性、可读性、健壮性、时间和空间效率。重点介绍了线性表这一抽象数据类型,包括其逻辑定义和两种存储结构:顺序存储和链式存储。对于线性表的顺序存储,详细讲解了初始化、获取元素、插入和删除等操作的实现。链式存储部分则讨论了单链表的结构和操作,如读取、插入和删除。这些内容对于理解数据结构和算法的效率至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法的特性

  • 输入
  • 输出
  • 有穷性
  • 确定性
  • 可行性

算法设计的要求:正确性、可读性、健壮性、时间效率高和存储量低。

算法的效率

  • 算法效率的度量方法

    • 事后统计方法
    • 事前分析估算方法
  • 算法时间复杂度T(n)

    • 用常数1取代运行时间中的所有加法常数。

    • 在修改后的运行次数函数中,只保留最高阶项。

    • 如果最高项存在且不是1,则去除与这个项相乘的常数。

    • 得到的最后的结果就是大O阶。

      O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

  • 算法的空间复杂度S(n)

抽象数据类型

抽象:是指抽取出事物具有普遍性的本质。

抽象数据类型(Abstract Data Type,ADT)是指一个数学模型及定义在该模型上的一组操作。

ADT 抽象数据类型名
Data
	数据元素之间逻辑关系的定义
operation
	操作
endADT

线性表

​ 由零个或多个数据元素组成的有限序列。(线性表强调的是有限的。)

线性表的抽象数据类型

ADT 线性表(List)
Data
	线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataType。其中,除第一个元素a1外
,每一个元素有且至于一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
	InitList(*L):初始化操作,建立一个空的线性表L。
	ListEmpty(L):判断线性表是否为空表,若线性表为空,返回true,否则返回false。
	ClearList(*L):将线性表清空。
	GetElem(L,i,*e):将线性表L中的第i个位置元素值返回给e。
	LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
	ListInsert(*L,i,e):在线性表中的第i个位置插入新元素e。
	ListDelete(*L,i,*e):删除线性表中第i个位置元素,并用e返回其值。
	ListLength(L):返回线性表L的元素个数。
end ADT;

线性表的顺序存储结构

结构代码

#define MAXSIZE 20
typedef int ElemType;
typedef struct{
	ElemType date[MAXSIZE];
	int length;//线性表当前长度
}SqList;

地址计算方法:LOC(ai)=LOC(a1)+(i-1)*c

获得元素操作

实现GetElem的具体操作,即将线性表L中的第i个位置元素值返回。我们只需要把数组第i-1下标的值返回即可。

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
//Status是函数的类型,其值是函数结果状态代码,如OK等。
//初始条件:顺序线性表L已经存在,1<= i <= ListLength(L)
//操作结果:用e返回L中第i个数据元素的值。
Status GetElem(SqList L,int i,ElemType *e){
	if(L.length == 0 || i<1 || i<L.length){
		return ERROR;
	}
	*e = L.data[i-1];
	return OK;
}
插入操作

实现ListInsert(*L,i,e),即在线性表L中的第i个位置插入新元素e

插入算法思路:

​ 如果插入位置不合理,抛出异常;

​ 如果线性表长度大于等于数组长度,则抛出异常或动态增加数组容量;

​ 从最后一个元素开始向前遍历到第i个位置,分别将他们都向后移动一个位置;

​ 将要插入元素填入位置i处;

​ 线性表的长度+1。

//初始条件:顺序线性表L已经存在,1<=i<=ListLength(L).
//操作结果:在L中第i个位置之前插入新的数据元素e,L长度+1.

Status ListInsert(SqList *L,int i,ElemType e){
	int k;
	if(L->length == MAXSIZE){//顺序线性表已经满了
		return ERROR;
	}
	if(i<1 || i>L->length+1){//当i不在范围内时
		return ERROR;
	}
	if(i<=L->length){//若插入数据位置不在表尾
		//将要插入位置后数据元素向后移动一位
		for(k=L->length-1;k>=i-1;k--){
			L->data[k+1] = L->data[k];
		}
	}
	L->data[i-1] = e;//将新元素插入
	L->length++;
	return OK;
}
删除操作

算法思路:

​ 如果删除位置不合理,抛出异常;

​ 取出删除元素;

​ 从删除元素位置开始遍历到最后一个元素位置,分别将他们向前移动一个位置;

​ 表长-1.

//初始条件:顺序线性表L已经存在,1<=i<=ListLength(L).
//操作结果:删除L的第i个数据元素,并用e返回其值,L的长度-1
Status ListDelete(SqList *L,int i,ElemType *e){
	int k;
	if(L->length == 0){
		return ERROR;
	}
	if(i<1 || i>L-length){
		return ERROR;
	}
	*e = L->data[i-1];
	if(i<L-length){
		for(k=i;k<L->length;k++){
			L->data[k-1] = L->data[k];
		}
	}
	L->length--;
	return OK;
}

线性表的链式存储结构

线性表的链式存储结构的特点:

​ 用一组任意的存储单元存储线性表的数据元素,这组存储单元可以存在内存中未被占用的任意位置。

我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成数据元素称为存储映像,称为结点(Node)。

n个结点链接成一个链表,即为线性表的链式存储结构。

因为此链表的每个结点中只包含一个指针域,所以叫单链表。

第一个结点的存储位置叫做头指针,最后一个结点指针为空(null)。

头指针
  • 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
  • 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
  • 无论链表是否为空,头指针均不为空。
  • 头指针是链表的必要元素。
头结点
  • 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)
  • 有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
  • 头结点不一定是链表的必须要素。
单链表存储结构

用结构指针来描述单链表

typedef struct Node{
	ElemType data;		//数据域
	struct Node* Next;  //指针域
}Node;
typedef struct Node* LinkList;
单链表的读取

算法思路:

​ 声明一个结点p指向链表第一个结点,初始化j从1开始;

​ 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j+1;

​ 若到链表末尾p为空,则说明第i个元素不存在;

​ 否则查找成功,返回结点p的数据。

Status GetElem(LinkList L,int i,ElemType *e){
	int j;
	LinkList P;
	P = L->next;
	j=1;
	while(p && j<i){
		p=p->next;
		++j;
	}
	if(!p || j>i){
		return ERROR;
	}
	*e= p->data;
	return OK;
}
单链表的插入

算法思路:

​ 声明一结点指向链表头结点,初始化j从1开始;

​ 当j<1时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;

​ 若到链表末尾p为空,则说明第i个元素不存在;

​ 否则查找成功,在系统中生成一个空结点s;

​ 将数据元素e赋值给s->data;

​ 然后插入结点

​ 返回成功

Status ListInsert(LinkList *L,int i,ElemType e){
	int j;
	LinkList p,s;
	
	p= *L;
	j=1;
	
	while(p && j<1){//用于寻找第i个结点
		p=p->next;
		j++;
	}
	if(!p || j>1){
		return ERROR;
	}
	
	s=(LinkList)malloc(size(Node));
	s->date=e;
	s->next=p->next;
	p->next=s;
	return OK;
}
单链表的删除

算法思路:

​ 声明结点p指向链表第一个结点,初始化j=1;

​ 当j<1时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;

​ 若到链表末尾p为空,则说明第i个元素不存在;

​ 否则查找成功,将欲删除结点p->next复制给q;

​ 单链表删除标准语句p->next=q->next;

​ 将q结点中的数据赋值给e,作为返回;

​ 释放q结点。

Status ListDelete(LinkList *L,int i,ElemType *e){
	int j;
	LinkList p,q;
	p=*L;
	j=1;
	
	while(p->next && j<i){
		p=p->next;
		++j;
	}
	
	if(!(p->next) || j>i){
		return ERROR;
	}
	q=p->next;
	p->next = q->next;
	
	*e=q->data;
	free(q);
	return OK;
}

->next=q->next;

​ 将q结点中的数据赋值给e,作为返回;

​ 释放q结点。

Status ListDelete(LinkList *L,int i,ElemType *e){
	int j;
	LinkList p,q;
	p=*L;
	j=1;
	
	while(p->next && j<i){
		p=p->next;
		++j;
	}
	
	if(!(p->next) || j>i){
		return ERROR;
	}
	q=p->next;
	p->next = q->next;
	
	*e=q->data;
	free(q);
	return OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值