由于单向链表只能从某个节点开始单向访问其后继节点,并没有存储其前驱节点信息,访问前面的节点是不容易办到的。
为了能从某个节点开始,既可以访问前驱节点也可以访问后继节点,我们可以改造一下单向链表的节点结构,在结构中多参加一个指向前驱节点的指针。

节点结构体类型
1.相比于单向链表的节点类型,多了一个指向前驱节点的指针
2.通过任意一个节点,既可以访问前驱节点也可以访问后继节点
typedef char datatype;  //数据元素类型
//定义双向链表结点类型
typedef struct Node
{
    union
    {
     datatype   data;     //普通结点数据域
     int len;           //头结点数据域
    };
     struct Node *prio;   //指向前驱节点的指针
     struct Node *next;   //指向后继节点的指针
}Node, *Node_ptr;
创建双向链表
1.只需要创建一个头节点即可
2.初始化时,需要两个指针域都置空
3.返回值为头节点的指针
//创建双向链表
Node_ptr list_create()
{
    //堆区申请一个头结点的大小
    Node_ptr L = (Node_ptr)malloc(sizeof(Node));
    if(NULL==L)
    {
        printf("链表创建失败\n");
        return NULL;
    }
    //初始化头结点
    L->len = 0;          //链表长度
    L->prio = NULL;        //前驱指针为空
    L->next = NULL;        //后继指针为空
    printf("链表创建成功\n");
    return L;
}
双向链表判空
//判空:1表示空,0表示非空
int list_empty(Node_ptr L)
{
    //判断逻辑
    if(NULL==L)
    {
        printf("链表不合法\n");
        return -1; 
    }
    return L->next==NULL;
}
申请节点封装数据
static Node_ptr list_node_apply(datatype e)
{
    //堆区申请一个结点
    Node_ptr p = (Node_ptr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("结点申请失败\n");
        return NULL;
    }
    //将数据封装进数据域
    p->data = e;
    p->next = NULL;
    p->prio = NULL;
    return p;
}
双向链表头插
//头插
int list_insert_head(Node_ptr L, datatype e)
{
    //判断逻辑
    if(NULL == L)
    {
        printf("所给链表不合法\n");
        return -1;
    }
    //申请结点封装数据
    Node_ptr p = list_node_apply(e);
    if(NULL==p)
    {
        return -1;
    }
    //头插逻辑
    if(list_empty(L))
    {
        //链表为空
        p->prio = L;
        L->next = p;
    }else
    {
        //链表不为空
        p->next = L->next;
        p->prio = L;
        L->next->prio = p;   //p->next->prio = p;
        L->next = p;
    }
    //表长变化
    L->len++;
    printf("插入成功\n");
    return 0;
}
双向链表的遍历
//遍历
void list_show(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("遍历失败\n");
        return ;
    }
    //遍历逻辑
    printf("当前链表中的元素分别是:");
    Node_ptr q = L->next;    //定义遍历指针从第一个结点出发
    while(q!=NULL)
    {
        printf("%c\t", q->data);
        q = q->next;
    }
    printf("\n");
}
按位置查找返回节点
//按位置查找返回结点
Node_ptr list_search_pos(Node_ptr L, int pos)
{
    //判断逻辑
    if(NULL==L || list_empty(L) ||pos<0 || pos>L->len)
    {
        printf("查找失败\n");
        return NULL;
    }
    //定义遍历指针从头结点出发
    Node_ptr q = L;
    for(int i=0; i<pos; i++)
    {
        q = q->next;
    }
    //将找到的结点返回
    return q;
}
双向链表任意位置插入
1.插入时。可以找到要插入的位置进行插入,也可以找到其前驱节点进行插入
2.使用双向链表完成尾插时要额外注意
//任意位置插入
int list_insert_pos(Node_ptr L, int pos, datatype e)
{
    //判断逻辑
    if(NULL==L || pos<1 || pos>L->len+1)
    {
        printf("插入失败\n");
        return -1;
    }
    //找到其前驱结点
    Node_ptr q = list_search_pos(L, pos-1);
    //申请结点封装数据
    Node_ptr p = list_node_apply(e);
    if(NULL==p)
    {
        return -1;
    }
    //插入逻辑
    if(q->next ==NULL)
    {
        //说明要进行尾插
        p->prio = q;
        q->next = p;
    }else
    {
        //中间任意位置
        p->next = q->next;
        p->prio = q;
        q->next->prio = p;
        q->next = p;
    }
    //表长变化
    L->len++;
    printf("插入成功\n");
    return 0;
}
双向链表的头删
//头删
int list_delete_head(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("删除失败\n");
        return -1;
    }
    //删除逻辑
    Node_ptr p = L->next;    //标记
    //判断要删除的结点后面是否还有结点
    if(NULL == p->next)
    {
        //说明p是链表中最后一个结点
        L->next = NULL;
    }else
    {
        //说明p不是最后一个结点
        L->next = p->next;
        p->next->prio = L;
    }
    free(p);          //释放结点
    p = NULL;
    //表长变化
    L->len--;
    printf("删除成功\n");
    return 0;
}
双向链表任意位置删除
//任意位置删除
int list_delete_pos(Node_ptr L, int pos)
{
    //判断逻辑
    if(NULL==L ||list_empty(L) ||pos<1 || pos>L->len)
    {
        printf("删除失败\n");
        return -1;
    }
    //找到要删除的结点
    Node_ptr q = list_search_pos(L, pos);
    //删除逻辑
    if(q->next == NULL)
    {
        //说明要删除的是最后一个结点
        q->prio->next = NULL;   //将前面的结点后继指针置空
    }else
    {
        //交接工作
        q->prio->next = q->next;
        q->next->prio = q->prio;
    }
    free(q);       //释放结点
    q = NULL;
    //表长变化
    L->len--;
    printf("删除成功\n");
    return 0;
}
双向链表按位置修改
//按位置进行修改
int list_update_pos(Node_ptr L, int pos, datatype e)
{
    //判断逻辑
    if(NULL==L || list_empty(L) ||pos<1 || pos>L->len)
    {
        printf("修改失败\n");
        return -1;
    }
    //找到要修改的结点
    Node_ptr p = list_search_pos(L, pos);
    
    p->data = e;          //将当前数据域进行修改
    printf("修改成功\n");
    return 0;
}
双向链表的释放
//销毁双向链表
void list_destroy(Node_ptr L)
{
    //判断链表是否合法
    if(NULL == L)
    {
        return ;
    }
    //开始将所有结点释放
    while(!list_empty(L))
    {
        //不断进行头删
        list_delete_head(L);
    }
    //释放头结点
    free(L);
    L = NULL;
    printf("销毁成功\n");
}
全部代码
doublelinklist.h
#ifndef DOUBLKELINKLIST_H
#define DOUBLKELINKLIST_H
typedef char datatype;  //数据元素类型
//定义双向链表结点类型
typedef struct Node
{
    union
    {
     datatype   data;     //普通结点数据域
     int len;           //头结点数据域
    };
     struct Node *prio;   //指向前驱节点的指针
     struct Node *next;   //指向后继节点的指针
}Node, *Node_ptr;
//创建双向链表
Node_ptr list_create();
//判空
int list_empty(Node_ptr L);
//头插
int list_insert_head(Node_ptr L, datatype e);
//遍历
void list_show(Node_ptr L);
//按位置查找返回结点
Node_ptr list_search_pos(Node_ptr L, int pos);
//任意位置插入
int list_insert_pos(Node_ptr L, int pos, datatype e);
//头删
int list_delete_head(Node_ptr L);
//任意位置删除
int list_delete_pos(Node_ptr L, int pos);
//按位置进行修改
int list_update_pos(Node_ptr L, int pos, datatype e);
//销毁双向链表
void list_destroy(Node_ptr L);
#endif
doublelinklist.c
#include<myhead.h>
#include"doublelinklist.h"
//创建双向链表
Node_ptr list_create()
{
    //堆区申请一个头结点的大小
    Node_ptr L = (Node_ptr)malloc(sizeof(Node));
    if(NULL==L)
    {
        printf("链表创建失败\n");
        return NULL;
    }
    //初始化头结点
    L->len = 0;          //链表长度
    L->prio = NULL;        //前驱指针为空
    L->next = NULL;        //后继指针为空
    printf("链表创建成功\n");
    return L;
}
//判空:1表示空,0表示非空
int list_empty(Node_ptr L)
{
    //判断逻辑
    if(NULL==L)
    {
        printf("链表不合法\n");
        return -1; 
    }
    return L->next==NULL;
}
//定义申请结点封装数据函数
static Node_ptr list_node_apply(datatype e)
{
    //堆区申请一个结点
    Node_ptr p = (Node_ptr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("结点申请失败\n");
        return NULL;
    }
    //将数据封装进数据域
    p->data = e;
    p->next = NULL;
    p->prio = NULL;
    return p;
}
//头插
int list_insert_head(Node_ptr L, datatype e)
{
    //判断逻辑
    if(NULL == L)
    {
        printf("所给链表不合法\n");
        return -1;
    }
    //申请结点封装数据
    Node_ptr p = list_node_apply(e);
    if(NULL==p)
    {
        return -1;
    }
    //头插逻辑
    if(list_empty(L))
    {
        //链表为空
        p->prio = L;
        L->next = p;
    }else
    {
        //链表不为空
        p->next = L->next;
        p->prio = L;
        L->next->prio = p;   //p->next->prio = p;
        L->next = p;
    }
    //表长变化
    L->len++;
    printf("插入成功\n");
    return 0;
}
//遍历
void list_show(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("遍历失败\n");
        return ;
    }
    //遍历逻辑
    printf("当前链表中的元素分别是:");
    Node_ptr q = L->next;    //定义遍历指针从第一个结点出发
    while(q!=NULL)
    {
        printf("%c\t", q->data);
        q = q->next;
    }
    printf("\n");
}
//按位置查找返回结点
Node_ptr list_search_pos(Node_ptr L, int pos)
{
    //判断逻辑
    if(NULL==L || list_empty(L) ||pos<0 || pos>L->len)
    {
        printf("查找失败\n");
        return NULL;
    }
    //定义遍历指针从头结点出发
    Node_ptr q = L;
    for(int i=0; i<pos; i++)
    {
        q = q->next;
    }
    //将找到的结点返回
    return q;
}
//任意位置插入
int list_insert_pos(Node_ptr L, int pos, datatype e)
{
    //判断逻辑
    if(NULL==L || pos<1 || pos>L->len+1)
    {
        printf("插入失败\n");
        return -1;
    }
    //找到其前驱结点
    Node_ptr q = list_search_pos(L, pos-1);
    //申请结点封装数据
    Node_ptr p = list_node_apply(e);
    if(NULL==p)
    {
        return -1;
    }
    //插入逻辑
    if(q->next ==NULL)
    {
        //说明要进行尾插
        p->prio = q;
        q->next = p;
    }else
    {
        //中间任意位置
        p->next = q->next;
        p->prio = q;
        q->next->prio = p;
        q->next = p;
    }
    //表长变化
    L->len++;
    printf("插入成功\n");
    return 0;
}
//头删
int list_delete_head(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("删除失败\n");
        return -1;
    }
    //删除逻辑
    Node_ptr p = L->next;    //标记
    //判断要删除的结点后面是否还有结点
    if(NULL == p->next)
    {
        //说明p是链表中最后一个结点
        L->next = NULL;
    }else
    {
        //说明p不是最后一个结点
        L->next = p->next;
        p->next->prio = L;
    }
    free(p);          //释放结点
    p = NULL;
    //表长变化
    L->len--;
    printf("删除成功\n");
    return 0;
}
//任意位置删除
int list_delete_pos(Node_ptr L, int pos)
{
    //判断逻辑
    if(NULL==L ||list_empty(L) ||pos<1 || pos>L->len)
    {
        printf("删除失败\n");
        return -1;
    }
    //找到要删除的结点
    Node_ptr q = list_search_pos(L, pos);
    //删除逻辑
    if(q->next == NULL)
    {
        //说明要删除的是最后一个结点
        q->prio->next = NULL;   //将前面的结点后继指针置空
    }else
    {
        //交接工作
        q->prio->next = q->next;
        q->next->prio = q->prio;
    }
    free(q);       //释放结点
    q = NULL;
    //表长变化
    L->len--;
    printf("删除成功\n");
    return 0;
}
//按位置进行修改
int list_update_pos(Node_ptr L, int pos, datatype e)
{
    //判断逻辑
    if(NULL==L || list_empty(L) ||pos<1 || pos>L->len)
    {
        printf("修改失败\n");
        return -1;
    }
    //找到要修改的结点
    Node_ptr p = list_search_pos(L, pos);
    
    p->data = e;          //将当前数据域进行修改
    printf("修改成功\n");
    return 0;
}
//销毁双向链表
void list_destroy(Node_ptr L)
{
    //判断链表是否合法
    if(NULL == L)
    {
        return ;
    }
    //开始将所有结点释放
    while(!list_empty(L))
    {
        //不断进行头删
        list_delete_head(L);
    }
    //释放头结点
    free(L);
    L = NULL;
    printf("销毁成功\n");
}
main.c
#include"doublelinklist.h"
#include<myhead.h>
int main(int argc, const char *argv[])
{
    //调用创建链表函数
    Node_ptr L = list_create();
    if(NULL==L)
    {
        return -1;
    }
    //调用头插函数
    list_insert_head(L,'Q');
    list_insert_head(L,'W');
    list_insert_head(L,'E');
    list_insert_head(L, 'R');
    //调用遍历函数
    list_show(L);
    //调用任意位置插入函数
    list_insert_pos(L, 1, 'D');
    list_insert_pos(L, L->len+1, 'F');
    list_insert_pos(L, 3, 'K');
    list_show(L);
    //调用头删函数
    list_delete_head(L);
    list_delete_head(L);
    list_show(L);
    //调用任意位置删除函数
    list_delete_pos(L, 1);
    list_delete_pos(L, 3);
    list_show(L);
    //调用按位置修改函数
    list_update_pos(L, 1, 'M');
    list_show(L);
    //销毁双向链表
    list_destroy(L);
    L = NULL;
    list_show(L);
    
    return 0;
}
循环链表
1.循环链表:首尾相接的链表称为循环链表
特点:在循环链表中,没有一个节点的指针域为空
分类:
单向循环链表:将单链表最后一个节点的指针域指向第一个节点即可(或者指向头节点)
双向循环链表:将头节点的前驱指针指向最后一个节点,最后一个节点的后继指针指向头节点

