文章目录
- 线性结构
- 线性表
-
- 广义表
-
- 堆栈
- 什么是堆栈
- 堆栈的抽象数据类型描述
- 堆栈的顺序存储实现
- 堆栈的链式存储实现
- 堆栈的应用
- 队列
- 什么是队列
- 队列的抽象数据类型描述
- 队列的顺序存储实现
- 队列的链式存储实现
线性结构
线性表
什么是线性表
-
线性表(Linear List):由同类型数据元素构成有序序列的线性结构。
- 表中元素个数称为线性表的长度。
- 线性表没有元素时,称为空表。
- 表起始位置称表头,表结束位置称表尾。
线性表的抽象类型描述
-
类型名称:线性表(List)。
-
数据对象集:线性表是 n(n≥0) 个元素构成的有序序列 (
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an)。
-
操作集:线性表
L
∈
L
i
s
t
L \in List
L∈List,整数i表示位置,元素
X
∈
E
l
e
m
e
n
t
T
y
p
e
X \in ElementType
X∈ElementType,线性表的基本操作主要有:
- List MakeEmpty():初始化空线性表L。
- ElementType FindKth(int K,List L):根据位序K,返回相应元素。
- int Find(ElementType X,List L): 在线性表L中查找X的第一次出现的位置。
- void Insert(ElementType X,int i,List L):在位序i前插入一个新元素X。
- void Delete(int i,List L):删除指定位序i的元素。
- int Length(List L):返回线性表L的长度n。
线性表的实现
-
利用数组的连续存储空间顺序存放线性表的各元素。
struct LNode{
ELementType Data[MAXSIZE];
int Last;
};
typedef struct LNode* List;
struct LNode L;
List LPtr;
访问下标为i的元素:L.Data[i]或LPtr->Data[i]。
线性表的长度:L.Last+1或者LPtr->Last+1。
-
主要操作的实现
-
初始化(建立空的顺序表)
List MakeEmpty()
{
List LPtr=NULL;
LPtr=(List)malloc(sizeof(struct LNode));
LPtr->Last=-1;
return LPtr;
}
-
查找
int Find(ElementType X,List LPtr)
{
int i=0;
while(i<=LPtr->Last&&LPtr->Data[i]!=x)
{
i++;
}
if(i>LPtr->Last)
{
return -1;
}
else
{
return i;
}
}
-
插入(第i(
1
≤
i
≤
n
+
1
1 \leq i \leq n+1
1≤i≤n+1)个位置上插入一个之为X的新元素)
下标i | 0 | 1 | …… | i-1 | i | …… | n-1 | …… | MAZSIZE-1 |
---|
Data |
a
1
a_1
a1 |
a
2
a_2
a2 | …… |
a
i
a_i
ai |
a
i
+
1
a_{i+1}
ai+1 | …… |
a
n
(
L
a
s
t
)
a_n(Last)
an(Last) | …… | - |
再第i个位置插入,将
a
i
a_i
ai~
a
n
a_n
an往后移动,再插入。
下标i | 0 | 1 | …… | i-1 | i | i+1 | …… | n | …… | MAZSIZE-1 |
---|
Data |
a
1
a_1
a1 |
a
2
a_2
a2 | …… | |
a
i
a_i
ai |
a
i
+
1
a_{i+1}
ai+1 | …… |
a
n
(
L
a
s
t
)
a_n(Last)
an(Last) | …… | - |
算法实现:
void Insert(ElementType X,int i,List LPtr)
{
int j=0;
if(LPtr->Last==MAXSIZE-1)
{
printf("表满\n");
return;
}
if(1>i||LPtr->Last+2<i)
{
printf("位置不合法\n");
return;
}
for(j=LPtr->Last;j>=i-1;j--)
{
LPtr->Data[j+1]=LPtr->Data[j];
}
LPtr->Data[i-1]=X;
LPtr->Last++;
}
平均移动次数为n/2
平均时间复杂度为O(n)。
-
删除(删除表的第i(
1
≤
i
≤
n
1 \leq i \leq n
1≤i≤n)个位置上的元素)
下标i | 0 | 1 | …… | i-1 | i | …… | n-1 | …… | MAZSIZE-1 |
---|
Data |
a
1
a_1
a1 |
a
2
a_2
a2 | …… |
a
i
a_i
ai |
a
i
+
1
a_{i+1}
ai+1 | …… |
a
n
(
L
a
s
t
)
a_n(Last)
an(Last) | …… | - |
删除第i个位置的数据,将第i个数据之后的数据往前挪。
下标i | 0 | 1 | …… | i-1 | …… | n-2 | n-1 | …… | MAZSIZE-1 |
---|
Data |
a
1
a_1
a1 |
a
2
a_2
a2 | …… |
a
i
+
1
a_{i+1}
ai+1 | …… |
a
n
(
L
a
s
t
)
a_n(Last)
an(Last) | | …… | - |
算法实现:
void Delete(int i,List LPtr)
{
int j=0;
if(1>i||i>LPtr->Last+1)
{
printf("不存在第%d个元素",i);
return;
}
for(j=i;j<=LPtr->Last;j++)
{
LPtr->Data[j-1]=LPtr->Data[j];
}
LPtr->Last--;
}
平均移动次数为(n-1)/2
平均时间复杂度O(n)。
-
线性表的链式存储实现
不要求逻辑上相邻的两个元素物理上也相邻;通过“链”建立起数据元素之间的逻辑关系。
struct LNode{
ElementType Data;
struct LNode *Next;
};
typedef struct LNode *List;
struct LNode L;
List LPtr;
-
主要操作的实现
-
求表长
int Length(List LPtr)
{
List currentPtr=LPtr;
int i=0;
while(currentPtr)
{
currentPtr=currentPtr->Next;
i++;
}
return i;
}
时间复杂度O(n)。
-
查找
-
按序号查找:FindKth();
List FindKth(int K,List LPtr)
{
List currentPtr=LPtr;
int i=1;
while(currentPtr!=NULL&&K>i)
{
currentPtr=currentPtr->Next;
i++;
}
if(K==i)
{
return currentPtr;
}
else
{
return NULL;
}
}
时间复杂度O(n)。
-
按值查找:Find()
List Find(ElementType X,List LPtr)
{
List currentPtr=LPtr;
while(currentPtr!=NULL&&X!=currentPtr->Data)
{
currentPtr=currentPtr->Next;
}
return currentPtr;
}
时间复杂度O(n)。
-
插入(再第i-1(
1
≤
i
≤
n
+
1
1 \leq i \leq n+1
1≤i≤n+1)个节点后插入一个值为X的新节点)
- 先构造一个新节点,用s指向;
- 再找到链表的第i-1个节点,用p指向;
- 然后修改指针,在插入节点(p之后插入新节点是s)
插入前:
操作顺序:
s->Next=p->Next;
p->Next=s;
插入后:
代码示例:
List Insert(ElementType X,int i,List LPtr)
{
List p=NULL,s=NULL;
if(i==1)
{
s=(List)malloc(sizeof(struct LNode));
s->Data=X;
s->Next=LPtr;
return s;
}
p=FindKth(int i-1,LPtr);
if(p=NULL)
{
printf("参数i错误");
return NULL;
}
else
{
s=(List)malloc(sizeof(struct LNode));
s->Data=X;
s->Next=p->Next;
p->Next=s;
return LPtr;
}
}
时间复杂度O(n)。
-
删除(删除链表的第i(
1
≤
i
≤
n
1 \leq i \leq n
1≤i≤n)个位置上的节点)
- 先找到链表的第i-1个节点,用p指向;
- 再用指针s指向要被删除的节点(p的下一个节点);
- 然后修改指针,删除s所指节点;
- 最后释放s所指节点的空间。
删除前:
操作顺序:
p->Next=s->Next;
free(s);
删除后:
代码示例:
List Delete(int i,List LPtr)
{
List p=NULL,s=NULL;
if(1==i)
{
s=LPtr;
if(LPtr!=NULL)
{
LPtr=LPtr->Next;
free(s);
return LPtr;
}
else
{
return NULL;
}
}
p=FindKth(i-1,LPtr);
if(p==NULL||p->Next==NULL)
{
printf("第%d个节点不存在",i);
return NULL;
}
else
{
s=p->Next;
p->Next=s->Next;
free(s);
return LPtr;
}
}
时间复杂度O(n)。
广义表
广义表定义
- 广义表是线性表的推广;
- 对于线性表来说,n个元素都是基本的单元素;
- 广义表中,这些元素不仅可以是单元素也可以是另一个广义表。
struct GNode{
int Tag;
union{
ElementType Data;
GList SubList;
}URegion;
struct GNode *Next;
};
typedef struct GNode* GList;
多重链表
多重链表:链表中的节点可能同时属于多个链
- 多重链表中节点的指针域会有多个,如广义表中包含了Next和SubList两个指针域;
- 但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表。
多重链表有广泛的用途:基本上如树、图这样的相对复杂的数据结构都可以采用多重链表方式实现存储。
多重链表的应用:
矩阵多重链表示意图:
矩阵多重链表节点设计:
堆栈
什么是堆栈
堆栈(Stack):具有一定操作约束的线性表。
堆栈的抽象数据类型描述
类型名称:堆栈(Stack)
数据对象集:一个有个或多个元素的有穷线性表。
操作集:长度为MaxSize的堆栈
S
∈
S
t
a
c
k
S \in Stack
S∈Stack,堆栈元素
i
t
e
m
∈
E
l
e
m
e
n
t
T
y
p
e
item \in ElementType
item∈ElementType
- Stack CreateStack(int MaxSize):生成空堆栈,其最大长度为MaxSize;
- int IsFull(Stack S,int MaxSize):判断堆栈S是否已满;
- void Push(Stack S,ElementType item):将元素item压入堆栈;
- int IsEmpty(Stack S):判断堆栈S是否为空;
- ElementType Pop(Stack S):删除并返回栈顶元素;
堆栈的顺序存储实现
栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成。
#define MaxSize <存储数据元素的最大个数>
struct SNode{
ElementType Data[MaxSize];
int Top;
};
typedef struct SNode* Stack;
-
主要操作实现
-
入栈
void Push(Stack SPtr, ElementType itme)
{
if(SPtr->Top==MaxSize-1)
{
printf("堆栈满");
return;
}
else
{
SPtr->Data[++(SPtr->Top)]=itme;
return;
}
}
-
出栈
ElementType Pop(Stack SPtr)
{
if(SPtr->Top==-1)
{
printf("堆栈空");
return ERROR;
}
else
{
return (SPtr->Data[(SPtr->Top)--]);
}
}
堆栈的链式存储实现
堆栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。
思考:栈顶指针Top应该在链表的哪一头?
答:应该将栈顶指针放在链表的头部,这样插入和删除操作都很方便;如果将栈顶指针放在链表尾部,那么删除操作实现比较困难。
struct SNode{
ElementType Data;
struct SNode *Next;
};
typedef struct SNode* Stack;
-
主要操作实现
-
建立空栈(空栈指只有一个头节点,节点不带任何数据)
Stack CreateStack()
{
Stack S=NULL;
S=(Stack)malloc(sizeof(struct SNode));
S->Next=NULL;
return S;
}
-
判断堆栈S是否为空
int IsEmpty(Stack S)
{
return (S->Next==NULL);
}
-
入栈
void Push(ElementType item,Stack S)
{
Stack Temp=NULL;
Temp=(stack)malloc(sizeof(struct SNode));
Temp->Data=item;
Temp->Next=S->Next;
S->Next=Temp;
}
-
出栈
ElementType Pop(Stack S)
{
Stack TempCell=NULL;
ElementType TempElem=0;
if(S->Next==NULL)
{
printf("d堆栈空");
return NULL;
}
else
{
TempCell=S->Next;
S->Next=TempCell->Next;
TempElem=TempCell->Data;
free(TempCell);
return TempElem;
}
}
堆栈的应用
- 表达式求值
具体过程详见https://blog.csdn.net/qq_42313728/article/details/99625250 - 函数调用及递归实现
- 深度优先搜索
- 回溯算法
队列
什么是队列
队列(Queue):具有一定操作约束的线性表
- 删除和插入操作:只能在一端插入,而在另一端删除。
- 数据插入:入队列(AddQ)
- 数据删除:出队列(DeleteQ)
- 先进先出:FIFO
队列的抽象数据类型描述
数据名称:队列(Queue)
数据对象集:一个有0个或多个元素的有穷线性表。
操作集:长度为MaxSize的队列
Q
∈
Q
u
e
u
e
Q \in Queue
Q∈Queue,队列元素
i
t
e
m
∈
E
l
e
m
e
n
t
T
y
p
e
item \in ElementType
item∈ElementType
- Queue CreatQueue(int MaxSize):生成长度为MaxSize的空队列;
- int IsFull(Queue Q,int MaxSize):判断队列Q是否已满;
- void AddQ(Queue Q,ElementType item):将数据元素插入到队列Q中;
- int IsEmpty(Queue Q):判断队列Q是否为空;
- ElementType DeleteQ(Queue Q):将队头数据元素从队列中删除并返回。
队列的顺序存储实现
队列的顺序存储结构通常由一个一位数组和一个记录队列头元素位置的变量front以及一个记录队列尾元素位置的变量rear组成。(rear指向对列尾最后一个元素,front指向队列头元素的前一个元素)
#define MAXSIZE <存储数据元素的最大个数>
struct QNode{
ElementType Data[MAXSIZE];
int rear;
int front;
};
typedef struct QNode *Queue;
在循环队列中,判空和判满的条件都是rear==front:
队列空时:
队列将满时(再增加一个元素之后队列满,然后rear==front):
解决这个问题的方法有两种:
-
使用额外标记:size或者tag域
- 用size来记录当前队列元素的个数,插入一个元素size加1,删除一个元素size减1。
- 用tag来记录队列上一次操作是插入还是删除,插入一个元素tag=1,删除一个元素tag=0,当front==rear时,根据tag来判断队列是空还是满。
-
仅使用MAXSIZE-1个数组空间
- 主要操作实现(第二种解决方案)
-
入队列
void AddQ(Queue QPtr,ElementType item)
{
if((QPtr->rear+1)%MAXSIZE==QPtr->front)
{
printf("队列满");
return;
}
QPtr->rear=(QPtr->rear+1)%MAXSIZE;
QPtr->Data[QPtr->rear]=item;
}
-
出队列
ElementType DeleteQ(Queue QPtr)
{
if(QPtr->rear==QPtr->front)
{
printf("队列空");
return ERROR;
}
else
{
QPtr->front=(QPtr->front+1)%MAXSIZE;
return QPtr->Data[QPtr->front];
}
}
队列的链式存储实现
队列的链式存储结构也可以用一个单链表实现。插入和删除操作分别在链表的两头进行。(front指向队列头元素,rear指向队列尾元素)
问:队列指针front和rear应该分别指向链表的哪一头?
答:队列front端用来进行删除操作,rear端用来做插入操作;而单向链表的头部插入删除都很方便,尾部插入方便,但是删除麻烦;所以front应该指向单向链表的头部,rear应该指向单向链表的尾部。
struct Node{
ElementType Data;
struct Node *Next;
};
struct QNode{
struct Node *rear;
struct Node *front;
};
typedef struct QNode *Queue;
Queue QPtr;
- 主要操作实现
-
入队列(不带头结点)
void AddQ(Queue QPtr,ElementType item)
{
struct Node *RearCell;
RearCell=(struct Node*)malloc(sizeof(struct Node));
RearCell->Data=item;
RearCell->Next=NULL;
if(QPtr->rear==NULL)
{
QPtr->rear=RearCell;
QPtr->front=RearCell;
}
else
{
QPtr->rear->Next=RearCell;
QPtr->rear=RearCell;
}
}
-
出队列(不带头结点)
ElementType Delete(Queue QPtr)
{
struct Node *FrontCell;
ElementType FrontElem;
if(QPtr->front==NULL)
{
printf("队列空");
return ERROR;
}
FrontCell=QPtr->front;
if(QPtr->front==QPtr->rear)
{
QPtr->front=NULL;
QPtr->rear=NULL;
}
else
{
QPtr->front=QPtr->front->Next;
}
FrontElem=FrontCell->Data;
free(FrontCell);
return FrontElem;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)