数据结构 _CH03_线性表(顺序表)

数据结构 _CH03_线性表(顺序表)

1.1知识回顾

在这里插入图片描述

2.1线性表的定义和特点

2.1.1定义

线性表是相同特性元素的一个有限序列

a1 a2 ...ai-1 ai ai+1 ... an
线						  线
性						  性						   
起						  终
点						  点

ai-1是ai的直接前驱
ai+1是ai的直接后继
a1又称起始结点
an又称终端结点
i是序号,表示元素在线性表当中的位置
n表示表长
n = 0表示空表
数据元素ai只是一个抽象符号,在不同条件下,其具体含义不一样。

e.g.1

字母表是线性表
(A,B,C,D,E,...,X,Y,Z)
元素特性相同,并且关系线性。

e.g.2
在这里插入图片描述

2.1.2特点

1、非空线性表,其起始结点a1,有且只有一个直接后继,没有直接前趋。终端结点类似。
2、除此之外的内部结点,有且只有一个直接前趋和直接后继。

2.1.3案例引入

在这里插入图片描述
两个多项式相加的运算。

Pn(x)每一项的指数i隐含在其系数Pi当中。
可以利用<数组>来实现

在这里插入图片描述
但是如果出现稀疏多项式,我们就需要把每个项的指数变成变量:
在这里插入图片描述

2.2线性表的实现

ADT List
{
数据对象:D = {ai| ai ∈ Elemtype , i = 1,2...n,n>= 0}
数据关系:R = { <ai-1, ai>|ai-1,ai ∈ D , i = 2,3,...n}
基本操作:
InitList(*L) //初始化线性表
ListEmpty(*L) //返回线性表是否为空,空返回True,非空返回False
ClearList(*L) //清空线性表
GetElem(L , i , *e) //把表中第i个元素返回给e
LocateElem(L,e) //找表中有没有跟e一样的元素,没有则返回0,有则返回元素下标
ListInsert(*L , i , e) //在第i个位置插入元素e
ListDelete(*L , i , *e) //删除表中第i个位置的元素e,并返回e的值
ListLength(L) //返回表长度
PriorElem(L,cur_e,*pur_e) //求前驱
NextElem(L,cur_e,*next_e) //求后继
ListTraverse(*L , visited()) //遍历线性表(对每个元素作visited()操作)
ShowList(*L)//打印整个线性表

}ADT List

//以上运算都是逻辑结构上定义的运算,而实现细节,只有等到存储结构确定后再来具体地实现。

2.2.1线性表的顺序实现

顺序表是顺序存储结构实现的线性表,又称线性表的顺序存储结构或者顺序映像。即把逻辑上相邻的数据元素,在物理位置上也相邻存储。
在这里插入图片描述

第一个元素的存储位置又称顺序表的基地址或者起始地址
另外,存储地址中间不能出现空地址,如果出现空地址也是不行的。
在这里插入图片描述
若已知一个元素占据的存储单元为h,则

LOC(a i+1) = LOC(ai) + h

在这里插入图片描述
因此,存储结构上:顺序表只要知道第一个元素的位置,就可以计算每一个元素的位置:

LOC(ai) = LOC(a1) + ( i-1 )*h

又因为逻辑结构和存储结构一一对应,所以每一个元素的值也是可以求出来的。
计算的时间复杂度是O(1)
在这里插入图片描述

对于存储元素的要求:

地址连续
依次存放
随机存取
类型相同

我们可以发现,这与C语言当中所学的数组是一致的。因此我们用一维数组来表示顺序表。
但是,线性表的长度可变,数组的长度是不可以动态定义的。因此需要自己定义一个一个常量表达式。

数组的定义:
数据类型 arr[常量表达式]
这个常量表达式可以是常量,也可以是表示常量的符号,但就是不能是变量。
#define List_Init_Size 100 //定义数组初始定义时分配的空间
typedef struct {
	Elemtype elem[List_Init_Size];
	int length; //当前表长度

}SqList;

