从头开始嵌入式编程学习-数据结构-线性表-顺序线性表

学习流程主要包括分为11个部分

  1. 绪论
  2. 线性表
  3. 栈和队列
  4. 递归和分治思想
  5. 串(KMP算法)
  6. 数组和广义表
  7. 树和二叉树
  8. 动态存储管理
  9. 查找
  10. 排序

二 线性表 

线性表 栈和队列  串都是线性结构

        常用的线性结构有:线性表,栈,队列,循环队列,数组。线性表中包括顺序表、链表等 。  

线性结构:元素的非空有限集中

                1.存在唯一的一个被称作“第一个”的元素;

                2.存在唯一的一个被称作“最后一个”的元素;

                3.除第一个之外,集合中的每一个数据元素均只有一个前驱;

                4.除最后一个之外,集合中每一个数据元素均只有一个后继;

2.1 线性表类型定义

        线性表:线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同的特性,即属于同一数据对象,相邻的数据元素之间存在着序偶的关系,若记为:

(a_{1},...a_{i-1},a_{i},a_{i+1},...a_{n})

则表中a_{i-1}领先于a_{i}a_{i}领先于a_{i+1 },称a_{i-1}a_{i}的直接前驱元素,a_{i+1 }a_{i}的直接后继元素。当i=1,2,...,n-1时,a_{i}有且仅有一个直接后继;i=2,3,...,n时,a_{i}有且仅有一个直接前驱。

        线性表中元素的个数定义为n,当n=0 时候为空表。

2.2 线性表的顺序表示

2.2.1 顺序线性表结构体      

        定义一个线性表(因为抽象数据类型抽象,且个人不是很用得上,此处以顺序表来实现):

        定义一个动态分配顺序存储结构(因为线性表是一个灵活的数据结构,可以根据需要来增长,

和缩短.)

#include <stdio.h>
#include <stdlib.h>

//- - - - - 线性表的动态分配顺序存储结构 - - - - - 

#define LIST_INIT_SIZE 10	//线性表存储空间的初始分配量
#define LIST_INCREAMENT 5	//线性表存储空间的分配增量

typedef struct {
	int* elem;		// 存储空间基地址
	int length;		// 当前长度
	int listsize;	// 当前分配的存储空间大小
} DynamicSeqList;

2.2.2 顺序线性表的初始化       

        首先,对动态线性表进行初始化操作,构造一个空的线性表(初始化线性表)List。

// 初始化线性表
void InitList(DynamicSeqList* list) {
	// 构造一个空的线性表list
	//malloc函数 extern void *malloc(unsigned int num_bytes);
	//分配长度为num_bytes字节的内存块
	list->elem = (int*)malloc(sizeof(int) * LIST_INIT_SIZE);
	if (!list->elem) {						// 内存分配失败
		printf("内存分配失败!!!\n");
		exit(1);							// 以非正常方式退出(实际应为OVERFLOW)
	}
	list->length = 0;						// 空表长度为0;
	list->listsize = LIST_INIT_SIZE;		// 初始内存容量
}

2.2.3 顺序线性表元素的插入         

        线性表元素插入操作,C语言中即是指在线性表的第i-1个元素和第i个元素之间插入一个新的元素,使长度为n的线性表:

(a_{1},...a_{i-1},a_{i},...a_{n})

变为长度为n+1的线性表:

(a_{1},...a_{i-1},b,a_{i},...a_{n})

所以,在第i(1\leqslant i \leqslant n)个元素之前插入一个元素时,需要将第n至第i(共n-i+1)个元素向后移动一个位置:

// 插入元素
void ListInsert(DynamicSeqList* list, int position, int element) {
	// 在线性表L中第 position 个位置之前,插入新的元素 element
	// position的合法值为 1≤i≤list.length+1
	if (position<1 || position>list->length + 1) {
		printf("插入位置不合法");
		return;	//结束函数执行
	}
	// 如果当前存储空间已满,增加内存分配
	if (list->length >= list->listsize) {
		// 重新分配更大的内存空间
		// realloc更改已经配置的内存空间,即更改由malloc()函数非配得内存空间大小
		// realloc内存扩大有三种情况, 
		// 1. 当前内存段后有需要的内存空间,直接扩展,realloc返回原指针
		// 2. 当前内存段后的内存空间不足,那么就使用堆中第一个能够满足要求的内存块,
		//    将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置
		// 3. 如果申请失败,返回NULL,此时,原来的指针仍然有效。
		list->elem = (int*)realloc(list->elem, sizeof(int)*(list->listsize + LIST_INCREAMENT));
		if (!list->elem) {						// 内存分配失败
			printf("内存分配失败!!!\n");
			exit(1);							// 以非正常方式退出(实际应为OVERFLOW)
		}
		list->listsize += LIST_INCREAMENT;		//更新存储空间大小为扩容后大小
	}
	for (int i = list->length; i < position; i--) {		//将插入位置后的元素向后移动一位
		list->elem[i] = list->elem[i - 1];
	}
	list->elem[position - 1] = element;		// 在插入位置后插入新的元素
	list->length++;		// 更新线性表长度
}

