408数据结构知识点——第二章 线性表(二)

文章目录

      • 线性表的链式表示
        • 单链表的定义
        • 单链表的插入和删除
          • 按位插入(带头结点)
          • 指定结点的后插操作
          • 指定结点的前插操作
          • 按位删除
          • 指定结点的删除
        • 单链表的查找
          • 按位查找
          • 按值查找
        • 单链表的建立
          • 尾插法建立单链表
          • 头插法建立单链表
        • 双链表
          • 双链表的初始化(带头结点)
          • 双链表的插入
          • 双链表的删除
          • 双链表的遍历
        • 循环链表
          • 循环单链表
          • 循环双链表
        • 静态链表
        • 顺序表和链表的比较

注:内容参考王道2024考研复习指导以及《数据结构》

线性表的链式表示

单链表的定义

链表——用链式结构的方式实现线性表

代码定义如下:

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;

不带头结点的单链表

bool InitList(LinkList &L){
    L=NULL;
    return true;
}
bool ListEmpty(LinkList L){
    return (L==NULL);
}

带头结点的单链表

bool InitList(LinkList &L){
    L=(LNode *)malloc(sizeof(LNode));
    if (L==NULL){
       return false; 
    }
    L->next=NULL;
    return true;
}
bool ListEmpty(LinkList L){
	return (L->next==NULL);
}

:不带头结点,写代码更麻烦,对第一个数据结点和后续数据结点的处理需要用不同的代码逻辑,对空表和非空表的处理需要用不同的代码逻辑。

单链表的插入和删除
按位插入(带头结点)
bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1){
        return false;
    }
    LNode *p;
    int j=0;
    p=L;
    while(p!=NULL && jnext;
        j++;
    }
    //以下代码可以在后插操作封装后替换称InsertNextNode(p,e)
    if(p==NULL){
        return false;
    }
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s-next=p->next;
    p->next=s;
    return true;
}

:不带头结点的链表按位插入,需要对插入第1个结点单独处理,即 i = = 1 i==1 i==1时做单独处理。

指定结点的后插操作
bool InsertNextNode(LNode *p,ElemType e){
    if(p==NULL){
        return false;
    }
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL){
        return false;
    }
    s->data=e;
    s-next=p->next;
    p->next=s;
    return true;
}
指定结点的前插操作
bool InsertPriorNode(LNode *p,ElemType e){
    if(p==NULL){
        return false;
    }
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL){
        return false;
    }
    s->next=p-next;
    p->next=s;
    s->data=p->data;
    p->data=e;
    return true;
}
按位删除
bool ListDelete(LinkList &L,int i,ElemType &e){
    if(i<1){
        return false;
    }
    LNode *p;
    int j=0;
    p=L;
    while(p!=NULL && jnext;
        j++;
    }
    if(p->next==NULL){
        return false;
    }
    LNode *q=p->next
    p-next=q->next;
	e=q->data;
    free(q);
    return true;
}
指定结点的删除
bool DeleteNode(LNode *p){
    if(p==NULL){
        return false;
    }
    LNode *q=p->next;
    p->data=q->data;
    p->next=q->next;
    free(p);
    return true;
}
单链表的查找
按位查找
LNode *GetElem(LinkList L,int i){
    if(i<0){
        return false;
    }
    LNode *p;
    int j=0;
    while(p!=NULL && jnext;
        j++;
    }
    return p;
}
按值查找
LNode *LocateElem(LinkList L,ElemType e){
    LNode *p=L->next;
    while(p!=NULL && p-data!=e){
        p=p->next;
    }
    return p;
}
单链表的建立
尾插法建立单链表
LinkList List_TailInsert(LinkList &L){
    int x;
    L=(LinkList)malloc(sizeof(LNode));
    L->next=NULL;
    LNode *s,*r=L;
    scanf("%d",&x);
    while(x!=-1){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
    return L;
}

当输入:10 16 27时,单链表如下所示

408数据结构知识点——第二章 线性表(二)_第1张图片

头插法建立单链表

重要应用于链表的逆置

LinkList List_HeadInsert(LinkList &L){
    int x;
    L=(LinkList)malloc(sizeof(LNode));
    L->next=NULL;
    LNode *s;
    scanf("%d",&x);
    while(x!=-1){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        s->next=L->next;
        L->next=s;
        scanf("%d",&x);
    }
    return L;
}

当输入:10 16 27时,单链表如下所示

image-20240117232431931

双链表

对于单链表存在无法逆向检索的问题,双链表可进可退,但是存储密度相对于单链表更低一些。

408数据结构知识点——第二章 线性表(二)_第2张图片

结点定义如下:

typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;
}DNode,*DLinkList;
双链表的初始化(带头结点)
bool InitDLinkList(DLinkList &L){
    L=(DlinkList)malloc(sizeof(DNode));
    if(L==NULL){
        return false;
    }
    L->next=NULL;
    L->prior=NULL;
    return true;
}
双链表的插入
bool InsertNextDNode(DNode *p,DNode *s){
    if (p==NULL || s==NULL)//非法参数
        return false;
    s->next=p->next;
    if(p->next != NULL)//如果p结点有后继结点
        p->next->prior=s;
    s->prior=p;
    p->next=s;
    return true;
}
双链表的删除
void DestoryList(DLinkList &L){
    while(L->next != NULL){
        DeleteNextDNode(L);
    }
    free(L);
    L=NULL;
}
bool DeleteNextDNode(DNode *p){
    if(p==NULL){
        return false;
    }
    DNode *q=p-next;
    if(q==NULL){
        return false;
    }
    p->next=q->next;
    if(q->next!=NULL){
        q->next->prior=p;
    }
    free(q);
    return true;
}
双链表的遍历
while (p!=NULL){//后向遍历
    p =p->next;
}
while (p!=NULL){//前向遍历
    p= p->prior;
}
while (p-> prior != NULL){//跳过头结点的前向遍历
	p = p->prior;
}

