数据结构之二:线性表的实现

本文详细解析了线性表的初始化、查找、插入和删除操作,以及链式存储的链表长度计算、查找、插入和删除方法。涵盖了广义表的概念,并讨论了多重链表的应用,如稀疏矩阵的存储。深入理解线性结构在数据结构中的核心作用。

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


内容参考自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

在这里插入图片描述在这里插入图片描述广义表比较复杂,欢迎大佬指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s_persist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值