一、绪论
基本概念
数据:信息载体,可以被计算机识别与处理(二进制0与1)
数据元素:数据基本单位,由若干数据项组成
数据结构:存在一种或多种特定关系的数据元素的集合
数据对象:拥有相同性质的数据元素的集合,是数据的一个子集
数据类型:值的集合,定义在此集合上的一组操作的总称
(原子类型&结构类型)
抽象数据类型:通常就是数据结构
三要素
逻辑结构
集合(各元素同属一个集合,无其他关系)
线性结构(元素之间是一对一的关系,有前驱与后继(头只有后继,尾只有前驱))
树形结构:(元素是一对多的关系)
图形结构:(元素是多对多的关系)
存储结构
顺序存储(存放到相邻的存储单元中)
链式存储(可以不相邻,用指针表示彼此之间的逻辑关系)
索引存储(建立索引表)
散列存储(哈希,利用关键字直接计算出地址)
数据运算
算法(处理数据结构中的信息来解决实际问题)
五特性
- 有穷性
- 确定性
- 可行性
- 输入(零个或多个)
- 输出(一个或多个)
“好”算法的特性
- 正确性
- 可读性
- 健壮性
- 高效率(时间复杂度)&低存储量(空间复杂度)
算法的效率度量
时间复杂度计算
只考虑阶数最高的部分!
加法取最大阶,乘法保留最大阶
eg:
n
3
+
2
n
2
=
O
(
n
3
)
n^3+2n^2=O(n^3)
n3+2n2=O(n3)
O
(
1
)
<
O
(
l
o
g
2
n
)
<
O
(
n
)
<
O
(
n
l
o
g
2
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1)<O(log_2n)<O(n)<O(nlog_2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
实在无法比较复杂度时可以写
l
i
m
n
→
∞
f
(
x
)
g
(
x
)
lim_{n\to \infty}\frac{f(x)}{g(x)}
limn→∞g(x)f(x),洛必达即可。
要会计算最坏复杂度、最好复杂度、平均复杂度(期望)
空间复杂度计算
只考虑阶数最高的部分!
可以是递归调用深度*每层需要空间!
二、线性表(相同数据类型的有限序列)
L
=
(
a
1
,
a
2
,
.
.
.
,
a
i
,
a
i
+
1
,
.
.
.
a
n
)
L=(a_1,a_2,...,a_i,a_{i+1},...a_n)
L=(a1,a2,...,ai,ai+1,...an)
线性表位序从1开始!不是0开始!
表长难以估计,需要经常删除/增加元素->链表
表长可预估,查询搜索功能较多->顺序表
顺序表(顺序存储)
根据下标查找
O
(
1
)
O(1)
O(1)
拓展容量不方便
O
(
n
)
O(n)
O(n)
插入删除不方便,需要移动大量元素
O
(
n
)
O(n)
O(n)
基本操作
- 初始化
- 销毁
- 插入
- 删除
- 按值查找
- 按位查找
- 求表长
- 输出表
- 判断为空
将操作封装为函数
传入时使用"&"->需要把处理的结果“带回来”
sizeof(变量类型/结构体名):某类型所占类型(单位:字节)
eg:sizeof(int)=4
如果不初始化,会出现**“脏数据”**(类似乱码的随机数据)
具体实现
静态分配(需要初始化)
#define MaxSize 10
typedef struct{
ElemType data[maxSize];
int length;
}SeqList;
void InitList(SeqList &L){//初始化
L.length=0;
}
bool ListInsert(SeqList &L,int index,int value){//插入元素O(n)
if(i<1 || i>L.length+1)//下标越界
return false;
if(L.length>=MaxSize)//空间已满
return false;
for(int j=L.length;j>=index;j--){//将第index个后面的元素后移
L.data[j]=L.data[j-1];
}
L.data[idex-1]=value;
L.length++;//更新长度
return true;
}
bool ListDelete(SeqList &L,int index,int &value){//删除元素O(n)
if(i<1 || i>L.length) //下标越界
return false;
value=L.data[index-1];
for(int j=index;j<L.length;j++){//将第index个后面的元素后移
L.data[j-1]=L.data[j];
}
L.length--;//更新长度
return true;
}
ElemType getElemByIndex(SeqList L ,int index){
return L.data[index-1];
}
int getIndexByElem(SeqList L,ElemType e){
for(int i=0;i<L.length;i++){
if(L.data[i]==e)
return i+1;
}
return 0;
}
动态分配(需要分配空间)
#define InitSize 10
typedef struct{
ElemType *data;
int maxSize;
int length;
}SeqList;
void InitList(SeqList &L){//初始化
SeqList.data=(ElemType *)malloc(sizeof(ElemType)*InitSize);//分配空间
L.length=0;
L.maxSize=InitSize;
}
/*
如果需要增加动态数组的长度,需要:
1. 先用一个指针p暂存原数据
2. 用malloc重新分配
3. 循环,将数据覆盖
4. 更新maxSize
5. free(p)
*/
void IncreaseSize(SeqList &L,int len){
ElemType *p=L.data;
L.data=SeqList.data=(ElemType *)malloc(sizeof(ElemType)*(len+L.maxSize));//重分配区间
for(int i=0;i<L.length;i++){
L.data[i]=p[i];//数据覆盖
}
L.maxSize+=len;//更新最大长度
free(p);//释放内存
}
链表(链式存储)
单链表
改变容量方便,但不能随机存取,要耗费一定资源来存放指针
带头结点的会多出一个NULL节点!
常用带头结点的写法!但实际考试可能都有!
无法逆向查找!
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;//分别是单节点和单链表
bool InitList(LinkList &L){//不带头结点的单链表创建
L=NULL;
return true;
}
bool Empty(LinkList L){//判断是否为空(不带头结点)
return L==NULL;
}
bool InitList(LinkList &L){
L=(LNode *)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=NULL;
return true;
}
bool Empty(LinkList L){//判断是否为空(带头结点)
return L->next==NULL;
}
LinkList List_HeadInsert(LinkList &L){//头插法建立单链表
LNode *temp,ElemType x;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
while(~输入x && x!=某个特殊终止符){
temp=(LNode*)malloc(sizeof(LNode));
temp->data=x;
temp->next=L->next;
L->next=temp;
}
return L;
}
LinkList List_TailInsert(LinkList &L){//尾插法建立单链表
ElemType x;
L=(LinkList)malloc(sizeof(LNode));
LNode *temp,*r=L;
while(~输入x && x!=某个特殊终止符){
temp=(LNode*)malloc(sizeof(LNode));
temp->data=x;
r->next=temp;
r=temp;
}
r->next=NULL;
return L;
}
LNode * getElemByIndex(LinkList L,int index){
if(i<0)
return NULL;
int pos=0;
LNode *p=L;
while(p!=NULL && pos<index){
p=p->next;
pos++;
}
return p;
}
int getLength(LinkList L){
int len=0;
LNode *p=L;
while(p->next!=NULL){
p=p->next;
len++;
}
return len;
}
LNode * getElemByElem(LinkList L,ElemType value){//找到第一个数据为value的节点
LNode *p=L->next;
while(p!=NULL && p->data!=value){
p-p>next;
}
return p;
}
bool ListInsert(LinkList &L,int index,ElemType value){//插入节点(带头结点)
if(index<1)
return false;
LNode *p;
int pos=0;
p=L;
while(p!=NULL && pos<index-1){//遍历到第i-1个
p=p->next;
pos++;
}
if(p==NULL)
return false;//非法
LNode *temp=(LNode *)malloc(sizeof(LNode));
temp->data=value;
temp->next=p->next;
p->next=temp;
return true;
}
bool ListInsert(LinkList &L,int index,ElemType value){//插入节点(不带头结点)
if(i<1)
return false;
if(i==1){//比较特殊的位置
LNode *temp=(LNode *)malloc(sizeof(LNode));
temp->data=value;
temp->next=L;
L=temp;
return true;
}
LNode *p;
int pos=0;
p=L;
while(p!=NULL && pos<index-1){//遍历到第i-1个
p=p->next;
pos++;
}
if(p==NULL)
return false;//非法
LNode *temp=(LNode *)malloc(sizeof(LNode));
temp->data=value;
temp->next=p->next;
p->next=temp;
return true;
}
bool InsertNextNode(LNode *p,ElemType value){//在某个节点后插入value
if(p==NULL)
return false;
LNode *temp=(LNode *)malloc(sizeof(LNode));
if(temp==NULL)//内存分配失败
return false;
temp->data=value;
temp->next=p->next;
p->next=temp;
return true;
}
bool InsertPriorNode(LNode *p,LNode *s){//在p节点前插入s
if(p==NULL || s==NULL){
return false;
}
s->next=p->next;
p->next=s;
ElemType temp=p->data;
p->data=s->data;
s->data=temp;
return true;
}
bool ListDelete(LinkList &L,int index,ElemType value){
if(index<1)
return false;
LNode *p;
int pos=0;
p=L;
while(p!=NULL && pos<index-1){//遍历到第i-1个
p=p->next;
pos++;
}
if(p==NULL)
return false;//非法
if(p->next==NULL){
return false;
}
LNode *temp=p->next;
value=q->data;
p->next=temp->next;
free(temp);//释放节点
return true;
}
bool DeleteNode(LNode *p){//删除指定节点
if(p==NULL)
return false;
LNode *temp=p->next;
p->data=temp->next->data;
p->next=temp->next;
free(temp);
return true;
}
双链表
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
bool InitDLinkList(DLinklist &L){//双链表初始化(带头结点)
L=(DNode *) malloc(sizeof(DNode));
if(L==NULL){
return false;
}
L->prior=NULL;
L->next=NULL;
return true;
}
bool Empty(DLinkList L){
return L->next==NULL;
}
bool InsertNextDNode(DNode *p,DNode *s){
if(p==NULL || s==NULL){
return false;
}
s->next=p->next;
if(p->next!=NULL)
p->next->prior=s;
s->prior=p;
p->next=s;
}
bool DeleteNextDNode(DNode *p){
if(p==NULL){
return false;
}
DNode *q=p->next;
if(q==NULL){
return false;
}
p->next=q->next;
if(q->next!=NULL){
q->next->prior=p;
}
free(q);
return true;
}
void DestoryList(DLinkList &L){
while(L->next!=NULL){
DeleteNextNode(L);
}
free(L);
L=NULL:
}
循环链表(第一个节点与最后一个节点相连)
循环单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
bool InitList(LinkList &L){
L=(LNode *)malloc(sizoef(LNode));
if(L==NULL){
return false;
}
L->next=L;//next指向头结点
return true;
}
循环双链表
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
bool InitDLinkList(DLinklist &L){//双链表初始化(带头结点)
L=(DNode *) malloc(sizeof(DNode));
if(L==NULL){
return false;
}
L->prior=L;
L->next=L;
return true;
}
bool Empty(DLinkList L){
return L->next==L;
}
bool isTail(DlinkList L,DNode *p){
return p->next==L;
}
bool InsertNextDNode(DNode *p,Dnode *s){
s->next=p->next;
p->next->prior=s;
s->prior=p;
p->next=s;
}
void DeleteNextDNode(DNode *p){
DNode *q;
p->next=q->next;
q->next->prior=p;
free(q);
}
静态链表(用数组实现的链表)
#define MaxSize 10
typedef struct Node{
ElemType data;
int next;//下一个元素的下标
}SLinkList[MaxSize];
初始化:a[0].next=-1;
查找:顺序遍历
在第i个位置插入结点:
- 先找到空闲节点(比如负数表示空闲)存入元素
- 遍历找到第i-1个结点
- 修改新结点的next
- 修改i-1结点的next
删除某个节点:
- 遍历找到其前驱节点
- 修改前驱结点下标,被删除结点变为空闲状态