数据结构之线性表

1 线性表的定义和基本操作

1.1 定义

线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。

线性表的特点:

  • 表中元素个数有限
  • 表中元素具有逻辑上的顺序性,有先后次序
  • 表中元素的数据类型都相同,具有抽象性

2 线性表的顺序表示

2.1 顺序表的定义

顺序表的特点是表中元素的逻辑顺序与其存储的物理顺序相同。
注意 :线性表中元素的位序是从1开始的,而数组中的元素的下标是从0开始的。

静态顺序表

c 复制代码
#define MaxSize 50 //定义线性表的最大长度
typedef struct{
  ElemType data[MaxSize];  //顺序表的元素
  int length;       //顺序表的当前长度
}SqList;    //顺序表的类型定义

顺序表的基本操作:增,删,改,插。

顺序表的优点:①可随机访问;②存储密度高。缺点:①元素的插入和删除需要移动大量元素;②顺序存储分配需要一段连续的存储空间,不够灵活。

2.2顺序表的插入

在顺序表插入元素,需要做到如下两步工作:

  1. 将要插入的位置元素及其后续元素整体向后移动一位
  2. 将元素放到腾出来的位置上

代码:

c 复制代码
bool ListInsert( SqList &L,int i,ElemType e)
{
  if(i<1||i>L.length+1)
    return false; 
  if(L.length>=MaxSize)
    return false;
  for(int j=L.length;j>=i;j++)
  {
    L.data[j]=L.data[j-1];
  }
  L.data[i-1]=e;
  L.length++;
  return true;
}

插入算法的平均时间复杂度为O(n)。

2.3 顺序表的删除

在顺序表中删除元素,就是找到改元素的位置,将其后续元素整体向前移动一个位置。

代码:

c 复制代码
bool ListDelete( SqList &L,int i,ElemType e)
{
  if(i<1||i>L.length+1)
    return false; 
  e=L.data(i-1);
  for(int j=i;j<L.length;j++)
  {
    L.data[j-1]=L.data[j];
  }
  L.length--;
  return true;
}

删除算法的时间复杂度为O(n)。

2.4 顺序表按值查找

在顺序表中查找第一个元素值为e的元素,并返回位序:

c 复制代码
int Locate(SqList L,ElemType e)
{
  int i;
  for(i=0;i<L.length;i++)
  {
    if(L.data[i]==e)
    {
     return i+1;
    }
  }
  return 0;
}

按值查找的平均时间复杂度为O(n)。

3 线性表的链式表达

链式存储就是用指针将相互的结点连接起来,链式存储根据链表的构造不同,可分成:单向链表,单向循环链表,双向链表,双向循环链表。

3.1 单链表的定义

线性表的链式存储也称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。

数据域 指针域
data next

数据域:存放数据元素的区域

指针域:存储直接后继位置的区域

单链表:

c 复制代码
typedef struct LNode{
  ElemType data;
  struct LNode *next;
}LNode,*LinkList;

3.1.1 初始化

带头结点的初始化:

c 复制代码
bool InitList(LinkList &L)
{
  L=(LNode*)malloc(sizeof(LNode));
  L->next=NULL;
  return true;
}

3.1.2 求表长

c 复制代码
int Length(LinkList L)
{
  int len=0;
  LNode *p=L;
  while(p->next!=NULL)
  {
   p=p->next;
   len++;
  }
  return len;
}

3.1.3 插入结点操作

插入结点操作将值为x的新结点插入到单链表的第i个位置。

首先查找第i-1个结点,假设第i-1个结点为p,然后令新节点 s 的指针域指向p的后继,再令结点p的指针域指向新插入的结点*s。

c 复制代码
bool ListInsert(LinkList &L,int i,ElemType e)
{
  LNode *p=L;
  int j=0;
  while(p!=NULL&&j<i-1)
  {
   p=p->next;
   j++;
  }
  if(p==NULL)
  {
   return false;
  }
  LNode *s=(LNode*)malloc(sizeof(LNode));
  s->data=e;
  s->next=p->next
  p->next=s;
  return true;
}

必须先执行 s->next=p->next,后执行 p->next=s,否则先执行 p->next=s后,指向原后继的指针就不存在了。

时间复杂度是O(n)

3.1.4 删除结点操作

删除结点操作是将单链表的第i个结点删除。

假设结点p为被删除结点的前驱,只需修改 p的指针域,将p的指针域指向q的下一个结点,然后释放*q的存储空间。

c 复制代码
bool ListDelete(LinkList &L,int i,ElemType &e)
{
	 LNode *p=L;
	 int j=0;
	 while(p->next==NULL&&j<i-1)
	 (
	  p=p->next;
	  j++;
	 )
	 if(p->next==NULL||j>i-1)
	 { 
	   return false;
	 }
	 LNode *q=p->next;
	 e=q->data;
	 p->next=q->next;
	 free(q);
	 return true;;
  
}

时间复杂度为O(n)

3.1.5 采用头插法建立单链表

该方法从一个空表开始,生成新的结点,并将读取到的数据放到新结点的数据中,然后将新结点插入到当前链表的表头,即头结点之后。

如图所示:

c 复制代码
Linklist List_HeadInsert(LinkList &L)
{
  LNode *s;int x;
  L=(LNode*)malloc(sizeof(LNode));
  L->next=NULL;
  scanf("%d",&x);
  while(x!=9999)
  {
   s=(LNode*)malloc(sizeof(LNode));
   s->data=x;
   s->next=L->next;
   L->next=s;
   scanf("%d",&x);
  }
  return L;
}

3.1.6 采用尾插法建立单链表

该方法将新结点插入到当前链表的表尾,为必须增加一个尾指针 r,使其始终指向当前链表的尾结点。

如图所示:

c 复制代码
Linklist List_TailInsert(LinkList &L)
{
  int x;
  L=(LNode*)malloc(sizeof(LNode));
  LNode *s,*r=L;
  scanf("%d",&x);
  while(x!=9999)
  {
   s=(LNode*)malloc(sizeof(LNode));
   s->data=x;
   r->next=s;
   r=s;
   scanf("%d",&x);
  }
  r->next=NULL;
  return L;
}

3.2 头指针和头结点

链表中,第一个结点存储的位置叫头指针 ,如果链表由头结点,那么头指针就是指向头结点的指针。

头指针所指的不存在数据元素的第一个结点就叫做头结点 (而头结点又指向首元结点),头结点一般不存放数据,存放第一个数据元素的结点叫做第一个数据元素节点,也叫做首元结点。

相关推荐
m0_672703314 小时前
上机练习第49天
数据结构·算法
样例过了就是过了4 小时前
LeetCode热题100 N 皇后
数据结构·c++·算法·leetcode·dfs·深度优先遍历
霖大侠4 小时前
CPAL: Cross-Prompting Adapter With LoRAs forRGB+X Semantic Segmentation
人工智能·深度学习·算法·机器学习·transformer
近津薪荼4 小时前
优选算法——分治(2):快速选择
算法
Z...........4 小时前
(优选算法)斐波那契数列模型
数据结构·算法
zyjyyds1134 小时前
和为0的四元组-双指针法(C语言实现)
c语言·数据结构·算法
炽烈小老头5 小时前
【每天学习一点算法 2026/03/16】电话号码的字母组合
学习·算法
Lee川5 小时前
时空迷宫探险记:从O(1)到O(2^n)的算法进化论
算法·面试
KangJX5 小时前
Matrix获取卡顿堆栈 (Point Stack)
算法·客户端