数据结构1:用数组去表示和实现线性表(C语言实现)

线性表的类型定义

线性表的定义(图示):
在这里插入图片描述
线性结构的特点:

  1. 只有唯一的一个数据元素可以被称为“首元素”
  2. 只有唯一的一个数据元素可以被称为“尾元素”
  3. 除了首元素之外,集合中的每个元素都只有一个直接前驱
  4. 除了尾元素之外,集合中的每个元素都只有一个直接后继

数组的特点:

  1. 地址是连续的
  2. 有一定的顺序
  3. 可以存放相同类型的数据

因此可以用数组来实现线性表,线性表中把这种用内部已经一定顺序的结构称作线性表的顺序存储,通称“顺序表”

在这里插入图片描述

//引入相应的库
#include <stdio.h>
#include <stdio.h>

//定义两个常量
#define LIST_INIT_SIZE 10 /* 线性表存储空间的初始分配量 */
#define LISTINCREMENT 2 /* 线性表存储空间的分配增量 */

#define TRUE 1   //代表真
#define FALSE 0  //代表假

//这场定义的是用于表示状态的类型
typedef int Status; 

//这里将int型的数据作为线性的数据元素,可以根据需要自行更换数据类型
typedef int ElemType;

typedef struct {
    
    
	ElemType *elem; //数据元素
	int length; //当前线性表的长度
	int size; //当前线性表的最大容量
}SqList;

基本操作:初始化一个线性表

  • 操作结果:构造一个空的线性表L
  • 这里使用的C语言的mallo方法去动态构建初始容量为10(LIST_INIT_SIZE )的数组。
Status InitSqList(SqList *L){
    
    
	//这里用的是动态创建数组的方式
	L->elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
	if(!L->elem){
    
    
		 exit(OVERFLOW); //存储分配失败
	}
	L->length = 0 ;//初始化的时候将线性表的长度设为 0 
	L->size =10 ; //初始化的时候将线性表的容量设为 10 
	return TRUE;
}

基本操作:销毁一个线性表

  • 初始条件:顺序线性表L已存在
  • 操作结果:销毁顺序线性表L
Status DestroyList(SqList *L){
    
    
	free(L->elem);
	L->elem = NULL;
	L->length =0 ;
	L->size = 0 ;
	return TRUE;
}

基本操作:重置一个

  • 初始条件:顺序线性表L已存在。
  • 操作结果:将L重置为空表
Status ClearList(SqList *L){
    
    
	L->length = 0;
	return TRUE ;
}

基本操作:是否为空

  • 初始条件:顺序线性表L已存在。
  • 操作结果:若L为空表,则返回TRUE,否则返回FALSE
 Status ListEmpty(SqList L){
    
    
	if(L->length == 0){
    
    
		return TRUE;
	}else{
    
    
		return FALSE;
	}
}

基本操作:获取指定位置的元素

  • 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
  • 操作结果:用e返回L中第i个数据元素的值
  • 优点:使用数组来实现线性表的优点就是可以随机读取数据,即可以根据索引直接读取数组中指定位置的数据。
Status GetElement(SqList L ,int index, Element *e){
    
    
	//取到索引,先判断索引的位置是否合理
	if(index<1 || index >L.length){
    
    
		printf("输入的索引错误\n") ;
		return FALSE;
	}
	*e = L.elem[index];
	return TRUE;
}

基本操作:定位元素

  • 初始条件:顺序线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
  • 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序,若这样的数据元素不存在,则返回值为0;
int LocateElem(SqList L,ElemType e,Status(*compare)(ElemType,ElemType))
 {
    
     
   ElemType *p;
   int i=1; /* i的初值为第1个元素的位序 */
   p=L.elem; /* p的初值为第1个元素的存储位置 */
   while(i<=L.length&&!compare(*p++,e))
     ++i;
   if(i<=L.length)
     return i;
   else
     return 0;
 }

基本操作:匹配指定元素的前驱

  • 初始条件:顺序线性表L已存在
  • 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义
