单链表、双链表、循环链表、静态链表

链表

单链表

定义

通过一组任意的存储单元来存储线性表中的数据元素,每个结点除了存放数据元素外,还要存储指向下一个结点的指针。

优点:不要求大片连续空间,改变容易方便。

缺点:不可以随机存取,要耗费一定的空间存放指针。

代码实现单链表

struct LNode{
    ElemType data; //每个节点存放一个数据元素
    struct LNode *next; //指针指向下一个结点
}

使用typedef关键字重命名结构体

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;
//分配空间
LNode *p = (LNode *)malloc(sizeof(LNode));

这样等价于

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

一般LinkList用来表示一个链表,而LNode *来表示一个结点,本质上两个定义方式定义了同一种元素,但是为了方便区分,就将定义方式区分开来。如下代码:

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;
// 这里的返回强调的是返回一个LNode结点,而函数体中的LinkList强调传入的参数L是一个链表。
LNode *GetElem(LinkList L, int i){
    int j=1;
    LNode *p=L->next;
    if(i == 0)
        return L;
    if(i < 1)
        return NULL;
    while(p ! = NULL && j<i){
        p=p->next;
        j++;
    }
    return p;
}

初始化单链表

不带头结点的单链表
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;


bool InitList(LinkList &L){
    L=NULL; //空表,还没节点
    return true;
}


void test(){
    LinkList L;
    //初始化一个空的不带头的单链表
    InitList(L);
}
带头结点的单链表
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 = NULL;
    return ture;
}

void test(){
    LinkList L;
    //初始化一个空的不带头的单链表
    InitList(L);
}

单链表的建立(默认带头的链表)

头插法、尾插法:核心就是初始化操作(InitList)指定操作的后插操作(InsertNextNode)

尾插法
LinkList List_TailInsert(LinkList &l){
    int x;    //设ElemType为整型
    InitList(L);  //建立头结点
    LNode *s,*r=L;  //r为表尾指针
    scanf("%d",&x);
    while(x! = 9999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=s;     //r指向新的表尾结点
        scanf("%d",&x);
    }
    r->next=NULL;
    return L;
}
头插法
LinkList List_HeadInsert(LinkList &L){
    LNode *s;
    int x;
    InitList(L); //初始化  并且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;
}

也可以直接使用我们封装好的方法

LinkList List_HeadInsert(LinkList &L){
    int x;
    InitList(L); //初始化  并且L->next=NULL
    scanf("%d",&x);
    while(x! = 9999){
        InsertNextNode(L,x);//在头结点L后面插入新的元素
        scanf("%d",&x);
    }
    return L;
}

单链表的基本操作

插入
按位序插入(带头结点)
// 在第i个位置插入元素e 带头结点的话每个节点的标号就是i值,如第一个结点 i=1
bool ListInsert(LinkList &L, int i, ElemType e){
   if(i<1)
       return false;
    LNode *p;
    int j=0;
    p=L;
    while(p! = NULL && j<i-1){
        p=p->next;
        j++;
    }
    if(p==NULL)  //i值不合法
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next; //将结点s连接到p之后
    p->next=s; //插入成功
    return true;
}

最好时间复杂度=O(1)

最坏时间复杂度=O(n)

平均时间复杂度=O(n)

按位序插入(不带头结点)
bool ListInsert(LinkList &L, int i, ElemType e){
   if(i<1)
       return false;
    if(i==1){
        LNode *s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=L;
        L=s;   //头指针指向新的结点
        return true
    }
    LNode *p;
    int j=1;  //当前p指向的是第几个结点
    p=L; //p指向的是第一个结点(不是头结点!!)
    while(p! = NULL && j<i-1){
        p=p->next;
        j++;
    }
    if(p==NULL)  //i值不合法
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next; //将结点s连接到p之后
    p->next=s; //插入成功
    return true;
}

时间复杂度与带头结点的按位序插入的时间复杂度相同。

指定结点的后插操作
// 后插操作,在p后插入元素e
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;
}
// 在第i个位置插入元素e 带头结点的话每个节点的标号就是i值,如第一个结点 i=1
bool ListInsert(LinkList &L, int i, ElemType e){
   if(i<1)
       return false;
    LNode *p;
    int j=0;
    p=L;
    while(p! = NULL && j<i-1){
        p=p->next;
        j++;
    }
    return InsertNextNode(p,e);
}
指定节点的前插操作
// 前插操作:在p结点之前插入结点s
bool InsertPriorNode(LNode *p,LNode *s){
    if(p==NULL||s==NULL)
        return false;
    s->next=p->next;
    p->next=s;
    ElemType temp=p->data;
    p->data=s->data;
    s->data=temp;
    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 && j<i-1){
        p=p->next;
        j++;
    }
    if(p==NULL)  //i值不合法
        return false;
    if(p->next==NULL) //第i-1个结点之后么节点了
        return false;
    LNode *q=p->next; //令q指向被删除结点
    e=q->data; //用e返回元素的值
    p->next=q->next;
    free(q);  //释放空间
    return true;
}
指定结点的删除
bool DeleteNode(LNode *p){
    if(p==NULL)
        return false;
    LNode *q=p->next;
    p->data=p->next->data;
    p->next=q->next;
    free(q);
    return true;
}

时间复杂度=O(1)

