[数据结构] 一、线性表

目录

一、线性表的思维导图

二、线性表的基本概念

三、顺序表的结构体定义和基本操作

四、单链表的结构体定义和基本操作

三、双链表的结构体定义和基本操作

四、循环链表的操作

五、静态数组简介


一、线性表的思维导图

注意点:(1)线性表中的存储的数据元素必须具有相同数据类型。(2)线性表是一个有限序列


二、线性表的基本概念

(1)线性表的定义:线性表是具有相同特性数据元素的一个有限序列。

(2)线性表的逻辑特性:只有一个表头元素,只有一个表尾元素,表头没有前驱,表尾没有后继,除表头和表尾元素之外,其他元素只有一个直接前驱,也只有一个直接后继。

(3)线性表的存储结构:线性表的存储结构有顺序存储和链式存储,前者成为顺序表,后者成为链表。

(a)顺序表:表中的元素按照其逻辑顺序,存储到一块连续的内存地址空间中。

(b)链表:每个节点不仅包含数据域,还包含指针域。如单链表中前驱节点的指针域存放后继节点的地址信息。

(4)链表的5种形式:

(a)单链表:每个节点由数据域和指针域组成,指针域用来指向其后继节点。

带头节点的单链表:头指针head指向头节点,头节点一般不含任何信息,从头节点的后继节点开始存储信息。头指针始终不等于NULL,head→next等于NULL的时候,链表为空。

不带头节点的单链表:头指针head直接指向开始节点。head等于NULL时,链表为空。

(b)双链表:每个节点由数据域和两个指针域组成,一个指向前驱节点,一个指向后继节点。

带头节点的双链表:头指针始终不等于NULL,head→next等于NULL的时候,链表为空。

不带头节点的单链表:头指针head直接指向开始节点。head等于NULL时,链表为空。

(c)循环单链表:在单链表的基础上把终端节点的指针域(空指针)指向链表中的第一个节点(可能是头节点)

带头节点的循环单链表:当head等于head→next时,链表为空。

不带头节点的循环单链表:头指针head直接指向开始节点。head等于NULL时,链表为空。

(d)循环双链表:在双链表的基础上把终端节点的next指针域(空指针)指向链表中的第一个节点(可能是头节点),将链表中第一个节点的prior指针指向终端节点。

带头节点的循环双链表:head→next 等于 head→prior 等于 head 的时候链表为空

不带头节点的循环双链表:头指针head直接指向开始节点。head等于NULL时,链表为空。

(e)静态链表:静态链表借助一维数组来实现,每一个数组元素中分别存储:序号、data域、"指针"域。

(5)顺序表和链表的比较:

(a)顺序表:具有随机访问特性、要求占用连续的内存地址空间、插入操作(不在终端节点)需要移动多个数据、插入和删除的平均时间复杂度为:O(n)。

(b)链表:不支持随机访问,只能顺序存取、节点的存储空间利用率较顺序表低一些(因为要有指针域)、插入操作无需移动元素,只需要修改指针、插入和删除的时间复杂度为:O(1)。


三、顺序表的结构体定义和基本操作

(1)顺序表的结构体定义:

#define  maxSize  100    //这里宏定义顺序表的最大长度
typedef  struct{
    int  data[maxSize]; //存放顺序表元素的数组
    int  length;        //存放顺序表的长度
}Sqlist;                

(2)初始化顺序表操作:只需要将length设置为 0。

void  initList(Sqlist  &L){//L本身要改变,所以用引用型
    L.length = 0;
}

(3)顺序表的按值查找操作:在顺序表查找第一个等于e的元素,若找到则返回其在线性表的位置,若没找到则返回 0。

int  findElem(Sqlist  L, int  e){
    int  i;
    for(i=0;i<L.length;i++){
        if(L.data[i] == e){
            return  ++i;    //若找到,则返回其在线性表的位置
        }
        return  0;       //没找到,返回0,作为查找失败的标记
    }
}

(4)顺序表的按位置查找操作:在顺序表查找第p(0<p<L.length+1)个位置的元素,并将其返回给 e。