Status PriorElem(SqList L,ElemType cur_e,ElemType *pre_e){
    
    
	int i=2;
   ElemType *p=L.elem+1;
   while(i<=L.length&&*p!=cur_e)
   {
    
    
     p++;
     i++;
   }
   if(i>L.length)
     return INFEASIBLE;
   else
   {
    
    
     *pre_e=*--p;
     return OK;
   }
}

基本操作:匹配下一个元素

  • 初始条件:顺序线性表L已存在
  • 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义。
Status NextElem(SqList L,ElemType cur_e,ElemType *next_e){
    
    
	int i = 1 ;
	ElemType *p = L.elem;
	while(i<L.length -1 && *p != cur_e){
    
    
		p++;
		i++;
	}
	if(i>=length){
    
    
		return INFEASIBLE;
	}
	*next_e = *++p;
	return OK;
}

基本操作:在指定位置插入数据

  • 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)+1
  • 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1

在这里插入图片描述
从上图中,我们明白,用数组实现线性表的的一个缺点,就是在插入数据的时候,会频繁的移动数据元素的位置。

Status InsertList(SqList *L, int index, ElemType e){
    
    
	EelemType *newElem, *p, *q;
	//第一步:先判断插入的位置是否合理
	if(index < 1 && index <=L->length+1){
    
    
		printf("插入的位置索引错误!\n") ;
		return NO;
	}
	//第二步:判断插入是否超过顺序表的最大容量
	if(L->length >= L->size){
    
    
		//这里可以进行扩大容量,也可以选择直接报线性表已经满,不允许插入。
		//进行扩容
		newElem=(ElemType *realloc((L->size+LISTINCREMENT )*sizeof(ElemType));
		if(!newElem){
    
    
			exit(OVERFLOW);/* 存储分配失败 */
		}
		L->elem = newElem ;
		L->size = L->size + LISTINCREMENT ;
	}
	//在数组中每一次插入都需要将插入位置的后面的元素后移
	q = L->elem+(index-1);
	p = L->elem+(L->length-1);
	while (p >= q){
    
    
		*(p+1) = *p ;//插入位置之后的所有元素往后移动一位
		p--;
	}
	*q = e ;  /* 插入新e */
	L->length ++;
	return 0; /* 表长增1 */
}

基本操作:删除指定位置的元素

  • 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
  • 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
    在这里插入图片描述
    在这里也可以看出,在删除一个元素的时候,也会频繁地移动数据元素的位置。
Status ListDelete(SqList *L,int index,ElemType *e) {
    
    
	ElemType *q , *p ;
	//判断插入的位置是否合理
	if(index <1 || index > L->lenght -1){
    
    
		printf("输入的位置不合理\n";
		return FALSE;
	}
	p = L->elem +index -1 ;
	*e = *p ; //用传入的e来返回删除的位置数据
	q = L->elem +L->length -1 ;
	for(p ; p < q ; p++){
    
    
		*p = *(p+1);
	}
	L.length--;
	return TRUE;
}

基本操作:查看线性表中所有的元素

  • 初始条件:顺序线性表L已存在
  • 操作结果:依次对L的每个数据元素调用函数vi()
 Status ListTraverse(SqList L,void(*vi)(ElemType*))
 {
    
     /*  */
   /*  */
   ElemType *p;
   int i;
   p=L.elem;
   for(i=1;i<=L.length;i++)
     vi(p++);
   printf("\n");
   return TRUE;
 }

从图中和代码的实现中,发现在顺序存储结构的线性表中的某个位置插入或删除一个元素的时候,计算机会花费大量的时间在移动元素上面。

  • 最好的情况:是插入的位置刚好是在最后面的位置,这样不用去移动其它元素。
  • 最坏的情况:是插入的位置偏偏是第一个位置,这样就需要将所有元素后移。
  • 时间复杂度:O(n)。

这里引用一下书籍 《数据结构(C语言版)》作者: 严蔚敏 分析时间复杂度的过程:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Hicodden/article/details/103467387
今日推荐