文章目录
内容参考自MOOC浙江大学数据结构课程。
一,什么是线性表和线性表的实现
1.线性表
“线性表(Linear List)”: 由同类型数据元素构成有序序列的线性结构
表中元素个数称为线性表的长度
线性表没有元素时,称为空表
表起始位置称表头,表结束位置称表尾
- 线性表的抽象数据类型描述
对线性表而言,我们可以建立一种抽象数据类型。一是这个类型包含的数据对象集是什么,另外一个,在这个对象集上有什么操作。
假定线性表叫list,里面一个具体的线性表叫L,里面的元素X有个类型叫ElementType,这个类型可以是整形、实形,也可以是结构,这里统称为ElementType。其中我们还用整数i表示一个元素在表中所占的位置。
需要定义的一些函数如下:
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。
2.线性表的初始化
- 线性表的定义
typedef int Position;
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
- 对线性表初始化
/* 初始化 */
List MakeEmpty()
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Last = -1;
return L;
}
3.线性表的查找
/* 查找 */
#define ERROR -1
Position Find( List L, ElementType X )
{
Position i = 0;
while( i <= L->Last && L->Data[i]!= X )
i++;
if ( i > L->Last ) return ERROR; /* 如果没找到,返回错误信息 */
else return i; /* 找到后返回的是存储位置 */
}
4.线性表的插入
/* 插入 */
/*这里P是存储下标位置(从0开始)*/
bool Insert( List L, ElementType X, Position P )
{ /* 在L的指定位置P前插入一个新元素X */
Position i;
if ( L->Last == MAXSIZE-1) {
/* 表空间已满,不能插入 */
printf("表满");
return false;
}
if ( P<0 || P>L->Last+1 ) { /* 检查插入位置的合法性 */
printf("位置不合法");
return false;
}
for( i=L->Last; i>=P; i-- )
L->Data[i+1] = L->Data[i]; /* 将位置P及以后的元素顺序向后移动 */
L->Data[P] = X; /* 新元素插入 */
L->Last++; /* Last仍指向最后元素 */
return true;
}
5.线性表的删除
/* 删除 */
bool Delete( List L, Position P )
{ /* 从L中删除指定位置P的元素 */
Position i;
if( P<0 || P>L->Last ) { /* 检查空表及删除位置的合法性 */
printf("位置%d不存在元素", P );
return false;
}
for( i=P+1; i<=L->Last; i++ )
L->Data[i-1] = L->Data[i]; /* 将位置P+1及以后的元素顺序向前移动 */
L->Last--; /* Last仍指向最后元素 */
return true;
}
二,线性表的链式存储实现
1.链表的长度
//计算链表长度
//返回值为链表长度
int ListLength(List Lhead)
{
List p = Lhead; //定义一个p指向链表的头节点地址
int i = 0;
if (p) //最后一个结点的指针域为空NULL
{
p = p->Next; //往后遍历
i++;
}
return i; //返回遍历个数,即总个数
}
2.链表的查找
- 按序号查找
//按序号查找,第K个
//返回值为第i个结点的地址
List FindTh(int k, List Lhead)
{
List p = Lhead;
int i = 1;
while (p != NULL&&i < k) //两个遍历条件,当到底链表尾端或者找到第K个
{
p = p->Next;
i++;
}
if (i == k) return p; //找到第k个返回该结点地址
else return NULL; //没有找到则返回空指针
}
- 按值查找
//按值查找
//返回值为值为X的结点的地址
List FindX(ElementType X, List Lhead)
{
List p = Lhead;
while (p!=NULL&&p->Data!=X)
p = p->Next;
return p; //找到时p为数据为X的结点的地址,找不到则返回空指针
}
3.链表的插入
插入在第 i − 1 ( 1 ≤ i ≤ n + 1 ) 个结点后插入一个值为 X 的新结点
(1)先构造一个新结点,用s 指向;
(2)再找到链表的第 i − 1 个结点,用 p 指向;
(3)然后修改指针,插入结点( p 之后插入新结点是 s )
//插入结点,第i个结点后
//返回值为头节点地址
List Insert(ElementType X, int i, List Lhead)
{
//1.头上插入
//2.其他地方插入
List AimNode, NewNode; //目标结点,新结点
if (i == 1) //头上插入
{
NewNode = (List)malloc(sizeof(LNode));//创建一个新的结点
NewNode->Next = Lhead; //填装结点
NewNode->Data = X;
NewNode = Lhead;
return NewNode;
}
//其他地方插入
AimNode = FindTh(i, Lhead); //找到插入点的结点地址
if (AimNode == NULL)
{
printf("插入位置错误");
return NULL;
}
else
{
NewNode = (List)malloc(sizeof(LNode));//创建一个新的结点
NewNode->Data = X;
NewNode->Next = AimNode->Next;
AimNode->Next = NewNode;
return Lhead;
}
}
4.链表的删除
删除链表的第 i ( 1 ≤ i ≤ n )个位置上的结点
(1)先找到链表的第 i − 1 个结点,用 p 指向;
(2)再用指针 s 指向要被删除的结点(p 的下一个结点);
(3)然后修改指针,删除 s 所指结点;
//删除结点
//返回值为头节点地址
List Delete(ElementType X, int i, List Lhead)
{
//1.删除头节点
//2.删除其他结点
List AimNode; //要删除的结点地址
if (i == 1) //删除头结点
{
AimNode = Lhead;
if (Lhead != NULL) //判断链表是否为空
Lhead = Lhead->Next; //改变头结点
else
return NULL;
free(AimNode); //释放要删除的结点空间
return Lhead;
}
List SurNode = FindTh(i, Lhead); //找到要删除的位置
if (SurNode == NULL) //如果结点不存在
{
printf("第%d结点不存在!\n",i-1);
return NULL;
}
else if (SurNode->Next == NULL) //如果下一个结点不存在
{
printf("第%d结点不存在!\n",i);
return NULL;
}
else
{
List AimNode = SurNode->Next; //存放要删除位置的结点地址
SurNode->Next = AimNode->Next;
free(AimNode);
return Lhead;
}
}
三,广义表
广义表(Generalized List)
- 广义表是线性表的推广
- 对于线性表而言, n个元素都是基本的单元素;
- 广义表中,这些元素不仅可以是单元素也可以是另一个广义表。
所以我们在广义表中碰到的问题可能是,一个域有可能可以分解,有可能是一个指针。
这里用到了一个工具叫union。union可以把不同类型的数据组装到一起。可以把这个空间理解成某种类型也可以理解成另外一种类型。
typedef struct GNode*GList;
struct GNode{
int Tag;//标志域:0表示结点是单元素,1表示结点是广义表
union //子表指针域Sublist与单元素Data复用。
{
ElementType Data;
GList SubList;
}URegion;
GList Next;//后继结点
};
多重链表:链表中的节点可能同时隶属于多个链表
多重链表中结点的指针域可能会有多个,如前面例子中Next和SubList两个指针域;
但包含两个指针域的链表不一定是多重链表,比如双向链表不是多重链表。一个指针往前指,一个往后指,实际上链表是同一个链表。
多重链表又广泛的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。
矩阵可以用二维数组表示,但二维数组表示有两个缺陷:
一是数组的大小需要事先确定
对于“稀疏矩阵”,将造成大量存储空间浪费
【分析】采用一种典型的多重链表——十字链表来存储稀疏矩阵
只存储矩阵非0元素项:结点的数据域:行坐标Row、列坐标Col、数值Value
每个结点通过两个指针域,把同行、同列串起来;
行指针(或称为向右指针)Right
列指针(或称为向下指针)Down
广义表比较复杂,欢迎大佬指正!