这个Elemtype表示元素类型,它可以是int char float等等。

typedef char Elemtype;
typedef int Elemtype;
typedef float Elemtype;
typedef stuct {

}Elemtype;//定义复杂类型

对于这一个多项式:
在这里插入图片描述
他的线性表实现为:

#define MAXSIZE 1000 //顺序表所能达到的最大长度

typedef struct {
	float p; //系数
	int e; //指数
}Polynomial;

typedef struct {
	Polynomial *elem; //存储空间的基地址
	int length; //多项式当前的项数
}SqList;

另外类似图书表的线性表定义:
在这里插入图片描述

#define MAXSIZE 1000

typedef struct {
	char num[20]; //ISBN码
	char name[50]; //书名
	int price; //定价
}Book;

typedef struct {
	Book *elem; //定义基地址
	int length;	 //当前存储的图书个数
}SqList;

2.2.2线性表的动态分配与静态分配的区别

静态分配

#define MaxSize 1000

typedef struct {
	Elemtype data[MaxSize];
	int length;
}SqList;

定义的这个数组名data存放的是数组的基地址,数组的大小直接定义为MaxSize

动态分配

#define MaxSize 1000

typedef struct {
	Elemtype *data;
	int length;
}SqList;

//定义的是一个指针类型data,存放第一个元素的地址,至于数组到底有多大,要用动态内存分配的函数malloc来实现。

SqList L; //定义一个SqList类型的表 L
L.data = (Elemtype*)malloc(sizeof(Elemtype)* MaxSize) //动态分配内存

另外,使用malloc需要加上头文件<stdlib.h>。完整代码

#define MaxSize 1000
#include<stdio.h>
#include<stdlib.h>

typedef struct {
	Elemtype *data;
	int length;
}SqList;

int main()
{
	SqList;
	L.data = (Elemtype*)malloc(sizeof(Elemtype)* MaxSize);
}

malloc的用法:
malloc 函数是 C 语言标准库函数之一,用于动态地在堆(heap)上分配内存。这个函数定义在 <stdlib.h> 头文件中。使用 malloc 时,你需要指定希望分配的字节数,它会返回一个指向该内存块的指针。如果内存分配失败,则返回 NULL。

void* malloc(size_t size);

这里的MaxSize即元素个数,而sizeof(Elemtype)计算的是这一个元素类型有几个字节,那么总字节数就是sizeof(Elemtype)*MaxSize。Elemtype 决定了存储什么类型,而 * 则是因为L.data是一个指针类型,要与之匹配。

free§:释放指针p所指变量的存储空间,即彻底删除一个变量。

2.2.3顺序表的基本操作具体实现

需要注意的是,线性表的逻辑位序和物理位序相差1。
在这里插入图片描述
若定义了一个SqList类型的变量L:有两种方式

SqList L;
SqList* L;

则它们访问成员的操作分别是

L.data
L.length
L->data
L->length
2.2.3.1 初始化操作

动态分配的初始化操作

#define MaxSize 100
#include<stdio.h>
#include<stdlib.h>
typedef int Elemtype;//通过这里更改Elemtype的数据类型
//给出线性表的结构定义
typedef struct {
	Elemtype* arr;
	int n;//整体申请的容量
	int length;//当前的元素个数
}SqList;

//初始化线性表
SqList* InitList(int n)//返回指针类型
{
	SqList* L = (SqList*)malloc(sizeof(Elemtype)* n); //定义一个顺序表类型的变量,采用指针形式定义
	if(!L) return NULL; //如果内存申请失败,即L为空指针,则返回NULL。这样可以提高代码的健壮性
	L->length = 0;//长度初始为0
	l->n = n;
	return L;//返回指针,跟函数返回类型相符。 
}

时间复杂度:O(1)

2.2.3.2插入