2.2.4 顺序线性表元素的删除  

        线性表元素的删除操作是使长度为n的线性表:

(a_{1},...a_{i-1},b,a_{i},...a_{n})

变为长度为n-1的线性表:

(a_{1},...a_{i-1},a_{i},...a_{n})

        一般情况下,删除第i(1\leqslant i \leqslant n)个元素时需要将从第i+1至第n(共n-i)个元素依次向前移动一个位置:

// 删除元素
void ListDelete(DynamicSeqList* list, int position) {
	// 在线性表L中第 position 个位置删除一个元素
	// position的合法值为 1≤i≤list.length+1
	if (position<1 || position>list->length + 1) {
		printf("删除位置不合法");
		return;	//结束函数执行
	}
	for (int i = position; i < list->length; i++) { // 将删除位置后的元素向前移动
		list->elem[i - 1] = list->elem[i];
	}
	list->length--;	//更新线性表长度
}

        由上述顺序存储结构的线性表可见,顺序存储结构的线性表的某个位置上插入或者删除一个数据元素时,其时间主要耗费在移动元素上 ,而移动元素的个数取决于插入或者删除元素的位置:

        假设p _{i}是在第i个元素之前插入一个元素的概率,则长度为n的线性表中插入一个元素时所需移动元素次数的期望值为:

E _{p} = \sum_{i=1}^{n+1}p _{i}(n-i+1)

        假设q _{i}是删除第i个元素的概率,则长度为n的线性表中插入一个元素时所需移动元素次数的期望值为:

E_{q} = \sum_{i=1}^{n}q _{i}(n-i)

        一般的,我们可以假设线性表的任何位置上插入或删除的概率都是相等的,即

p _{i}=\frac{1}{n+1},q _{i}=\frac{1}{n};

\sum_{i=1}^{n+1}(n-i+1) = (0+n)(n+1)/2

\sum_{i=1}^{n}(n-i) = (0+n-1)(n)/2

        综合上述式子,可以得到:

E _{p} =\frac{1}{n+1}\sum_{i=1}^{n+1}(n-i+1)=\frac{n}{2}

E_{q} =\frac{1}{n}\sum_{i=1}^{n}(n-i) = \frac{n-1}{2}

        所以,在顺序存储结构的线性表中插入或者删除一个数据元素,平均约移动一半的元素,则算法ListInsert,和ListDelete的时间复杂度为O(n)

下面添加一些其他的基本操作。

2.2.5 顺序线性表元素的查找

// 查找元素

int ListSearch(DynamicSeqList* list, int element) {
	for (int i = 1; i < list->length; i++) {	// 遍历线性表中的元素
		if (list->elem[i] == element) {		//如果找到指定元素
			return i + 1;		//返回元素在线性表中的位置
		}
	}
	return -1;	//返回-1 表示未找到指定元素
}

2.2.6 顺序线性表元素的修改

void ListModify(DynamicSeqList* list,int position, int element) {
	if (position<1 || position>list->length + 1) {
		printf("修改位置不合法");	//若修改位置不合法则输出错误信息
		return;	// 结束函数执行
	}
	list->elem[position - 1] = element;	// 修改指定位置的元素值为新元素的值
}

2.2.7 顺序线性表的表输出

// 输出表

void ListPrint(DynamicSeqList* list) {
	printf("当前线性表中的元素为:");
	for (int i = 0; i < list->length; i++) {	// 遍历线性表的元素
		printf("list[%d]=%d ", i + 1, list->elem[i]);		//输出元素的值
	}
	printf("\n");
}

2.2.8 顺序线性表的表删除

// 删除表

void ListDestroy(DynamicSeqList* list) {	//在顺序表不再需要的时候调用
	free(list->elem);		// 释放存储数据元素的空间
	list->elem = NULL;		// 将指向数组的指针设置为空
	list->length = 0;		// 将线性表长度置为0
	list->listsize = 0;		// 将存储空间大小置为0
}

