数据结构(C语言版)-第2章-线性表(1)

来源书籍:
数据结构(C语言版)(第2版·微课版) 秦锋 汤亚玲主编 清华大学出版社 2021年12月
书籍链接:http://www.tup.tsinghua.edu.cn/booksCenter/book_09444301.html#

1.线性表

1.1 顺序表上基本运算的实现

1.1.1 顺序表的初始化

#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100

这段代码定义了预处理指令#include和宏定义:
stdio.h是C语言标准库头文件,提供了输入和输出函数的定义,如printf和scanf等。
malloc.h是动态内存分配函数的头文件,提供了malloc和free等函数的定义。
MAXSIZE是定义的宏,表示顺序表的最大长度为100。

typedef int DataType;
typedef struct 
{
    
    
	DataType data[MAXSIZE];
    int length;
} SeqList,*PSeqList;       /*顺序表类型定义*/ 

在这段代码中,typedef 关键字用于创建两个新类型:

SeqList:它是一个结构体类型,包含了一个 DataType 类型的数组 data一个整数类型的 length,用于表示顺序表中元素的个数。
PSeqList:它是一个指针类型指向 SeqList 结构体类型,它可以被用于声明指向顺序表的指针变量。
SeqList 是一个结构体类型名,而 *PSeqList 是一个指向 SeqList 结构体的指针类型名。因此,当我们声明一个 SeqList 类型的变量时,我们可以写成 SeqList mylist;,而当我们声明一个指向 SeqList 类型的指针变量时,我们可以写成 PSeqList plist;。在这个例子中,SeqList 和 *PSeqList 实际上是等价的,因为它们都是用于表示顺序表的数据类型。

//顺序表的初始化 
PSeqList  Init_SeqList()
{
    
      /*创建一顺序表,入口参数无,返回一个指向顺序表的指针,指针值为零表示分配空间失败*/
	PSeqList  PL;
	PL =( PSeqList )malloc(sizeof(SeqList));
	if (PL)       /*若PL=0表示分配失败*/
		PL -> length =0;     
	return (PL);
}

这是一个C语言函数Init_SeqList(),用于创建一个新的顺序表(也称为数组或列表),并返回一个指向它的指针。
第一行声明了一个名为Init_SeqList()的函数,它返回一个指向SeqList结构的指针。
第三行使用malloc()函数分配了一个新的SeqList结构的内存,并返回一个指向分配内存块的指针。sizeof(SeqList)参数指定了要分配的内存块的大小,即SeqList结构的大小。(PSeqList)强制类型转换用于将malloc()的返回值转换为PL指针的类型,即指向SeqList结构的指针类型。如果malloc()函数无法分配内存块,则返回一个NULL指针。
第四行检查指针PL是否不等于NULL,这意味着malloc()成功为SeqList结构分配了内存。如果PL等于NULL,则表示内存分配失败。
第五行将SeqList结构中的length字段设置为0,表示新创建的顺序表中没有任何元素。

1.1.2 求顺序表的长度

//求顺序表的长度
 int Length_SeqList (PSeqList  PL)
{
    
      /*求顺序表的长度,入口参数:为顺序表,返回表长*/
	return (PL->length);
}  

这段代码是一个 C 语言函数 Length_SeqList(),它用于计算顺序表的长度并返回该长度。函数的参数是一个指向顺序表的指针 PL,函数返回一个整数值表示该顺序表的长度。
函数返回顺序表的长度,该长度存储在 SeqList 结构体中的 length 成员变量中。**PL->length 表示指针 PL 所指向的 SeqList 结构体中的 length 成员变量的值。**函数返回该值作为函数的返回值。

1.1.3 顺序表的检索操作

//顺序表的检索操作
int Location_SeqList (PSeqList  PL, DataType  x)
{
    
      /*顺序表检索,入口参数:为顺序表,检索元素,返回元素位置,0示查找失败*/
	int i=0;
    while (i< PL->length && PL->data[i]!= x)
        i++;
    if (i>=PL->length)  return 0;
    else   return (i+1);  
} 