循环链表
循环单链表

表尾结点的next指针指向头结点。

从一个结点出发可以找到其他任何一个结点。

image-20240117234421866

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;
bool InitList(LinkList &L){
    L=(LNode *)malloc(sizeof(LNode));
    if (L==NULL){
       return false; 
    }
    L->next=L;//与单链表不同之处
    return true;
}
循环双链表

表头结点的 prior 指向表尾结点,表尾结点的 next 指向头结点。

image-20240117234724541

typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;
}DNode,*DLinkList;
bool InitDLinkList(DLinkList &L){
    L=(DlinkList)malloc(sizeof(DNode));
    if(L==NULL){
        return false;
    }
    L->next=L;
    L->prior=L;
    return true;
}
静态链表

分配一整片连续的内存空间,各个结点集中安置。

408数据结构知识点——第二章 线性表(二)_第3张图片

代码定义如下:

#define MaxSize 10
typedef struct {
    ElemType data;
    int next;
}SLinkList[MaxSize];
void testSLinkList(){
    SLinkList a;//a为静态链表
}

查找:从头结点出发挨个往后遍历结点

插入位序为 i 的结点

①找到一个空的结点(next为特殊值,例如-2表示结点为空),存入数据元素

②从头结点出发找到位序为 i-1 的结点

③修改新结点的 next

④修改 i-1 号结点的 next

删除某个结点

①从头结点出发找到前驱结点

②修改前驱结点的游标

③被删除结点 next 设为 -2

  • 优点:增、删 操作不需要大量移动元素

  • 缺点:不能随机存取,只能从头结点开始依次往后查找;容量固定不可变

  • 适用场景:①不支持指针的低级语言;②数据元素数量固定不变的场景(如操作系统的文件分配表FAT)

顺序表和链表的比较
类型 逻辑结构 存储结构 基本操作
顺序表 线性表,线性结构。 支持随机存取、存取密度高;但是大片连续空间分配不方便、改变容量不方便。 在查找方面,顺序表支持随机存取,时间复杂度为 O ( 1 ) O(1) O(1),优于链表;在插入元素和删除元素方面,顺序表时间开销主要来自移动元素。
链表 线性表,线性结构。 离散的小空间分配方便、改变容量方便;但是不可随机存取、存储密度低。 创建方面,链表具有弹性,可扩容,优于顺序表;在插入元素和删除元素方面,链表只需要修改指针,时间开销主要来自查找元素,时间代价更低,优于顺序表。

问题: 请描述顺序表和链表的…实现线性表时,用顺序表还是链表好?
:顺序表和链表的逻辑结构都是线性结构,都属于线性表。但是二者的存储结构不同,顺序表采用顺序存储…(特点,带来的优点缺点);链表采用链式存储…(特点、导致的优缺点)。由于采用不同的存储方式实现,因此基本操作的实现效率也不同。当初始化时…;当插入一个数据元素时…;当删除一个数据元素时…;当查找一个数据元素时…

你可能感兴趣的:(数据结构,考研,学习)