查找
按位查找
LNode *GetElem(LinkList L, int i){
    int j=0; //当前指向第几个结点 0为头结点
    LNode *p=L;
    while(p! =NULL && j<i){
        p=p->next;
        j++;
    }
    return p;
}
// 增加操作可以封装为
bool ListInsert(LinkList &L, int i,ElemType e){
    if(i<1)
        return false;
    //先找到i个节点的前驱结点,即i-1个结点
    LNode *p=GetElem(L, i-1);
   	// 在i-1结点后插入新的结点
    return InsertNextNode(p,e);
}
按值查找
LNode *LocateElem(LinkList L,ElemType e){
    LNode *p=L->next;
    // 默认ElemType为int  若不为int则不可以直接使用判断符号,需要写相应的函数来对struct类型的数据进行判断!!!!!
    while(p! = NULL && p->data! =e)
        p=p->next;
    return p;
}
求表长
int Length(LinkList L){
    int len=0;
    LNode *p=L;
    while(p->next ! = NULL){
        p=p->next;
        len++;
    }
    return len;
}

双链表

定义

双链表中又两个指针prior和next分别指向其直接前驱结点和直接后继结点。

代码实现双链表

typedef struct DNode{
    ElemType data; //每个节点存放一个数据元素
    struct DNode *prior; //指针指向前一个元素
    struct DNode *next; //指针指向下一个结点
}*DLinkList, DNode;

初始化双链表

bool InitDLinkList(DLinkList &L){
    L=(DNode *)malloc(sizeof(DNode));
    if(L==NULL)
        return false;
    L->prior=NULL;
    L->next=NULL;
    return true;
}
// 判空操作
bool Empty(DLinkList L){
    if(L->next==NULL)
        return true;
    else
        return false;
}

双链表的建立

头插法
// 在头结点后面插入新的结点
DLinkList List_HeadInsert(DLinkList &L){
    InitDLinkList(L);
    DNode *s;
    int x;
    scanf("%d",&x);
    while(x! =9999){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=x;
        InsertNextDNode(L,s);
        scanf("%d",&x);
    }
    return L;
}
尾插法
DLinkList List_TailInsert(DLinkList &l){
    InitDLinkList(L);
    DNode *s,*r=L;  //r为链表末尾指针
    int x;
    scanf("%d",&x);
    while(x! = 9999){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=x;
        InsertNextDNode(r,s); //在尾结点后面插入新节点
        r=r->next; //r指向新的尾结点
    	scanf("%d",&x);
    }
    r->next=NULL;
    return L;
}

双链表的基本操作

插入

前插操作也可以用后插操作来实现,比如在第5个结点前面插入新的结点s,只需要找到第四个结点,在其后面插入s即可。

//在p结点后面插入s结点
bool InsertNextDNode(DNode *p, DNode *s){
    if(p==NULL || s==NULL) //非法操作
        return false;
    s->next=p->next;
    if(p->next! = NULL)
        p->next->prior=s;
    s->prior=p;
    p->next=s;
    return true;
}
按位序插入
在第i处插入结点s
bool ListInsert(DLinkList &L, DNode *s, int i){
    if(i<0)
        return false;  //i值非法
    int j=0;  //j表示当前是第几个结点
    DNode *p=L;
    while(p! =null && j<i-1){
        p=p->next;
        j++;
    }
    return InsertNextDNode(p, s);
}
删除
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p){
    if(p==NULL)
        return false; //当前结点为空
    DNode *q=p->next;   //找到p的后继节点
    if(q==NULL)
        return false; //没有后继节点
    p->next=q->next;  
    if(q->next ! =NULL)
        q->next->prior=p; // q不是最后一个结点 
    free(q);
    return true;
}

//销毁双链表
void DestroyList(DLinkList &L){
    // 循环释放每个数据节点
    while(L->next ! =NULL)
        DeleteNextDNode(L);
    free(L);   //释放头结点
    L=NULL;    //头指针指向NULL
}
查找

同单链表的查找


循环链表

循环单链表

初始化
bool InitLinkList(LinkList &L){
    L=(LNode *)malloc(sizeof(LNode));
    if(L==NULL)
        return false;
    L->next=L;
    return true;
}
判空
// 判断循环单链表是否为空
bool Empty(LinkList L){
    if(L->next==L)
        return true;
    else
        return false;
}
判断是否为表尾结点
bool isTail(LinkList L){
    if(p->next==L)
        return true;
    else
        return false;
}
插入

与单链表插入方法相同

循环双链表

初始化
bool InitDLinkList(DLinkList &L){
    L=(DNode *)malloc(sizeof(DNode));
    if(L==NULL)
        return false;
    L->next=L;
    L->prior=L;
    return true;
}
判空
// 判断循环双链表是否为空
bool Empty(DLinkList L){
    if(L->next==L)
        return true;
    else
        return false;
}
判断是否为表尾

同循环单链表判断方法

插入
bool InsertNextDNode(DNode *p, DNode *s){
    s->next=p->next; //将结点s插入到p后面
    p->next->prior=s; //循环双链表无需考虑p结点是否为尾结点,即使是尾结点,p->next->prior也不为空,这是课本上给出双链表后插的方法
    s->prior=p;
    p->next=s;
}
删除
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p){
    DNode *q=p->next;   //找到p的后继节点
    p->next=q->next;
    q->next->prior=p;  
    free(q);
    return true;
}


静态链表

用数组的方式实现链表

#define MaxSize 10
typedef struct{
    ElemType data;  //存储的数据元素
    int next;   //下一个结点的存放位置
}SLinkList[MaxSize];

初始化静态链表

把静态链表a[0]的next初始化为-1,表示a[0]为头结点,同时将其他位置的next初始化为-2,表示当前位置未存放数据。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值