这是一个 C 语言函数 Location_SeqList(),用于在给定的顺序表中查找指定的元素 x,并返回该元素在顺序表中的位置。
第一行声明了一个名为 Location_SeqList() 的函数,它有两个参数:一个指向 SeqList 结构的指针 PL,表示待查找的顺序表;一个数据类型为 DataType 的参数 x,表示待查找的元素。该函数返回一个整数值,表示指定元素在顺序表中的位置,如果未找到则返回0。
第二行定义了一个整数变量 i,并初始化为0,它将被用于迭代查找顺序表中的元素。
第三行使用一个 while 循环来迭代查找顺序表中的元素,直到找到指定元素 x 或遍历完整个顺序表。该循环有两个条件:顺序表中仍然有元素需要查找(即 i < PL->length),并且当前元素不等于指定元素 x(即 PL->data[i] != x)。如果当前元素等于指定元素,循环将终止。
第五行检查循环是否已经查找完整个顺序表。如果是,它返回0,表示未找到指定元素。否则,它返回当前位置 i+1,表示指定元素在顺序表中的位置。这里将位置加1的原因是,数组下标从0开始计数,但是通常人们更习惯从1开始计数。

问:平均比较次数为?
在顺序表中查找指定元素的平均比较次数可以用如下公式计算:
平均比较次数 = (1 + 2 + … + n) / n = (n+1) / 2
其中,n 表示顺序表中元素的个数。
因为在最坏情况下,需要比较整个顺序表中的元素才能确定指定元素是否存在,所以在平均情况下,顺序表的查找效率是线性的,时间复杂度为 O(n)。但是,在实际情况下,顺序表中的元素通常是无序的,这种情况下可以考虑使用更高效的数据结构,例如二叉搜索树或哈希表等。

1.1.4 顺序表的插入运算

//顺序表的插入运算
int Insert_SeqList(PSeqList PL,int i,DataType  x)
{
    
       /*在顺序表的第i个元素之前插入x,入口参数:顺序表指针,插入位置,插入元素,
返回标志,1表示成功,0表示插入位置不合法,-1表示溢出,-2表示表不存在*/
	int  j;
  	if (!PL)
	{
    
    
    	printf("表不存在\n");
  		return(-2);              /*表不存在,不能插入*/
	}  
    if (PL-> length >= MAXSIZE)
	{
    
     
		printf("表溢出\n"); 
		return(-1);            /*表空间已满,不能插入*/
	} 
   	if (i<1||i>PL->length+1)/*检查插入位置的合法性*/
	{
    
    
        printf("插入位置不合法\n"); 
		return(0); 
	} 
    for(j= PL -> length -1; j>=i-1; j--)
        PL ->data[j+1]= PL ->data[j];    /* 移动元素 */
    PL->data[i-1]=x;  /*新元素插入*/
    PL-> length++;  /*表长加 1*/
    return (1);  /*插入成功,返回*/
}

需要注意的是,这段代码并没有考虑到插入位置为表尾的情况,因为在这种情况下可以直接将新元素添加到表尾,不需要移动元素。此外,在移动元素时,应该从表尾开始向插入位置移动,因为从表头开始移动元素会覆盖掉一些原本存在的元素。

问:!PL ?
!PL是一个逻辑非运算符,表示对PL进行逻辑非运算,即如果PL为0(空指针),则返回真,否则返回假。在该函数中,如果顺序表PL不存在,即PL指向空,!PL的结果为真,会执行if语句块中的代码,输出提示信息"表不存在",并返回-2表示插入失败。如果顺序表PL存在,即PL指向一个非空的顺序表结构体,!PL的结果为假,不会执行if语句块中的代码,程序继续往下执行。

1.1.5 顺序表的删除运算

//顺序表的删除运算
int Delete_SeqList(PSeqList PL,int i)
{
    
      /*删除顺序表第i个元素,入口参数:顺序表指针,删除元素位置,
返回标志1表示成功,0表示删除位置不合法,-1表示表不存在*/
	int  j;
	if (!PL)
	{
    
     
		printf("表不存在\n"); 
		return(-1); /*表不存在,不能删除元素*/
	} 
 	if(i<1 || i> PL -> length)  /*检查删除位置的合法性*/
	{
    
      
        printf ("删除位置不合法\n"); 
		return(0);
   	} 
    for(j=i;j< PL -> length;j++)
        PL ->data[j-1]= PL ->data[j]; /*向上移动*/
	PL -> length --;    
	return (1);        /*删除成功*/
}

该函数的作用是删除顺序表中第i个元素,其入口参数为顺序表指针PL和删除元素位置i。如果PL为0,即指向空的指针,则说明表不存在,返回-1表示删除失败;如果i的值小于1或大于顺序表的长度,即删除位置不合法,返回0表示删除失败。如果以上检查都通过,就需要将第i个元素删除并向上移动其他元素,最后将表长减1,表示删除成功,返回1。

问:上述删除算法中被删除的数据元素没有保存下来,如果要保存下来,该如何做?
如果要保存被删除的数据元素,可以在函数定义中增加一个参数,作为保存被删除元素的变量指针
例如:

int Delete_SeqList(PSeqList PL, int i, DataType* deletedItem)

函数执行后,被删除的元素将会保存在 deletedItem 所指向的内存中。在函数中,需要修改删除元素的逻辑,将被删除的元素保存在 deletedItem 中。
具体实现可参考以下代码:

int Delete_SeqList(PSeqList PL, int i, DataType* deletedItem)
{
    
      
    /* 删除顺序表第i个元素,入口参数:顺序表指针,删除元素位置,
    返回标志1表示成功,0表示删除位置不合法,-1表示表不存在 */

    if (!PL) {
    
     
        printf("表不存在\n"); 
        return(-1); /*表不存在,不能删除元素*/
    } 

    if (i < 1 || i > PL->length) {
    
     /*检查删除位置的合法性*/
        printf("删除位置不合法\n"); 
        return(0);
    } 

    *deletedItem = PL->data[i-1]; /* 保存被删除元素 */

    for (int j = i; j < PL->length; j++) {
    
    
        PL->data[j-1] = PL->data[j]; /*向上移动*/
    }

    PL->length--;    
    return (1); /*删除成功*/
}

调用该函数时,需要提供一个指向保存被删除元素的变量的指针。例如:

DataType deletedItem;
int result = Delete_SeqList(seqList, 2, &deletedItem);
if (result == 1) {
    
    
    printf("删除成功,被删除的元素是:%d\n", deletedItem);
}

这行代码调用了 Delete_SeqList 函数,将 seqList 中的第 2 个元素删除,并将被删除的元素保存在 deletedItem 中。函数返回的结果为整数类型,1 表示删除成功,0 表示删除位置不合法,-1 表示表不存在。

如果 result 的值为 1,则说明删除成功,可以通过 printf 函数输出被删除的元素值。如果 result 的值为 0 或 -1,则说明删除失败,此时不应该输出被删除的元素值。

1.1.6 顺序表的销毁运算

//销毁顺序表
int Destroy_SeqList (PSeqList  *PL)
{
    
      /*顺序表检索,入口参数:为顺序表,检索元素,返回元素位置,0示查找失败*/
	if(*PL)
		free(*PL);
	*PL=NULL;
}

这个函数的功能是销毁一个顺序表参数是一个指向顺序表指针的指针。顺序表销毁后,顺序表指针需要被置为 NULL。
具体实现是:如果顺序表指针不为 NULL,就调用 free() 函数释放其指向的内存空间,然后将顺序表指针置为 NULL。
函数的返回值类型应该是 void,因为销毁顺序表后没有任何返回值。

:free(PL)?
这行代码是用来释放顺序表占用的内存空间,从而达到销毁顺序表的目的。在使用malloc等函数动态分配内存时,一定要记得在使用完毕后调用free函数释放所占用的内存空间,否则会造成内存泄漏。此处的
PL表示指向顺序表结构体的指针,free(*PL)即为释放该指针指向的内存空间。

:*PL=NULL?

*PL=NULL; 是将传入的指向顺序表的指针设置为 NULL,这是为了避免野指针的出现,以确保程序的安全性。在程序中,已经释放了指向顺序表的内存空间,如果不将指针设置为 NULL,那么该指针仍指向一个已释放的内存空间,这时如果再去使用这个指针,就会出现未定义的行为,甚至会导致程序崩溃。因此,将指针设置为 NULL 是一种良好的编程习惯。

1.1.7 顺序表的遍历

//顺序表的遍历         
void DispList(PSeqList  L)  /*输出线性表*/
{
    
    
    int i;
    printf("  NO           DATA   \n");
    for (i=0;i<L->length;i++)
    {
    
    
        printf("  %d           %d",i+1,L->data[i]);
        printf("\n");
     }
    printf("目前表中元素总数:%d\n",L->length);
    printf("\n"); 
}

这段代码实现了顺序表的遍历操作,即按照从前往后的顺序依次输出表中的元素以及对应的下标。
在函数中,首先使用一个for循环遍历表中的每个元素,循环变量i从0开始,每次循环自增1,直到循环结束,也就是遍历完整个表。循环体内,通过printf函数将当前元素的下标i+1以及对应的数据值**L->data[i]**输出到控制台上,中间用制表符"\t"分隔。另外,为了方便查看,输出结果中还加入了表头信息和表长信息。
最终,该函数将整个表遍历一遍,输出了表中所有元素以及表长信息。

猜你喜欢

转载自blog.csdn.net/aaaccc444/article/details/130474981