数据结构与算法笔记 cp2-1:线性表

  • 2019 年 11 月 7 日
  • 筆記

线性表

1.1 定义:

List,由零个或多个数据特性相同的元素构成的有限序列。个数 n 称为线性表的长度,n=0 的时候称为空表。比如说,十二星座就是一个线性表,它的“第一个”和“最后一个”元素都是唯一的,并且中间的元素均只有一个前驱、一个后继。

1.2 抽象数据类型

ADT List{      Data:.......      Operation:          InitList(&L): 初始化,建立一个空的线性表L          ClearList(&L): 清空线性表L          GetElem(L,i,&e): 将L中第i个位置元素值返回给e          LocateElem(L,e): 在L中查找与e相等的元素          ListInsert(&L,i,e): 在L中第i个位置插入新元素e          ListDelete(&L,i,&e): 删除L中第i个位置元素,并用e返回其值          ListLength(L): 返回L长度  } ADT List

线性表的顺序存储结构 / 顺序表

2.1 定义:

指的是用一段地址连续的存储单元依次存储线性表的数据元素。

各个元素的地址之间构成等差数列,因此,知道了某一个元素的地址,就可以随时推导出其它元素的地址,也就是说不管取出还是存入哪一个元素,花费的时间都一样,是一个常数(O(1)),这种特性的存储结构就叫随机存取结构

一维数组可以实现线性表的顺序存储结构,不过要注意数组下标从0开始,而说线性表的时候说的是位序,是从1开始的。

2.2 基本操作

(1) 初始化:

首先定义线性表顺序存储结构:

typedef struct Table{      int * head;//声明了一个名为head的长度不确定的数组,也叫“动态数组”      int length;//记录当前顺序表的长度      int size;//记录顺序表分配的存储容量  }table;

第一个操作是初始化,包括:给head申请足够大小的物理空间;给size和length赋初值:

#define Size 5 //对Size进行宏定义,表示顺序表申请空间的大小  table initTable(){      table t;      t.head=(int*)malloc(Size*sizeof(int));//构造一个空的顺序表,动态申请存储空间      if (!t.head) //如果申请失败,作出提示并直接退出程序      {          printf("初始化失败");          exit(0);      }      t.length=0;//空表的长度初始化为0      t.size=Size;//空表的初始存储空间为Size      return t;  }

(2) 取值: 找某一个位序的元素,只需要判断位序的合法性,之后返回数组[位序-1]即可。

(3) 查找/修改: 遍历数组元素,与目标元素比较,相等则返回。如果要修改,重新赋值即可。

(4) 删除: 找到目标元素,让后续元素整体前移一个位置,之后长度减一即可。

table delTable(table t,int add){      if (add>t.length || add<1) {          printf("被删除元素的位置有误");          exit(0);      }      //删除操作      for (int i=add; i<t.length; i++) {          t.head[i-1]=t.head[i];      }      t.length--;      return t;  }

比如说要删除5个元素中的第三个,那么 add 就是 3,而 t.head[3] 就是第四个元素,从这个元素开始依次向前移动并覆盖前面的元素。

(5) 插入: 找到目标位置,将该位置对应元素以及后续元素整体后移一个位置,之后长度加一即可。

//插入函数,其中,elem为插入的元素,add为插入到顺序表的位置  table addTable(table t,int elem,int add)  {      //判断插入本身是否存在问题      if (add>t.length+1||add<1) {          printf("插入位置有问题");          return t;      }      //线性表长度等于数组长度,没有多余空间容纳新插入的元素,此时需要申请      if (t.length==t.size) {          t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));          if (!t.head) {              printf("存储分配失败");              return t;          }          t.size+=1;      }      for (int i=t.length-1; i>=add-1; i--) {          t.head[i+1]=t.head[i];      }      t.head[add-1]=elem;      t.length++;      return t;  }

  • 首先要注意范围的判断,add 作为目标位置,至少也得等于1,此时是插在最前面;同时,add 可以等于 length+1,此时是插在最后面,但不能比 length+1 更大了,否则会出现空隙。
  • 接着要注意是从后往前遍历的,因为如果是从前往后的话就会发生覆盖。
  • 可以不做“目标位置是否在表尾”的判断,这样会直接绕过for循环,执行赋值。

这里可以发现,线性表的插入和删除元素操作往往需要移动大量的元素。