2.2 顺序表

顺序表

定义

​ 用顺序存储的方式实现线性表的顺序存储。把逻辑上相邻的元素存储在物理位置也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。

​ 顺序表这种物理位置相邻的数据结构是基于数组实现的。数据结构作用的数据元素是数组中的元素,而数组是数据对象,故而对顺序表的操作,即是对单个数组进行操作。这也是与其他线性表不同的一个方面。

​ 按顺序表的长度是否可变,分为静态顺序表动态顺序表

静态顺序表

定义

​ 内存空间始终固定不变的顺序表。

下面的代码中类似 int &n 的代码; 的的语句属于 C++, C语言不能编译。
含义为:声明 n 为一个 int 类型的引用 。
如函数调用中将 实参 a 传给 n, 表示 n 为 a 的引用,或者说 a 的别名,在函数中将直接操作 a 中的内容。

#define MAXSIZE 10		// 顺序表最大长度
typedef struct {		// 定义顺序表元素结构,并设置别名
	int data[MAXSIZE] 	// 用静态数组来存放数据元素
	int length;			// 顺序表当前长度
} SeqList; 				// Sequence:顺序

基本操作及时间复杂度

1、初始化

​ 初始化要遍历整个数组,循环次数为数组长度n,故时间复杂度为 O(n)。

// 1、初始化 
void InitSeqList(SeqList &L){
	// 随机分配的内存空间都存在垃圾数据,清空所有垃圾数据。
	for(int i=0; i<MAXSIZE; i++){
		L.data[i] = 0;
	} 
	// length中也存有垃圾值,只有static全局变量分配到的内存才会是 0 
	L.length = 0;	// 顺序表的长度初始为 0; 
} 
2、插入

​ 插入数据时要将后面的元素都向后移,最坏情况下要将数组所有元素向后移,循环次数为数组的长度n,故时间复杂度为 O(n)。

// 2、插入元素
bool ListInsert(SeqList &L, int i, int data){
    // 检查位序i 是否正确。位序从1开始。
    if (i<1 || i > L.length+1) return false;
    // 检查数组是否装满
    if (L.length >= MAXSIZE) return false;
    
    // 将位序i上的元素及其后面的元素后移。数组下标从0开始,比位序小1。
    for (int j=L.length; j>=i; j--){
        L.data[j] = L.data[j-1];
    }
    
    // 在位序为 i 处插入元素, 表尾指针后移。
    L.data[i-1] = data;
    L.length ++;
    
    return true;
}
3、删除

​ 删除元素需要将后面的元素向前移。最坏的情况下需要移动数组中除第一个元素外的所有元素,循环次数为 n-1,时间复杂度为 O(n)

// 3、删除元素; 删除 i 处的
bool ListDelete(SeqList &L, int i, int &e){
    // 检查位序i 是否正确
    if (i<1 || i > L.length) return false;
    
    // 将位序i 上的元素通过 e 传递出去。
    e = L.data[i-1];
    
    // 向前移动位序i后面的元素
    for( ; i<L.length; i++){
        L.data[i-1] = L.data[i];
    }
    
    // 表尾指针前移
    L.length--;
    
    return true;
}
4、按位查找

​ 由于顺序表的各个数据元素在内存中连续存放,因此可以根据起始地址和数据元素大小立即找到第 i 个元素——“随机存取”特性。

​ 时间复杂度为 O(1)。

// 4、按位查找
int ListLocated(SeqList &L, int i){
    // 检查位序i 是否正确
    if (i<1 || i > L.length) return 0;
    // 返回位序为 i 的元素。
    return L.data[i-1];
}
5、按值查找

​ 按值查找需要对比每个元素,无法提前知道元素的位置,故而需要遍历整个数组。最坏情况下,遍历完数组所有元素,循环执行n次,时间复杂度为 O(n)。

// 5、按值查找
int ListFind(SeqList &L, int e){
    // 按顺序遍历所有元素
    for(int i=0; i<L.length; i++){
        if (L.data[i] == e)         // 找到元素,返回位序
            return i+1;     
    }
    return 0;   // 遍历完所有元素仍未找到,返回0,而位序从1开始。
}

