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

线性表是什么

线性表是包含若干数据元素的一个线性序列,记为: L = (a0,…ai-1,ai,ai+1,…an-1)

  • L为表名,ai(0≤ i ≤n-1)为数据元素;
  • n为表长,n>0时,线性表 L 为非空表,否则为空表。

线性表L可用二元组形式描述(程序员间的表述):
L = (D,R)
即线性表 L 包含数据元素集合 D 和关系集合 R
D = {ai | ai ∈ datatype ,i=0,1,2, …n-1 ,n≥0}
R = {i, ai+1> | ai,ai+1∈D,0 ≤ i ≤ n-2}

  • 关系符i,ai+1>在这里称为有序对
  • 表示任意相邻的两个元素之间的一种先后关系
  • ai 是 ai+1直接前驱,ai+1 是 ai直接后继

数据结构 - 线性表(顺序表)_第1张图片

线性表的特征

  • 对非空表,a0是表头,无前驱;
  • an-1是表尾,无后继;
  • 其它的每个元素 ai 有且仅有一个直接前驱 ai-1 和 一个直接后继 ai+1

线性表 - 顺序表

顺序存储结构的表示1

若将线性表 L = (a0,a1,…,an-1) 中的各元素依次存储于计算机一片连续的存储空间。

设 Loc(ai) 为 ai 的地址,Loc(a0) = b,每个元素占 d 个单元, 则:Loc(ai) = b + i*d

顺序存储结构的特点

  • 逻辑上相邻的元素 ai,ai+1,其存储位置也是相邻的
  • 对数据元素 ai 的存取为随机存取按地址存取
  • 存储密度高
    • 存储密度 D = (数据结构中元素所占存储空间)/ (整个数据结构所占空间)
  • 不足:对表的插入和删除等运算的时间复杂度较差

顺序存储结构的表示2

在 C 语言中,可借助于一维数组类型来描述线性表的顺序存储结构:

#define N 100
typedef int data_t;
typedef struct
{
	data_t data[N]; //表的存储空间;
	int last;
} sqlist, *sqlink;
	

数据结构 - 线性表(顺序表)_第2张图片
上面的代码可以这么理解:

  • typedef struct sqlist 可以看作是整个图书馆,而这里的 data[N] 是图书馆里的书,结构体里的 last 就可以看作是一共有多少本书,虽然叫作 last 是因为它是顺序表里最后一个元素的下标,但是这个下标也可以标识一共有多少本书,只要可以代表总数,也可以换成别的。
  • 那为什么要用 typedef 定义变量和结构体?因为这样可以复用代码,比如这里的 data_t 可以换成 char ,也可以换成别的类型,它可以是书,也可以是餐具等等,结构体 typedef 同理

线性表的基本运算

