数据结构学习之路-第二章:循环链表

【 声明:版权所有,转载请标明出处,请勿用于商业用途。  联系信箱:libin493073668@sina.com】


前言:

前面我们已经学会了单链表的动态与静态操作,我们知道了单链表与顺序表各自的优点与缺点,但是单链表还是有缺点的,由于其存储方式的随机性,那么如果我仅仅知道单链表的其中一个结点的地址,那么我该如何寻找它前面那些结点的位置呢?在单链表中我们仅仅只保存了每个结点的下一个结点的指针域,那么一旦我们找到最后的结点了,我们却并不能返回头结点。怎么解决这个问题呢?接下来我们就将要学习循环链表。


注:

本文仅代表博主本人的一些浅显的见解,欢迎大家评论学习,共同创造出一个良好的环境

对于一些问题,博主会尽量为大家解答,但是如果有疑问没有及时回答的,也希望其他热心人心帮忙解决,鄙人不胜感激


循环单链表


1.存储结构

单链的循环链表存储结构和单链表的存储结构是一样的。

所不同的是,最后一个结点的next指向头结点,而不是“空”。

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


2.相关操作

由于循环单链表是根据单链表而来的,相关的一些操作基本也差不多,我们知道循环链表中,最后一个结点的next指向头结点,因此由表尾找到表头很简单,但是若果链表很长,则由表头找到表尾却很费事,因此单循环链表一般设立尾指针而不是头指针,如图所示:


Status InitList_CL(LinkList *L)
{
    //操作结果:构造一个空的线性表L
    (*L) = (LinkList)malloc(sizeof(struct LNode));   //产生头结点,并使L指向此头结点
    if(!(*L)) exit(OVERFLOW);        //存储分配失败
    (*L)->next = (*L);               //指针域指向头结点
    return OK;
}

Status DestroyList_CL(LinkList *L)
{
    //操作结果:销毁线性表L
    LinkList q,p = (*L)->next;  //p指向头结点
    while(p!=(*L))              //没到表尾
    {
        q = p->next;
        free(p);
        p = q;
    }
    free(*L);
    (*L) = NULL;
    return OK;
}

Status ClearList_CL(LinkList *L)
{
    //初始条件:线性表L已存在。操作结果:将L重置为空表
    LinkList p,q;
    (*L) = (*L)->next;  //L指向头结点
    p = (*L)->next;     //p指向第一个结点
    while(p!=(*L))      //没到表尾
    {
        q = p->next;
        free(p);
        p = q;
    }
    (*L)->next = (*L);  //头结点指针域指向自身
    return OK;
}

Status ListEmpty_CL(LinkList L)
{
    //初始条件:线性表L已存在。
    //操作结果:若L为空表,则返回TRUE,否则返回FALSE
    return L->next == L;
}

int ListLength_CL(LinkList L)
{
    //初始条件:L已存在。
    //操作结果:返回L中数据元素个数
    int i = 0;
    LinkList p = L->next;   //p指向头结点
    while(p!=L)             //没到表尾
    {
        i++;
        p = p->next;
    }
    return i;
}

Status GetElem_CL(LinkList L,int i,ElemType *e)
{
    //操作结果:当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
    int j = 1;                      //初始化,j为计数器
    LinkList p = L->next->next;     //p指向第一个结点
    if(i<=0 || i>ListLength_CL(L)) return ERROR;   //第i个元素不存在
    while(j<i)
    {
        //顺指针向后查找,直到p指向第i个元素
        p = p->next;
        j++;
    }
    *e = p->data;  //取第i个元素
    return OK;
}

int LocateElem_CL(LinkList L,ElemType e,Status (*compare)(ElemType,ElemType))
{
    //初始条件:线性表L已存在,compare()是数据元素判定函数
    //操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值为0
    int i = 0;
    LinkList p = L->next->next;           //p指向第一个结点
    while(p!=L->next)
    {
        i++;
        if(compare(p->data,e)) return i;  //满足关系
        p = p->next;
    }
    return 0;
}

Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e)
{
    //初始条件:线性表L已存在
    //操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义 */
    LinkList q,p = L->next->next;  //p指向第一个结点
    q = p->next;
    while(q!=L->next)              //p没到表尾
    {
        if(q->data==cur_e)
        {
            *pre_e = p->data;
            return TRUE;
        }
        p = q;
        q = q->next;
    }
    return FALSE;
}

Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e)
{
    //初始条件:线性表L已存在
    //操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义 */
    LinkList p=L->next->next;   //p指向第一个结点
    while(p!=L)                 //p没到表尾
    {
        if(p->data==cur_e)
        {
            *next_e = p->next->data;
            return TRUE;
        }
        p = p->next;
    }
    return FALSE;
}

Status ListInsert_CL(LinkList *L,int i,ElemType e)
{
    //操作结果:在L的第i个位置之前插入元素e
    LinkList p = (*L)->next,s;   //p指向头结点
    int j = 0;
    if(i<=0 || i>ListLength_CL(*L)+1) return ERROR;   //无法在第i个元素之前插入
    while(j<i-1)        //寻找第i-1个结点
    {
        p = p->next;
        j++;
    }
    s = (LinkList)malloc(sizeof(struct LNode));   //生成新结点
    s->data = e;                                  //插入L中
    s->next = p->next;
    p->next = s;
    if(p==(*L)) (*L) = s;                         //改变尾结点
    return OK;
}

