数据结构之——线性表

1.线性表定义


线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n为0时,该线性表是一个空表。除第一个元素外,每个元素有且仅有一个直接前驱,除最后一个元素外,每个元素有且仅有一个直接后继。

线性表的特点:
1.表中元素的个数有限
2.表中元素具有逻辑上的顺序性,在序列中各元素排序有其先后次序
3.表中元素都是数据元素,每一个元素都是单个元素
4.表中元素的数据类型都相同,这意味着每一个元素都占有相同大小的存储空间
5.表中元素具有抽象性,即仅讨论元素间的逻辑关系,不考虑元素究竟表示什么内容

知识框架:
在这里插入图片描述

2.顺序表


线性表的顺序存储成为顺序表,它是用一组地址连续的存储单元,依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。

顺序表的特点:
1.可以随机访问,即通过首地址和元素符号可以在O(1)的时间内找到指定的元素。
2.表中元素的逻辑顺序与其物理顺序相同。
3.存储密度高,每个结点只存储数据元素。

注意:线性表中元素的位序是从1开始,而数组中元素的下标 是从0开始。
线性表的顺序存储类型描述为:

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

顺序表的基本操作如下:
(1)插入操作
在顺序表L的第i(1<=i<=L.length+1)个位置插入新元素e。如果i的输入不合法,则返回false,表示插入失败;否则将顺序表的第i个元素及其后的所有元素右移一个位置,腾出一个空位置插入新元素e,顺序表长度加1,插入成功,返回true。

bool ListInsert(SqList &L, int i, int e ){
 if(i<1 || i>L.length +1)    return false;  //判断i的范围是否有效
 if(L.length >= MaxSize)  return false;  //当前存储空间已满,不能插入
 
 for(int j = L.length ; j>=i; j--){
  L.data[j] = L.data[j-1];  //将第i个元素及之后的元素后移
 }
 L.data[i-1] = e;  //在位置i处加入e
 L.length ++;  //线性表长度加1
 retur true;
}

(2)删除操作
删除顺序表L中第i(1<=i<=L.length)个位置的元素,成功则返回true,并将被删除的元素用引用变量e返回,否则返回false。


bool ListDelete(SqList &L, int i, int &e){
  if(i<1 || i>L.length +1)    return false;
  e = L.data[i-1];    // 将被删除的值赋值给e
   
  for(int j=i; j<L.length ;j++){
   L.data[j-1] = L.data[j];   //将第i个位置之后的元素前移 
  }
  L.length --;
  return false;
}

(3)按值查找(顺序查找)
在顺序表L中查找第一个元素值等于e的元素,并返回其位序。

int Locate(SqList L, int e){
 int i;
 for(i 0; i<L.length ;i++){
  if(L.data[i] ==e){
   return i+1;  //下标为i的元素值等于e,返回其位序i+1 
  }
 }
 return 0;  //退出循环,说明查询失败 
}

3.单链表


线性表的链式存储又称为单链表,它不要求逻辑上相邻的两个元素在物理位置上也相邻,为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后继的指针,因此,对线性表的插入、删除不需要移动元素,只需要修改指针。

为了操作方便,在单链表第一个结点之前附加一个结点,称为头结点,头结点的数据域可以不设任何信息,也可以记录表长等相关信息,头结点的指针域指向线性表的第一个元素结点。

头结点和头指针的区分:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点链表中的第一个结点,结点内通常不存储信息。
引入头结点,可以带来两个优点:
(1)由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。
(2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一。

单链表中结点类型描述如下:

typedef struct LNode{   //定义单链表结点类型 
 int data;     //数据域 
 struct LNode *next;  //指针域 
}LNode, *LinkList;

单链表的基本操作如下:
(1)尾插法建立单链表
将新结点插入到当前链表的表尾中,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点,代码如下::

LinkList CreatList(LinkList &L){
 int x;    //设置元素类型为整型
 L =(LinkList)malloc(sizeof(LNode));
 LNode *s, *r = L;    //r为表尾指针
 scanf("%d", &x);
 
 while(x != 9999){
        s = (LinkList)malloc(sizeof(LNode));
       s->data = x;
     r->next =s;
     r = s;  //r指向新的表尾结点
     scanf("%d", &x); 
 }
 r->next = NULL;  //尾结点指针空置 
 return L;
}

(2)按序号查找结点值
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。

LNode *Get(LinkList L, int i){
 int j =1;  //计数,初始为1
    LNode *p = L->next ;  //头结点指针赋给p
 if(i == 0)   return L;  //若i等于0,则返回头结点
 if(i < 1)    return NULL;  //若i无效,返回NULL
 
 while(p&&j <i){
  p = p->next ;  //从第1个结点开始找,查找第i个结点
  j++; 
 } 
 
 return p;  //返回第i个结点的指针 
}

(3)按值查找表结点
从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。代码如下:

LNode *Locate(LinkList L, int x){
 LNode *p = L->next ;
 wile(p != NULL && p->data !=e ){  
  p = p->next ;
 }
 return p;  //找到后返回该结点指针p,否则返回NULL 
}

(4)插入结点操作
插入操作是将值为x的新结点插入到单链表的第i个位置。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新结点。代码如下:

void insert(LinkList &L, int x){
 LNode *s,*p;
 s = new LNode;   //新建结点 
    s->next = NULL;
    
    p = Get(L,i-1);   //查找插入位置的前驱结点 
    s->data = x;  //插入结点的数据域为x; 
    
    s->next = p->next ;
 p->next = s;  //前一个位置的结点指针指向新节点 
   
} 

(5)删除结点操作
删除操作是将单链表的第i个结点删除,先检查删除位置的合法性,然后查找表中第i-1个结点,即被删除结点的前驱结点,再将其删除。假设结点p为找到的删除结点的前驱结点,为了实现这一操作后的逻辑关系的变化,仅需修改p的指针域,即将p的指针域next指向q的下一个结点。代码如下:

void Delet(LinkList &L, int i){
 LNode *s, *p;
 
    p = Get(L,i-1);   //查找插入位置的前驱结点
 s = p->next ;  //令s指向被删除结点
 p->next = s->next ; //将*s结点从链中断开
 free(s);  //释放结点的存储空间 
    
} 

参考文章

猜你喜欢

转载自blog.csdn.net/xiaoyong5854/article/details/106362450