int  findElem(Sqlist  L, int  p, int  &e){
    if(p<1||p>L.length){
        return  0;        //p越界,返回0
    }
    e = L.data[p+1];      //将第p个位置的元素返回给e
    return  1;            //查找成功返回 1
}

(5)顺序表的修改操作:查找顺序表中第一个等于e的元素,并对其值修改为x,修改成功返回 1,失败则返回 0。

int  findAndUpdate(Sqlist  &L, int  e, int  x){//L本身要改变,所以用引用型
    int  i;
    for(i=0;i<L.length;i++){
        if(L.data[i] == e){
            L.data[i] = x;  
            return  1;    //修改成功,则返回 1
        }
    }
    return  0;       //没找到,返回 0,作为查找失败的标记
}

(6)顺序表的插入操作:在顺序表的第p(0<p<L.length+1)个位置插入新元素e,如果p输入不正确则返回0,代表插入失败;如果p输入的正确,则将顺序表第p个元素以及以后的元素向后移一位,腾出一个空位置,插入新元素,顺序表长度增加1,插入成功,返回1。

int  insertElem(&Sqlist  &L, int  p, int  e){//L本身要改变,所以用引用型
    int  i;
    if(p<0||p>L.length||L.length==maxSize){//位置错误或者表长达到最大值,插入不成功
          return  0;
    }
    for(i=L.length-1;i>=p;i--){
        L.data[i+1] = L.data[i];    //从后往前,逐个将元素向后移一位
    }
    L.data[p] = e;        //将e放在插入位置p上
    L.length ++;          //表长加 1
    return  1;            //插入成功放回 1
}

(7)顺序表的删除操作:删除顺序表L的第p(0<p<L.length)个位置的元素,如果输入的p位置不正确则返回0,代表删除失败;如果p的位置输入正确,则将顺序表的第p个位置的元素赋值给e,然后将其后的元素依次向前移动一个位置,顺序表长度减少1,删除成功,返回1。

int  deleteElem(Sqlist  &L, int  p, int  &e){
    int  i;
    if(p<1||p>L.Length){    //删除的位置不对,返回0
        return  0;    
    }
    e = L.data[p];
    for(i=p;i<L.length-1;i++){    //从p位置开始,后边的元素逐个向前移动一个位置
        L.data[i] = L.data[i+1];
    }
    L.length --;        //删除成功表长 -1
    return  1;          //删除成功返回 1
}

四、单链表的结构体定义和基本操作

(1)单链表的节点结构体定义:

typedef  struct  LNode{
    int  data;                //data存放数据域
    struct  LNode  *next;     //指向后继节点的指针
}LNode;

(2)头插法建立单链表:

void  createListF(LNode  *&C, int  a[], int  n){
    LNode  *s;
    int  i;
    C = (LNode*)malloc(sizeof(LNode));    //申请C的头节点空间
    C -> next = NULL;                     //将链表初始化为空
    for(i=0;i<n;i++){
        s = (LNode*)malloc(sizeof(LNode));    //s指向新申请的节点
        s -> data = a[i];                     //用s节点来接收a[]中的元素
        s -> next = C -> next;        //s所指的新节点的指针域的next指向C中的开始节点
        C -> next = s;                //头节点的指针域指向s节点,是s成为新的开始节点
    }
}

(3)尾插法建立单链表:

void  createListR(LNode  *&C, int  a[], int  n){
    LNode  *s  *r;        //s指向新申请的节点,r指向C的终端节点
    int  i;
    C = (LNode*)malloc(sizeof(LNode));    //申请C的头节点空间
    C -> next = NULL;                     //将链表初始化为空
    r = C;        //r指向头节点,因为此时头节点就是终端节点
    for(i=0;i<n;i++){
        s = (LNode*)malloc(sizeof(LNode));    //s指向新申请的节点
        s -> data = a[i];                     //用s节点来接收a[]中的元素
        r -> next = s;            //用r来接纳新节点
        r = r -> next;            //将r指向终端节点,便于接纳下一个到来的节点
    }
    r -> next = NULL;    //数组中的元素都插入到了链表中,此时将终端节点的指针域设置为NULL
}

(4)单链表的按值查找操作:查找单链表L中的第一个节点数据为e的元素,若找到则返回1,若没有则返回0;