Status ListDelete_CL(LinkList *L,int i,ElemType *e)
{
    //操作结果:删除L的第i个元素,并由e返回其值
    LinkList p = (*L)->next,q;                     //p指向头结点
    int j = 0;
    if(i<=0 || i>ListLength_CL(*L)) return ERROR;  //第i个元素不存在
    while(j<i-1)                                   //寻找第i-1个结点
    {
        p = p->next;
        j++;
    }
    q = p->next;              //q指向待删除结点
    p->next = q->next;
    *e = q->data;
    if((*L)==q) (*L) = p;    //删除的是表尾元素
    free(q);                 //释放待删除结点
    return OK;
}

Status ListTraverse_CL(LinkList L,void (*vi)(ElemType))
{
    //初始条件:L已存在。
    //操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
    LinkList p = L->next->next;
    while(p!=L->next)
    {
        vi(p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

3.合并操作

循环链表的合并操作比较简单,只需要将一个表的表尾和另一个表的表头相接即可,如图:


void MergeList_CL(LinkList *La,LinkList Lb)
{
    //操作结果:将Lb表合并到La表后面,并返回新形成的La表
    LinkList p = Lb->next;
    Lb->next = (*La)->next; //Lb的尾结点指向La头结点
    (*La)->next = p->next;  //La的尾结点指向Lb的头结点
    free(p);
    *La = Lb;               //La的尾指针地址变为Lb的尾指针地址
}

4.具体测试

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
#include <ctype.h>
#include <malloc.h>
#include <limits.h>
#include <stdlib.h>
#include <io.h>
#include <process.h>
using namespace std;

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1

typedef int ElemType;
typedef int Status;

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

Status InitList_CL(LinkList *L)
{
    //操作结果:构造一个空的线性表L
    (*L) = (LinkList)malloc(sizeof(struct LNode));   //产生头结点,并使L指向此头结点
    if(!(*L)) exit(OVERFLOW);        //存储分配失败
    (*L)->next = (*L);               //指针域指向头结点
    return OK;
}

Status DestroyList_CL(LinkList *L)
{
    //操作结果:销毁线性表L
    LinkList q,p = (*L)->next;  //p指向头结点
    while(p!=(*L))              //没到表尾
    {
        q = p->next;
        free(p);
        p = q;
    }
    free(*L);
    (*L) = NULL;
    return OK;
}

Status ClearList_CL(LinkList *L)
{
    //初始条件:线性表L已存在。操作结果:将L重置为空表
    LinkList p,q;
    (*L) = (*L)->next;  //L指向头结点
    p = (*L)->next;     //p指向第一个结点
    while(p!=(*L))      //没到表尾
    {
        q = p->next;
        free(p);
        p = q;
    }
    (*L)->next = (*L);  //头结点指针域指向自身
    return OK;
}

Status ListEmpty_CL(LinkList L)
{
    //初始条件:线性表L已存在。
    //操作结果:若L为空表,则返回TRUE,否则返回FALSE
    return L->next == L;
}

int ListLength_CL(LinkList L)
{
    //初始条件:L已存在。
    //操作结果:返回L中数据元素个数
    int i = 0;
    LinkList p = L->next;   //p指向头结点
    while(p!=L)             //没到表尾
    {
        i++;
        p = p->next;
    }
    return i;
}

Status GetElem_CL(LinkList L,int i,ElemType *e)
{
    //操作结果:当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
    int j = 1;                      //初始化,j为计数器
    LinkList p = L->next->next;     //p指向第一个结点
    if(i<=0 || i>ListLength_CL(L)) return ERROR;   //第i个元素不存在
    while(j<i)
    {
        //顺指针向后查找,直到p指向第i个元素
        p = p->next;
        j++;
    }
    *e = p->data;  //取第i个元素
    return OK;
}

int LocateElem_CL(LinkList L,ElemType e,Status (*compare)(ElemType,ElemType))
{
    //初始条件:线性表L已存在,compare()是数据元素判定函数
    //操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值为0
    int i = 0;
    LinkList p = L->next->next;           //p指向第一个结点
    while(p!=L->next)
    {
        i++;
        if(compare(p->data,e)) return i;  //满足关系
        p = p->next;
    }
    return 0;
}

Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e)
{
    //初始条件:线性表L已存在
    //操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义 */
    LinkList q,p = L->next->next;  //p指向第一个结点
    q = p->next;
    while(q!=L->next)              //p没到表尾
    {
        if(q->data==cur_e)
        {
            *pre_e = p->data;
            return TRUE;
        }
        p = q;
        q = q->next;
    }
    return FALSE;
}

Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e)
{
    //初始条件:线性表L已存在
    //操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义 */
    LinkList p=L->next->next;   //p指向第一个结点
    while(p!=L)                 //p没到表尾
    {
        if(p->data==cur_e)
        {
            *next_e = p->next->data;
            return TRUE;
        }
        p = p->next;
    }
    return FALSE;
}

Status ListInsert_CL(LinkList *L,int i,ElemType e)
{
    //操作结果:在L的第i个位置之前插入元素e
    LinkList p = (*L)->next,s;   //p指向头结点
    int j = 0;
    if(i<=0 || i>ListLength_CL(*L)+1) return ERROR;   //无法在第i个元素之前插入
    while(j<i-1)        //寻找第i-1个结点
    {
        p = p->next;
        j++;
    }
    s = (LinkList)malloc(sizeof(struct LNode));   //生成新结点
    s->data = e;                                  //插入L中
    s->next = p->next;
    p->next = s;
    if(p==(*L)) (*L) = s;                         //改变尾结点
    return OK;
}

Status ListDelete_CL(LinkList *L,int i,ElemType *e)
{
    //操作结果:删除L的第i个元素,并由e返回其值
    LinkList p = (*L)->next,q;                     //p指向头结点
    int j = 0;
    if(i<=0 || i>ListLength_CL(*L)) return ERROR;  //第i个元素不存在
    while(j<i-1)                                   //寻找第i-1个结点
    {
        p = p->next;
        j++;
    }
    q = p->next;              //q指向待删除结点
    p->next = q->next;
    *e = q->data;
    if((*L)==q) (*L) = p;    //删除的是表尾元素
    free(q);                 //释放待删除结点
    return OK;
}

Status ListTraverse_CL(LinkList L,void (*vi)(ElemType))
{
    //初始条件:L已存在。
    //操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
    LinkList p = L->next->next;
    while(p!=L->next)
    {
        vi(p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

void MergeList_CL(LinkList *La,LinkList Lb)
{
    //操作结果:将Lb表合并到La表后面,并返回新形成的La表
    LinkList p = Lb->next;
    Lb->next = (*La)->next; //Lb的尾结点指向La头结点
    (*La)->next = p->next;  //La的尾结点指向Lb的头结点
    free(p);
    *La = Lb;               //La的尾指针地址变为Lb的尾指针地址
}

Status compare(ElemType c1,ElemType c2)
{
    return c1==c2;
}

void visit(ElemType c)
{
    printf("%d ",c);
}

int main()
{
    LinkList L;
    ElemType e;
    int j;
    Status i;
    i=InitList_CL(&L);        //初始化单循环链表L
    printf("初始化单循环链表L i=%d (1:初始化成功)\n",i);
    i=ListEmpty_CL(L);
    printf("L是否空 i=%d(1:空 0:否)\n",i);
    ListInsert_CL(&L,1,3);    //在L中依次插入3,5
    ListInsert_CL(&L,2,5);
    i=GetElem_CL(L,1,&e);
    j=ListLength_CL(L);
    printf("L中数据元素个数=%d,第1个元素的值为%d。\n",j,e);
    printf("L中的数据元素依次为:");
    ListTraverse_CL(L,visit);
    PriorElem_CL(L,5,&e);     //求元素5的前驱
    printf("5前面的元素的值为%d。\n",e);
    NextElem_CL(L,3,&e);      //求元素3的后继
    printf("3后面的元素的值为%d。\n",e);
    printf("L是否空 %d(1:空 0:否)\n",ListEmpty_CL(L));
    j=LocateElem_CL(L,5,compare);
    if(j)
        printf("L的第%d个元素为5。\n",j);
    else
        printf("不存在值为5的元素\n");
    i=ListDelete_CL(&L,2,&e);
    printf("删除L的第2个元素:\n");
    if(i)
    {
        printf("删除的元素值为%d,现在L中的数据元素依次为:",e);
        ListTraverse_CL(L,visit);
    }
    else
        printf("删除不成功!\n");
    printf("清空L:%d(1: 成功)\n",ClearList_CL(&L));
    printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty_CL(L));
    printf("销毁L:%d(1: 成功)\n",DestroyList_CL(&L));

    //合并
    int n=5,k;
    LinkList La,Lb;
    InitList_CL(&La);
    for(k=1; k<=n; k++)
        ListInsert_CL(&La,k,k);
    printf("La=");         //输出链表La的内容
    ListTraverse_CL(La,visit);
    InitList_CL(&Lb);
    for(k=1; k<=n; k++)
        ListInsert_CL(&Lb,1,k*2);
    printf("Lb=");         //输出链表Lb的内容
    ListTraverse_CL(Lb,visit);
    MergeList_CL(&La,Lb);
    printf("La+Lb=");      //输出合并后的链表的内容
    ListTraverse_CL(La,visit);

    return 0;
}

总结:

循环链表貌似并没有说什么新的东西,就连书本也就是一笔带过,不知道大家是否学到了一点东西呢?

目前我们一直讲的都只是单向的链表,有的同学可能早就有想法了,难道我就不能再在结构体内添加一个指向前一个元素的指针域吗?

答案当然是可以的,这就是我们下一次将要说的双向链表的内容了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值