设线性表 L = (a0,a1,…,an-1) , 对 L 的基本运算有:
1)建立一个空表:list_create(L)
2)置空表:list_clear(L)。若成功返回1,失败返回0。
3)判断表是否为空:list_empty(L)。若表为空,返回值为 0,否则返回 -1
4)求表长:length(L)
5)取表中某个元素:GetList(L,i),即ai。要求0≤ i ≤ length(L) - 1
6)定位运算:Locate(L,x)。确定元素 x 在表 L 中的位置(或序号)
L o c a t e ( L , x ) = { i 当元素 x = a i ∈ L ,且 a i 是第一个与 x 相等时 − 1 当 x 不属于 L 时 Locate(L,x)=\left\{ \begin{array}{rcl} i & & {当元素x=a_i∈L,且a_i是第一个与x相等时}\\ -1 & & {当x不属于L时}\\ \end{array} \right. Locate(L,x)={i1当元素x=aiL,且ai是第一个与x相等时x不属于L
7)插入:Insert(L,x,i)。将元素 x 插入到表 L 中第 i 个元素 ai 之前,且表长 +1。

线性表运算的实现

我们在实现时一般会写三个文件(顺序表):
1、sqlist.h 定义数据结构,定义线性表运算
2、sqlist.c 实现线性表运算
3、 test.c 实现实际功能,调 sqlist.h 接口
这样写的好处是:①结构清晰; ②代码复用性高:自己可以反复用,不管是现在的项目,还是以后的项目都可以用;同事也可以用。③给外包公司接口,实现部分给他们二进制 .so 文件,这样以后业务升级还找你

分文件编写以后怎么编译运行呢?

  • 可以一条一条的把 .c 汇编为 .o ,最后链接 .o 和 库文件 运行:
    例如:

    gcc -c sqlist.c -o sqlist.o
    
    gcc -c test.c -o test.o
    
    gcc test.o sqlist.o -o test
    
  • 利用简洁的命令进行编译执行:

    gcc *.c -o test
    
  • 利用 Makefile 进行编译

    SRC=sqlist.c
    SRC+=test.c
    
    test:$(SRC)
      gcc $^ -o $@
    
    .PHONY:clean
    
    clean:
      rm *.o
    

sqlist.c 中线性表运算具体实现

  • 创建顺序表 list_create()

    • 思路:
      1. 分配内存空间(一定要注意检查分配成功没有)
      2. 初始化
      3. 返回顺序表指针
    • 代码:
    sqlink list_create()
    {
    	//malloc
    	sqlink L;
    	L = (sqlink)malloc(sizeof(sqlist));
    	if(L == NULL)
    	{
    		printf("list malloc failed!\n");
    		return L;
    	}
    
        //init
        memset(L, 0, sizeof(sqlist));
        L->last = -1;
    
        //return
        return L;
    }
    
  • 清空顺序表 list_clear(sqlink L)

    • 思路:
      1. 先检查表原来是否是空表(如果是空表的话清空也没啥意义)(有疑问??)
      2. 然后给表空间置0,给表尾元素挪到初始位置(-1)
    • 代码:
     int list_clear(sqlink L)
     {
         if(L == NULL)
         {
              printf("the list is null!\n");
              return -1;
         }
    
     	memset(L, 0, sizeof(sqlist));
     	L->last = -1;
    
     	return 0;
     }
    
  • 判断顺序表是否为空表 list_empty(sqlink L)

    • 思路:
      1. 判断指针 L 是否为空,避免非法操作
      2. 判断表尾元素是否还在初始位置(-1),如果在初始位置证明是空表,返回 0,如果不是的话就返回 1
    • 代码:
    int list_empty(sqlink L)
    {
    	if (L == NULL)
    	{
    		printf("illegal operation!\n");
    		return -1;
    	}
    	
    	if (L->last == -1)
    	{
    		return 0;
    	}
    	else
    	{
    		return 1;
    	}
    }
    
  • 计算顺序表长度 list_length(sqlink L)

    • 思路:
      1. 判断指针 L 是否为空,避免非法操作
      2. 顺序表长度可以看作是表尾元素下标 + 1
    • 代码:
    int list_length(sqlink L)
    {
      if (L == NULL)
      {
      	return -1;
      }
      
      return (L->last + 1);
    }
    
  • 向顺序表中插入元素

    • 思路:
      1. 判断顺序表满不满,如果已经满了就不能再插入元素了
      2. 判断元素插入位置是否合法(插入位置不能小于下标 0 ,也不能大于顺序表末尾元素下标 L->last 的后一位)
      3. 如果以上两条都通过,那么开始新增元素,新增元素按照下图的箭头所示,依次从后往前的每个元素都向后移动一位给新增元素腾地方。比如插入元素位置在下标为 1 的地方,那么就先移动下标 4 的元素 9 到下标 5 ,然后移动下标 3 的元素 4 到 下标 4,······以此类推,把下标 1 的元素移动到下标 2 后,就可以进行下一步了
      4. 把要新增的值赋给数组的新增元素
      5. 末尾元素下标加一
        数据结构 - 线性表(顺序表)_第3张图片
    • 代码:
    int list_insert(sqlink L, data_t value, int pos)
    {
      int i;
      if (L->last == N - 1)
      {
      	printf("the list is full!\n");
      	return -1;
      }
      
      if (pos < 0 || pos > L->last + 1)
      {
      	printf("the insertion position is invalid\n");
      	return -1;
      }
    
      for (i = L->last; i <= pos ; i--)
      {
      	L->data[i+1] = L->data[i];
      }
      
      L->data[pos] = value;
      
      L->last++;
    
      return 0;
    }
    
  • 打印顺序表 list_show(sqlink L)

    • 思路:
      1. 判断表指针 L 是否为空,避免非法操作
      2. 判断表尾元素是否在初始位置,给予空表提示
      3. 打印顺序表
    • 代码:
    int list_show(sqlink L)
    {
      int i;
       
      if (L == NULL)
      {
      	printf("invalid!\n");
      	return -1;
      }
      
      if (L->last == -1)
      {
      	printf("the list is null!\n");
      	return 0;
      }
    
      for (i = 0; i <= L->last; i++)
      {
      	printf("%d ", L->data[i]);
      }
      
      printf("\n");
    
  • 销毁顺序表 list_free(sqlink L) ,因为有时候程序并不能主动释放顺序表内存空间,一直占用内存会造成浪费

    • 思路:
      1. 判断表指针 L 是否为空,如果为空那么这个表就没有建立起来,也没必要销毁
      2. 释放 L 内存空间
      3. 把表指针 L 置空,不再让其指向这段已经被释放的内存空间
    • 代码:
    int list_delete(sqlink L)
    {
    	if (L == NULL)
    	{
    		printf("invalid!\n");
    		return -1;
    	}
    
        free(L);
        L = NULL;
        
        return 0;
    }    
    

你可能感兴趣的:(数据结构,c语言)