初始化操作之后,线性表里还没有任何内容,所以我们先插入
(在表第i元素的位置插入e)

#define _CRT_SECURE_NO_WARNINGS
#define MaxSize 100
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int Elemtype; // 定义Elemtype的数据类型

typedef struct {
    Elemtype* arr;    // 存储元素的数组
    int n;            // 整体申请的容量
    int length;       // 当前元素个数
} SqList;

// 初始化线性表
SqList* InitList(int n) {
    SqList* L = (SqList*)malloc(sizeof(SqList)); // 为SqList结构体分配内存
    if (!L) return NULL; // 如果内存申请失败,则返回NULL

    L->arr = (Elemtype*)malloc(sizeof(Elemtype) * n); // 为Elemtype数组分配内存
    if (!L->arr) {
        free(L); // 如果数组内存申请失败,释放结构体内存
        return NULL;
    }

    L->n = n;
    L->length = 0; // 长度初始为0
    return L; // 返回指针
}

// 插入元素
bool ListInsert(SqList* L, int i, Elemtype e) {
    if (i < 0 || i > L->length) return false; // 插入位置i的判断

    if (L->length == L->n) { // 如果表满了,扩容
        Elemtype* tmp = (Elemtype*)realloc(L->arr, sizeof(Elemtype) * (L->n * 2));
        if (!tmp) return false; // 申请空间失败则报错
        L->arr = tmp;
        L->n *= 2;
    }

    // 将i及之后的元素向后移动一位
    for (int j = L->length; j >= i; j--) {
        L->arr[j] = L->arr[j - 1];
    }
    L->arr[i] = e; // 在位置i插入元素e
    L->length++;
    return true;
}

// 释放线性表内存
void FreeList(SqList* L) {
    free(L->arr);
    free(L);
}

int main() {
    SqList* P = InitList(100);
    if (!P) {
        printf("Memory allocation failed for the list.\n");
        return 1;
    }

    for (int i = 0; i < 100; i++) {
        if (!ListInsert(P, i, i)) {
            printf("Insertion failed at position %d.\n", i);
            FreeList(P); // 插入失败,释放内存
            return 1;
        }
    }

    // 打印线性表的内容,检查插入结果
    printf("List content after insertion:\n");
    for (int i = 0; i < P->length; i++) {
        printf("%d ", P->arr[i]);
    }
    printf("\n");

    // 释放线性表内存
    FreeList(P);

    return 0;
}

返回结果

List content after insertion:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 

时间复杂度

在这里插入图片描述

2.2.3.3查找下标

查找第i个元素(1<=i<=n)
第i个元素的下标是i-1。

bool GetElem(SqList* L,Elemtype* e,int i)
{
	//判误
	if(i<0 || L->length < 0 || i>L->n)return false;
	*e = L->arr[i-1]; //对指针e解引用,则访问了e的地址,把arr[i-1]给了这个地址,即把arr[i-1]找到的这个元素放在了e指向的地址里,说明能够找到。
	return true;
}

合并之前的代码,我们可以看到找到的元素是4

#define _CRT_SECURE_NO_WARNINGS
#define MaxSize 100
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int Elemtype; // 定义Elemtype的数据类型

typedef struct {
    Elemtype* arr;    // 存储元素的数组
    int n;            // 整体申请的容量
    int length;       // 当前元素个数
} SqList;

// 初始化线性表
SqList* InitList(int n) {
    SqList* L = (SqList*)malloc(sizeof(SqList)); // 为SqList结构体分配内存
    if (!L) return NULL; // 如果内存申请失败,则返回NULL

    L->arr = (Elemtype*)malloc(sizeof(Elemtype) * n); // 为Elemtype数组分配内存
    if (!L->arr) {
        free(L); // 如果数组内存申请失败,释放结构体内存
        return NULL;
    }

    L->n = n;
    L->length = 0; // 长度初始为0
    return L; // 返回指针
}

