双链表和循环双链表复习

首先不管是单链表还是双链表或者循环双链表,都是为了提高访问效率,比如同样的插入操作,顺序表需要用数组移动元素,访问效率比较差,相反链表只需要移动指针,而且顺序表是有最大空间的,而链表没有。

双链表格式:


struct node     //双向链表
{
    ElemType data;   //数据域
    struct node *next;   //指针域   指向下一个后继方向
    struct node *prior;           //指向前一个前驱方向
};

结点指向 p == p->prior->next == p->next->prior

双向链表和单链表各个功能使用的区别:

1.初始化;

int LinkInit(Node **l)
{
    *l = (Node *)malloc(sizeof(Node) * 1);   //分配头结点   l 就是头指针
    if (NULL == *l)
    {
        return FAILURE;
    }

    (*l)->next = NULL;    //头结点指针域为空
    (*l)->prior = NULL;   //双向链表,头结点prior指针为空。可以看出相比单链表多分配一个指针

    return SUCCESS;
}

 

 

2.插入函数:

int LinkInsert(Node *l, int n, ElemType e)  //n 插入的位置
{
    Node *p = l;
    int k = 1;   //k 移动的次数

    if (NULL == l)
    {
        return FAILURE;
    }

    while (k < n && p != NULL)
    {
        p = p->next;
        k++;
    }

    if (k > n || p == NULL)
    {
        return FAILURE;
    }

    Node *q = (Node *)malloc(sizeof(Node) * 1);
    if (NULL == q)
    {
        return FAILURE;
    }

    q->data = e;
    q->prior = p;//多了一步指向p头指针的前继
    q->next = p->next;
    p->next = q;
    if (NULL != q->next)    //多了一步如果q不是最后一个结点(画图理解会发现,后面p后面结点如果没有这一步会少一个前驱)
    {
        q->next->prior = q;
    }

    return SUCCESS;
}

3.浏览函数和单链表一样

int LinkTraverse(Node *l, void (*p)(ElemType))//比较函数判断是否相等
{
    if (NULL == l)
    {
        return FAILURE;
    }
    Node *q = l;

    while (q->next)
    {
        q = q->next;
        p(q->data);
    }

    return SUCCESS;
}

4,长度函数一样

int LinkLength(Node *l)
{
    if (NULL == l)
    {
        return FAILURE;
    }

    int len = 0;
    Node *p = l->next;

    while (p)
    {
        len++;
        p = p->next;
    }

    return len;
}

 

5.判空函数一样

int LinkEmpty(Node *l)
{
    return (l->next == NULL) ? TRUE : FALSE;
}

6删除函数:

int LinkDelete(Node *l, int p, ElemType *e)//删除位置p
{
    int k = 1;
    Node *q = l;

    if (l == NULL)
    {
        return FAILURE;
    }

    while (k < p && q != NULL)//移动被删元素前一个
    {
        q = q->next;
        k++;
    }

    if (k > p || q == NULL)
    {
        return FAILURE;
    }

    Node *n = q->next;//让n指向q后面要删的元素
    *e = n->data;//将被删元素放入e中
    q->next = n->next;  //指向被删元素的后面一个 ,如果n是最后一个指向nuLL
    if (n->next != NULL)     //如果不是最后一个进入循环  
    {
        n->next->prior = q;//让n后面的元素的前继指向q
    }
    free(n);释放删除元素

    return SUCCESS;
}

 

清空函数的区别:

int LinkClear(Node *l)
{
    if (NULL == l)
    {
        return FAILURE;
    }

    Node *p = l->next;

    while (p)
    {
        l->next = p->next;
        //p->next->prior = l;//可有可无,目的是让后面结点的前驱指向头结点
        free(p);
        p = l->next;//防止p为野指针,free(P)会让变成NULL,并将p往后指向,依次释放,直到最后一个结点
    }

    return SUCCESS;
}

 

注意:双链表没有倒叙的说法
二:双链循环链表

循环链表,和单链表最大的区别,单链表最后指向NULL;而循环链表指向头结点或者第一个结点

 

如图:可以看出首尾的链表,在空表的时候都是头尾都是指向自己的。

所以初始化也与单链表有一定区别:

int LinkInit(Node **l)
{
    *l = (Node *)malloc(sizeof(Node) * 1);   //分配头结点   l 就是头指针
    if (NULL == *l)
    {
        return FAILURE;
    }

    (*l)->next = (*l);    //后继指向自己
    (*l)->prior = (*l);   //前驱指向自己

    return SUCCESS;
}

插入函数:

int LinkInsert(Node *l, int n, ElemType e)  //n 插入的位置
{
    Node *p = l;
    int k = 1;   //k 移动的次数

    if (NULL == l)    //入参判断
    {
        return FAILURE;
    }

    if (n > LinkLength(l) + 1)    //超过最大长度报错,由于是循环链表,最后会回到头结点
    {
        return FAILURE;
    }

    while (k < n)     //***
    {
        p = p->next;
        k++;
    }


    if (k > n)    //循环次数大于插入位置,会让数据变得不准确,比如n=0,k=1,不进入while循环,数据被送入头结点
    {
        return FAILURE;
    }

    Node *q = (Node *)malloc(sizeof(Node) * 1);
    if (NULL == q)
    {
        return FAILURE;
    }


    q->data = e;
    q->prior = p;
    q->next = p->next;
    p->next = q;
    if (l != q->next)    //如果q不是最后一个结点 **
    {
        q->next->prior = q;
    }

    return SUCCESS;
}
 

浏览函数:

 

int LinkTraverse(Node *l, void (*p)(ElemType))
{
    if (NULL == l)
    {
        return FAILURE;
    }
    Node *q = l;

    while (q->next != l)     //不等于本身  
    {
        q = q->next;
        p(q->data);
    }

    return SUCCESS;
}

测试长度函数:

int LinkLength(Node *l)
{
    if (NULL == l)
    {
        return FAILURE;
    }

    int len = 0;
    Node *p = l->next;

    while (p != l)   //不等于本身 **
    {
        len++;
        p = p->next;
    }

    return len;
}

判断是否为空函数

int LinkEmpty(Node *l)
{
    return (l->next == l && l->prior == l) ? TRUE : FALSE;  //前驱和后继指向头结点,判断为空表
}

元素获取函数

int GetElem(Node *l, int p, ElemType *e)    //p 位置
{
    if (NULL == l || p < 1)   //入参判断  p不能小于0
    {
        return FAILURE;
    }

    Node *q = l->next;   //双向循环链表,q指向第一个结点  //**
    int i;

    for (i = 1; i < p && q != l; i++)    //循环p次,同时满足q不为空  双向循环链表 i = 1**
    {
        q = q->next;
    }

    if (q == l)      //如果q为空,说明p(位置)大于长度  **这里要用q=L来判断插入位置是否大于长度
    {
        return FAILURE;
    }

    *e = q->data;     //q已经指向第p个结点

    return SUCCESS;
}

查询函数:

 

int LocateElem(Node *l, ElemType e, int (*p)(ElemType, ElemType))
{
    if (NULL == l)
    {
        return FAILURE;
    }
    
    Node *q = l->next;
    int len = 1;

    while (q != l)     //双向循环链表  最后指向头结点
    {
        if (p(e, q->data) == TRUE)
        {
            return len;
        }
        q = q->next;
        len++;
    }

    return FAILURE;
}

 

 

删除函数:

int LinkDelete(Node *l, int p, ElemType *e)
{
    int k = 1;
    Node *q = l;

    if (l == NULL)
    {
        return FAILURE;
    }

    if (p > LinkLength(l) + 1)   //判断位置p是否合法  ,因为相对于普通链表没有p=nuLL末尾判断条件
    {
        return FAILURE;
    }

    while (k < p)  //**
    {
        q = q->next;
        k++;
    }

    if (k > p)  //**
    {
        return FAILURE;
    }

    Node *n = q->next;
    *e = n->data;
    q->next = n->next;   
                         //如果不是最后一个  **

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

    return SUCCESS;
}

 

清空函数:

int LinkClear(Node *l)
{
    if (NULL == l)
    {
        return FAILURE;
    }

    Node *p = l->next;

    while (p != l)  //不回到头结点时
    {
        l->next = p->next;
        //p->next->prior = l;
        free(p);
        p = l->next;
    }

    l->prior = l;   //**

    return SUCCESS;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值