2.2.9 两个顺序表(递增)的合并 (归并排序)

// 两个顺序表(递增)的合并 (归并排序)

void MergeList(DynamicSeqList* list1, DynamicSeqList* list2, DynamicSeqList* mergeList) {
	mergeList->elem = (int*)malloc(sizeof(int) * (list1->length + list2->length));
	if (!mergeList->elem) {						// 内存分配失败
		printf("内存分配失败!!!\n");
		exit(1);							// 以非正常方式退出(实际应为OVERFLOW)
	}
	mergeList->length = 0;	// 初始化列表长度
	mergeList->listsize = list1->length + list2->length;		//初始化存储空间大小
	
	int i = 0, j = 0, k = 0;
	// 循环遍历list1和list2,将元素按序存放如mergeList中
	while (i < list1->length && j < list2->length) {
		// 比较list1和list2当前位置的元素大小
		if (list1->elem[i] <= list2->elem[j]) {
			// 如果list1当前位置元素小于list2当前位置元素,则将list1当前位置元素放入mergeList
			mergeList->elem[k++] = list1->elem[i++];
		}
		else {
			// 如果list2当前位置元素小于list1当前位置元素,则将list2当前位置元素放入mergeList
			mergeList->elem[k++] = list2->elem[j++];
		}
	}
	// 因为归并循环的过程中 一定会出现一个顺序表已经被排完的情况。
	// 将剩余的元素拷贝到合并列表中
	while (i < list1->length) {
		mergeList->elem[k++] = list1->elem[i++];
	}
	while (j < list2->length) {
		mergeList->elem[k++] = list2->elem[j++];
	}
	mergeList->length = k;		// 更新合并后列表的长度

}

2.2.10 顺序线性表基本操作的实现

int main() {
	DynamicSeqList list1, list2, mergeList;

	// 初始化顺序表
	InitList(&list1);
	InitList(&list2);
	InitList(&mergeList);

	// 向list1中插入一些元素
	ListInsert(&list1, 1, 3);
	ListInsert(&list1, 2, 5);
	ListInsert(&list1, 3, 7);
	ListInsert(&list1, 4, 9);

	// 向list2中插入一些元素
	ListInsert(&list2, 1, 4);
	ListInsert(&list2, 2, 6);
	ListInsert(&list2, 3, 8);
	ListInsert(&list2, 4, 10);

	// 输出list1和list2
	printf("List1: ");
	ListPrint(&list1);
	printf("List2: ");
	ListPrint(&list2);

	// 删除list1,元素
	ListDelete(&list1, 4);
	ListPrint(&list1);
	// 删除list2,元素
	ListDelete(&list2, 4);
	ListPrint(&list2);

	// 查找list1元素
	ListSearch(&list1, 3);
	// 查找list2元素
	ListSearch(&list2, 3);

	// 修改list1元素
	printf("修改后List1: ");
	ListModify(&list1, 3, 9);
	ListPrint(&list1);
	// 修改list2元素
	printf("修改后List2: ");
	ListModify(&list2, 3, 10);
	ListPrint(&list2);

	// 合并表
	ListMerge(&list1, &list2, &mergeList);
	printf("合并后list1表为:");
	ListPrint(&list1);
	printf("合并后list2表为:");
	ListPrint(&list2);
	printf("合并后mergeList表为:");
	ListPrint(&mergeList);

	// 删除表
	ListDestroy(&list1);
	printf("删除后后list1表为:");
	ListPrint(&list1);
	ListDestroy(&list2);
	printf("删除后后list2表为:");
	ListPrint(&list2);
	ListDestroy(&mergeList);
	printf("删除后后mergeList表为:");
	ListPrint(&mergeList);
	return 0;
}

输出结果为:

List1: 当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=7 list[4]=9
List2: 当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=8 list[4]=10
当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=7
当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=8
修改后List1: 当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=9
修改后List2: 当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=10
合并后list1表为:当前线性表中的元素为:list[1]=3 list[2]=5 list[3]=9
合并后list2表为:当前线性表中的元素为:list[1]=4 list[2]=6 list[3]=10
合并后mergeList表为:当前线性表中的元素为:list[1]=3 list[2]=4 list[3]=5 list[4]=6 list[5]=9 list[6]=10
删除后后list1表为:当前线性表中的元素为:
删除后后list2表为:当前线性表中的元素为:
删除后后mergeList表为:当前线性表中的元素为:

  • 51
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值