// 插入元素
bool ListInsert(SqList* L, int i, Elemtype e) {
    if (i < 0 || i > L->length) return false; // 插入位置i的判断

    if (L->length == L->n) { // 如果表满了,扩容
        Elemtype* tmp = (Elemtype*)realloc(L->arr, sizeof(Elemtype) * (L->n * 2));
        if (!tmp) return false; // 申请空间失败则报错
        L->arr = tmp;
        L->n *= 2;
    }

    // 将i及之后的元素向后移动一位
    for (int j = L->length; j >= i; j--) {
        L->arr[j] = L->arr[j - 1];
    }
    L->arr[i] = e; // 在位置i插入元素e
    L->length++;
    return true;
}

//查找元素
bool GetElem(SqList* L,Elemtype* e,int i)
{
	//判误
	
	if(i<0 || L->length < 0 || i>L->n)return false;
	*e = L->arr[i-1]; //对指针e解引用,则访问了e的地址,把arr[i-1]给了这个地址,即把arr[i-1]找到的这个元素放在了e指向的地址里,说明能够找到。
	return true;
}

// 释放线性表内存
void FreeList(SqList* L) {
    free(L->arr);
    free(L);
}

int main() {
    SqList* P = InitList(100);
    if (!P) {
        printf("Memory allocation failed for the list.\n");
        return 1;
    }

    for (int i = 0; i < 100; i++) {
        if (!ListInsert(P, i, i)) {
            printf("Insertion failed at position %d.\n", i);
            FreeList(P); // 插入失败,释放内存
            return 1;
        }
    }

    // 打印线性表的内容,检查插入结果
    printf("List content after insertion:\n");
    for (int i = 0; i < P->length; i++) {
        printf("%d ", P->arr[i]);
    }
    printf("\n");
    
    Elemtype element;
    int indexToPrint = 5; // 假设你想打印索引为 5 的元素

    // 调用 GetElem 函数来获取元素
    if (GetElem(P, &element, indexToPrint)) {
        // 如果 GetElem 返回 true,说明元素成功获取
        printf("Element at index %d is: %d\n", indexToPrint, element);
    } else {
        // 如果 GetElem 返回 false,说明获取元素失败
        printf("Failed to get element at index %d\n", indexToPrint);
    }

    // 释放线性表内存
    FreeList(P);

    return 0;
}

输出:

List content after insertion:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 
Element at index 5 is: 4

时间复杂度:
在这里插入图片描述

2.2.3.4查找元素

查找第一个值为e的元素,返回其下标

int LocateElem(SqList* L,Elemtype e)
{
	for(int i = 0; i<=L->length ; i++)
	{
		if(L->arr[i] == e)
		{
			return i;
		}
	}
	return -1;
}


合并之前的代码

#define _CRT_SECURE_NO_WARNINGS
#define MaxSize 100
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int Elemtype; // 定义Elemtype的数据类型

typedef struct {
    Elemtype* arr;    // 存储元素的数组
    int n;            // 整体申请的容量
    int length;       // 当前元素个数
} SqList;

// 初始化线性表
SqList* InitList(int n) {
    SqList* L = (SqList*)malloc(sizeof(SqList)); // 为SqList结构体分配内存
    if (!L) return NULL; // 如果内存申请失败,则返回NULL

    L->arr = (Elemtype*)malloc(sizeof(Elemtype) * n); // 为Elemtype数组分配内存
    if (!L->arr) {
        free(L); // 如果数组内存申请失败,释放结构体内存
        return NULL;
    }

    L->n = n;
    L->length = 0; // 长度初始为0
    return L; // 返回指针
}