int  findList(LNode *C, int e){
    LNode  *p;
    p = C;    //将p指针指向单链表L的开始节点
    while(p->next!=NULL){
        if(p->next-data == e){
            return  1;    //查找成功返回 1
        }
        p = p->next;
    }
    return  0;    //查找失败返回 0
}

(5)单链表的按位置查找操作:查找单链表L中的i个节点的元素,若找到则将其data数据返回给e,成功返回1,失败返回0;

int  findList(LNode *C, int &e, int i){
    int  j=1;    //标记当前位置
    LNode  *p;
    p = C;    //将p指针指向单链表L的开始节点
    while(p->next!=NULL){
        if(j!=i){
            p = p->next;
            j ++;
        }else{
            e = p.data;
            return 1;    //查找成功返回 1
        }
    }
    return  0;    //查找失败返回 0
}

(5)单链表修改操作:修改单链表中第一个节点数据为e的元素,将其修改为x,若修改成功则返回1,若失败则返回0;

int  updateList(LNode *C, int e, int  x){
    LNode  *p;
    p = C;    //将p指针指向单链表L的开始节点
    while(p->next!=NULL){
        if(p->next-data == e){
            p->next-data == x;
            return  1;            //修改成功返回 1
        }
        p = p->next;
    }
    return  0;    //修改失败返回 0
}

(5)单链表的插入操作:找到单链表的第i个位置,在其后面插入一个新节点s,成功返回1,失败返回0;

int  insertList(LNode  *C, int  i, int e){
    int  j=1;    //标记当前位置
    LNode  *p;
    p = C;    //将p指针指向单链表的开始节点
    while(p!=NULL && j<i){    //寻找第i个节点
        p = p->next;
        j ++;
    }
    if(p==NULL){        //如果到末尾了还没找到,则返回 0
        return 0;
    }
    s = (LNode*)malloc(sizeof(LNode));
    s->data  =  e;
    s->next = p->next;
    p->next = s;
    return  1;
}

(6)单链表的删除操作:删除第 i 个位置的节点,将其data数据赋值给e,若成功则返回 1,若失败则返回 0;

int  deleteList(LNode  *C, int  i, int *e){
    int  j=1;    //标记当前位置
    LNode  *p,  *q;
    p = C;    //将p指针指向单链表的开始节点
    while(p->next!=NULL && j<i){    //寻找第i个节点
        p = p->next;
        j ++;
    }
    if(p->next==NULL){        //如果到末尾了还没找到,则返回 0
        return  0;
    }
    q = p->next;
    p->next = q->next;
    e = q->data;
    free(q);
    return  1;    //删除成功返回 1
}

三、双链表的结构体定义和基本操作

(1)双链表的节点结构体定义:

typedef  struct  DLNode{
    int  data;                //data存放数据域
    struct  DLNode  *next;     //指向后继节点的指针
    struct  DLNode  *prior;    //指向前驱节点的指针
}LNode;

(2)双链表的插入操作:

s->next = p->next;
s-prior = p;
p->next = s;
s-next-prior = s;    //加入p指向最后一个节点,则本行可以去掉

(3)双链表的删除操作:

q = p->next;
p-next = q->next;
q->next->prior = p;
free(q);

四、循环链表的操作

循环单链表和循环双链表是由对应的单链表和双链表改造而来,只需再终端节点和头节点之间建立联系即可。循环单链表终端节点的next节点指针指向表头节点;循环双链表终端节点的next指针指向表头节点,头节点的prior指针指向标为节点。需要注意的是,如果p指针沿着循环链表行走,则判断p走到表尾节点的条件是:p->next==head。循环链表的其余操作均与单链表类似。

五、静态数组简介

一般链表结点空间来自于整个内存,静态链表则来自于一个结构体数组。数组中的每一个结点含有两个分量:一个是数据元素分量data:另一个是指针分量,指示了当前结点的直接后继结点在数组中的位置(这和一般链表中next指针的地位是同等的)。
注意:静态链表中的指针不是我们通常所说的C语言中用来存储内存地址的指针型变量,而是一个存储数组下标的整型变量,通过它可以找到后继结点在数组中的位置,其功能类似于真实的指针,因此称其为指针。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值