单向循环链表节点结构体
typedef struct Node
{
    union 
    {
        int len;   //头节点数据域
        datatype data;   //普通结点数据域
    };
    //指针域
    struct Node * next;   
}Node, *Node_ptr;
循环链表的创建
注意:创建链表时,头节点指针域指向自己
//创建循环链表
Node_ptr list_create()
{
    //在堆区申请一个头结点的空间
    Node_ptr L = (Node_ptr)malloc(sizeof(Node));
    if(NULL==L)
    {
        printf("创建链表失败\n");
        return NULL;
    }
    //初始化
    L->len = 0;
    L->next = L;          //指针域指向自己
    printf("循环链表创建成功\n");
    return L;
}
全部代码
looplinklist.h
#ifndef LOOPLINKLIST_H
#define LOOPLINKLIST_H
typedef char datatype;    //数据元素类型
//定义节点类型
typedef struct Node
{
    union 
    {
        int len;   //头节点数据域
        datatype data;   //普通结点数据域
    };
    //指针域
    struct Node * next;   
}Node, *Node_ptr;
//创建循环链表
Node_ptr list_create();
//判空
int list_empty(Node_ptr L);
//尾插
int list_insert_tail(Node_ptr L, datatype e);
//遍历
void list_show(Node_ptr L);
//尾删
int list_delete_tail(Node_ptr L);
//删除头结点
Node_ptr list_head_delete(Node_ptr L);
//销毁
void list_destroy(Node_ptr L);
//删除头结点的循环链表的遍历
void list_desplay(Node_ptr H);
#endif
looplinklist.c
#include"looplinklist.h"
#include<myhead.h>
//创建循环链表
Node_ptr list_create()
{
    //在堆区申请一个头结点的空间
    Node_ptr L = (Node_ptr)malloc(sizeof(Node));
    if(NULL==L)
    {
        printf("创建链表失败\n");
        return NULL;
    }
    //初始化
    L->len = 0;
    L->next = L;          //指针域指向自己
    printf("循环链表创建成功\n");
    return L;
}
//判空 : 1表示空,0表示非空
int list_empty(Node_ptr L)
{
    if(NULL==L)
    {
        printf("链表不合法\n");
        return -1;
    }
    return L->next == L;  //判断是否为空格
}
//尾插
int list_insert_tail(Node_ptr L, datatype e)
{
    //判断逻辑
    if(NULL==L)
    {
        printf("插入失败\n");
        return -1;
    }
    //找到最后一个结点
    Node_ptr q = L;    //定义遍历指针从头结点出发
    while(q->next != L)
    {
        q = q->next;    //不断将遍历指针后移,直到移到最后一个结点位置
    }
    //申请结点将数据封装
    Node_ptr p = (Node_ptr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("结点申请失败\n");
        return -1;
    }p->data = e;     //将数据封装进结点
    p->next = NULL;    //防止野指针
    //插入逻辑
    p->next = L;  //将新节点指针域指向头结点
    q->next = p;  //将最后一个结点的指针域指向自己
    //表长变化
    L->len++;
    printf("插入成功\n");
}
//遍历
void list_show(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("遍历失败\n");
        return ;
    }
    //遍历逻辑
    printf("链表中的元素分别是:");
    Node_ptr q = L->next;  //定义遍历指针从第一个结点开始
    while(q != L)
    {
        printf("%c\t", q->data);
        q = q->next;
    }
    printf("\n");
}
//尾删
int list_delete_tail(Node_ptr L)
{
    //判断逻辑
    if(NULL==L || list_empty(L))
    {
        printf("删除失败\n");
        return -1;
    }
    //找到倒数第二个结点
    Node_ptr q = L;
    while(q->next->next != L)
    {
        q = q->next;
    }
    //删除逻辑
    free(q->next);    //释放最后一个结点
    q->next = L;     //将新的最后一个结点指针域指向头结点
    //表长变化
    L->len--;
    printf("删除成功\n");
    return 0;
}
//删除头结点
Node_ptr list_head_delete(Node_ptr L)
{
    //判断逻辑
    if(NULL==L)
    {
        printf("去头失败\n");
        return NULL;
    }
    if(list_empty(L))        //如果为空,直接释放
    {
        free(L);        //释放头结点
        return NULL;
    }
    //遍历整个链表找到最后一个结点
    Node_ptr q = L->next;    //从第一个结点出发
    while(q->next != L)
    {
        q = q->next;
    }
    //将最后一个结点的指针域指向第一个结点
    q->next = L->next;
    free(L);    //将头结点释放
    L = NULL;
    printf("去头成功\n");
    return q->next;
}
//删除头结点的循环链表的遍历
void list_desplay(Node_ptr H)
{
    //判断链表释放为空
    if(NULL==H)
    {
        printf("遍历失败\n");
        return ;
    }
    //定义遍历指针从当前结点出发
    printf("链表总的结点元素分别是:");
    Node_ptr q = H;
    do
    {
        printf("%c\t", q->data);
        q = q->next;
    }while(q!=H);
    printf("\n");
}
//销毁
void list_destroy(Node_ptr H)
{
    if(NULL==H)   //说明已经销毁了
    {
        return;
    }
    //先将其他结点释放
    while(H->next != H)
    {
        Node_ptr p = H->next;   //标记下一个结点
        H->next = p->next;   //孤立
        free(p);    //释放
        p = NULL;
    }
    //将第一个结点释放
    free(H);
    H = NULL;
    printf("销毁成功\n");
    return ;
}
main.c
#include"looplinklist.h"
#include<myhead.h>
int main(int argc, const char *argv[])
{
    //调用创建链表函数
    Node_ptr L = list_create();
    if(NULL==L)
    {
        return -1;
    }
    
    //调用尾插函数
    list_insert_tail(L, 'Q');
    list_insert_tail(L, 'W');
    list_insert_tail(L, 'E');
    list_insert_tail(L, 'R');
    //调用遍历函数
    list_show(L);
    //调用尾删函数
    list_delete_tail(L);
    list_delete_tail(L);
    list_show(L);
    //调用循环链表去头操作
    Node_ptr H = list_head_delete(L);
    L = NULL;
    //此时的H就是指向链表的头指针
    
    //调用遍历函数,遍历去头后的链表
    list_desplay(H);
    //调用销毁函数
    list_destroy(H);
    H = NULL;
    list_desplay(H);
    
    return 0;
}

                  
                  
                  
                  
      
          
                
                
                
                
              
                
                
                
                
                
              
                
                
              
            
                  
					893
					
被折叠的  条评论
		 为什么被折叠?
		 
		 
		
    
  
    
  
            


            