// 插入元素
bool ListInsert(SqList* L, int i, Elemtype e) {
    if (i < 0 || i > L->length) return false; // 插入位置i的判断

    if (L->length == L->n) { // 如果表满了,扩容
        Elemtype* tmp = (Elemtype*)realloc(L->arr, sizeof(Elemtype) * (L->n * 2));
        if (!tmp) return false; // 申请空间失败则报错
        L->arr = tmp;
        L->n *= 2;
    }

    // 将i及之后的元素向后移动一位
    for (int j = L->length; j >= i; j--) {
        L->arr[j] = L->arr[j - 1];
    }
    L->arr[i] = e; // 在位置i插入元素e
    L->length++;
    return true;
}

//查找下标
bool GetElem(SqList* L,Elemtype* e,int i)
{
	//判误
	
	if(i<0 || L->length < 0 || i>L->n)return false;
	*e = L->arr[i-1]; //对指针e解引用,则访问了e的地址,把arr[i-1]给了这个地址,即把arr[i-1]找到的这个元素放在了e指向的地址里,说明能够找到。
	return true;
}

//查找元素
int LocateElem(SqList* L,Elemtype e)
{
	for(int i = 0; i<=L->length ; i++)
	{
		if(L->arr[i] == e)
		{
			return i;
		}
	}
	return -1;
}

// 释放线性表内存
void FreeList(SqList* L) {
    free(L->arr);
    free(L);
}

int main() {
    SqList* P = InitList(100);
    if (!P) {
        printf("Memory allocation failed for the list.\n");
        return 1;
    }

    for (int i = 0; i < 100; i++) {
        if (!ListInsert(P, i, i)) {
            printf("Insertion failed at position %d.\n", i);
            FreeList(P); // 插入失败,释放内存
            return 1;
        }
    }

    // 打印线性表的内容,检查插入结果
    printf("List content after insertion:\n");
    for (int i = 0; i < P->length; i++) {
        printf("%d ", P->arr[i]);
    }
    printf("\n");
    
    Elemtype element;
    int indexToPrint = 5; // 假设你想打印索引为 5 的元素

    // 调用 GetElem 函数来获取元素
    if (GetElem(P, &element, indexToPrint)) {
        // 如果 GetElem 返回 true,说明元素成功获取
        printf("Element at index %d is: %d\n", indexToPrint, element);
    } else {
        // 如果 GetElem 返回 false,说明获取元素失败
        printf("Failed to get element at index %d\n", indexToPrint);
    }
    
    
    //调用LocateElem查找元素
    int index = LocateElem(P,9);
    printf("%d\n",index);

    // 释放线性表内存
    FreeList(P);

    return 0;
}

输出

List content after insertion:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 	37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 
Element at index 5 is: 4
9

时间复杂度:
经过了一个循环,所以时间复杂度是O(n)

2.2.3.5删除

删除第i个元素(1<= i <= n),并将其放入指针e所指的空间,成功返回true,失败返回false

bool ListDelete(SqList* L , int i , Elemtype* e)
{
	//判误,表空或者i值异常
	if(!L->length||i<1||L->n)return false;
	//把第i个元素【即下标为i-1】放入指针e内的空间
	*e = L->arr[i-1];
	//把第i个元素之后的元素全部向前移动一格
	for(int j = i; j < L->length ; j++)
	{
		L->arr[j-1] = L->arr[j];
	}
	L->length--;
	return true;
}

时间复杂度:
在这里插入图片描述

2.2.3.6清空

清空线性表所有元素,但是不是删除线性表。
利用free()函数释放内存,并且要检查是否为空

void ClearList(SqList* L)
{
	if(L-> != NULL)
	{
		free(L->arr);
	}
	L->length = 0;
}
2.2.3.7输出整个线性表

之前我们在主函数用for循环将线性表的所有元素全部打印了,我们可以将输出整个线性表的操作封装一下。

void ShowList(SqList *L)
{
	if(!L || ! L->length)return false;
	for(int j = 0 ; j<L->length ; j++)
	{
		printf("这是第%d个元素: %d",j+1,L->arr[j]);
	}
	printf("\n");
}
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值