动态顺序表

定义

顺序表满了以后,如果再插入数据,就重新开辟一个更大的空间。

// 动态顺序表 
#define InitSize 10 	// 顺序表的初始长度
typedef struct{
	int *data; 			// 指示动态分配数组的指针
	int MaxSize; 		// 顺序表的最大容量
	int length; 		// 顺序表的当前长度
} DySeqList;			// dynamic: 动态的 

基本操作

动态顺序表的初始化、与插入操作与静态顺序表稍有不同,且多一个增加数组长度的操作。其他操作均与静态数组一致。

1、初始化

数据初始化需要遍历整个数组,时间复杂度为 O(n)。

// 1、初始化
void InitSeqList(DySeqList &L){
	// malloc 动态申请一块连续的内存空间 
	L.data = (int *)malloc(InitSize*sizeof(int));
	L.length = 0;
	L.MaxSize = InitSize;
	// 动态分配的内存中也都是垃圾值,需要清空 
	for(int i=0; i<L.MaxSize; i++){
		L.data[i] = 0;
	}
}
2、增加数组长度

​ 复制数据和清理数组需要遍历整个数组,时间复杂度为 O(n)。

// 2、增加动态数组的长度 
void IncreaseSize(DySeqList &DL, int len){
    // n 增加的长度
    int *p = DL.data;
    DL.MaxSize += len;
    // 重新申请空间 
    DL.data = (int*)malloc(DL.MaxSize*sizeof(int));
    
    // 复制数据 
    int i = 0;
    for ( ; i<DL.length; i++){
        DL.data[i] = p[i];
    }
    
    // 清除垃圾数据
	for ( ; i<DL.MaxSize; i++){
        DL.data[i] = 0;
    }
    
    // 释放旧内存
    free(p);
}
3、插入

​ 插入数据与静态顺序表不同,动态数组满了后可以增加数组长度,而不是返回 false。

​ 最坏情况下,需要先增加数组长度,再插入元素, 时间复杂度都为 O(n),和仍然为 O(n)。

​ 时间复杂度为 O(n)。

// 3、插入元素
bool ListInsert(DySeqList &L, int i, int data){
    // 检查位序i 是否正确。位序从1开始。
    if (i<1 || i > L.length+1) return false;
    // 检查数组是否装满
    if (L.length >= MAXSIZE) 
        IncreaseSize(L, InitSize);
    
    // 将位序i上的元素及其后面的元素后移。数组下标从0开始,比位序小1。
    for (int j=L.length; j>=i; j--){
        L.data[j] = L.data[j-1];
    }
    
    // 在位序为 i 处插入元素, 表尾指针后移。
    L.data[i-1] = data;
    L.length ++;
    
    return true;
}
4、删除

​ 算法完全同静态顺序表,时间复杂度为 O(n)。

// 4、删除元素; 删除 i 处的
bool ListDelete(DySeqList &L, int i, int &e){
    // 检查位序i 是否正确
    if (i<1 || i > L.length) return false;
    
    // 将位序i 上的元素通过 e 传递出去。
    e = L.data[i-1];
    
    // 向前移动位序i后面的元素
    for( ; i<L.length; i++){
        L.data[i-1] = L.data[i];
    }
    
    // 表尾指针前移
    L.length--;
    
    return true;
}
5、查找

查找算法完全同静态顺序表,按位查找、按值查找的时间复杂度分别为 O(1)、O(n)。

// 5、按位查找
int ListLocated(DySeqList &L, int i){
    // 检查位序i 是否正确
    if (i<1 || i > L.length) return 0;
    // 返回位序为 i 的元素。
    return L.data[i-1];
}

// 6、按值查找
int ListFind(DySeqList &L, int e){
    // 按顺序遍历所有元素
    for(int i=0; i<L.length; i++){
        if (L.data[i] == e)         // 找到元素,返回位序
            return i+1;
    }
    return 0;   // 遍历完所有元素仍未找到,返回0,而位序从1开始。
}

posted @ 2021-10-19 20:23  流水自净  阅读(54)  评论(0编辑  收藏  举报