双向动态链表

//双向动态链表

#include <stdio.h>
#include <stdlib.h>

/** 链表结点中存储的数据的类型:LNodeT **/
typedef int LNodeT;

/** 链表结点的数据类型的预先声明 **/
struct linklist;

/** 单向链表结点的数据类型,两部分:data为数据、next为下一结点的地址(链接结构),pre为上一个结点的地址 **/
struct linklist {
    LNodeT data;
    struct linklist *next;
    struct linklist *pre;/** 双向链表的结点还要多一个向前的指针**/
};

/** 单向链表结点中存储的数据的类型:LNodeT **/
typedef struct linklist LinkT;

/** 动态申请存储空间,创建链表的结点 **/
LinkT* creat_node(LNodeT a)//已测试
{
 //开辟空间
    LinkT *p=(LinkT*)malloc(sizeof(LinkT));
 if(!p)
 {
  printf("在开辟空间时出现错误!");
  exit(0);
 }
 //数据域初绐化
 p->data=a;
 //指针域初绐化
    p->next=NULL;
 p->pre=NULL;

    return p;
}

/** 释放链表结点的存储空间 **/
void erase_node(LinkT *p)//已测试
{
    free(p);
}

#define erase_node(p) (free(p))//已测试

/** 在链表头部插入一个结点,返回头结点指针 **/
LinkT* insert_first1(LinkT *head, LNodeT a)
{
    LinkT *p=creat_node(a);
 LinkT* insert_first(LinkT *head, LinkT *p);
    return insert_first(head,p);
}

/** 在链表头部插入一个结点,返回头结点指针 **/
LinkT* insert_first(LinkT *head, LinkT *p)//已测试
{
    if(!head)
 {
  //此时p的指针,head只要旨向p就可以了
  p->next=p;
  p->pre=p;
  head=p;
 }
 else
 {
  //p的前指针指向head指向结点的前一个结点
  p->pre=head->pre;
  //p的后指针指向head指向的结点
  p->next=head;
  //head指向结点的前一个结点的后指针指向p
  head->pre->next=p;
  //head指向结点的前指针指向p
  head->pre=p;
  //head指向新开辟的这个结点
  head=p;
 }
    return head;
}

/** 在链表尾部插入一个结点 **/
LinkT* insert_tail(LinkT *head, LinkT *p)//已测试
{
 if(!head) return insert_first(head, p);
 //在尾部插入相当于在第一个位置插入后head后移一个位置
   head=insert_first(head, p);
   return head->next;
}

/** 依照结点的数据内容,在链表中插入一个结点 **/
LinkT* insert_by_data(LinkT *head,LNodeT node)//已测试
{
 //首先要给这个等插入的数据开辟一个空间
 LinkT *p=head,*link=creat_node(node);
 //针对链表空否进行插入
 if(!p)//如果这个链表是一个空的,则这个结点要插头部
 { 
  head=insert_first(head,link);
  return head;
 }
 else//如果这个链表不为空,但是这个链表的第一项就符合要求,则要插在首结点后面
 { 
  //针对第一个是不是要插入的位置进行分析
  if(p->data==node)//是
  {
   head=insert_first(head,link);
   return head;
  }
  else//不是
  {
   //否则的话,要先遍历这个链表,找到要插入的位置
   p=head->next;
   while(p!=head && p->data!=node)//如果这个链表没有这么样子的结点,或者是找到了插入的位置,则退出循环
   {
   
    p=p->next;
   }
   //插入这个结点
   insert_first(p,link);
  }
  return head;
 }
}

/** 依照结点的顺序,在链表中插入一个结点 **/
LinkT* insert_by_sub(LinkT *head,int n,LinkT *link)//已测试
{
 int i;
 LinkT *p;
 if(n<=0)
 {
  printf("插入位置有误!/n");
  return head;
 }
 else
 {
  //对插入位置是不是第一个做出判断
  if(n==1)
  {
   insert_first(head,link);
  }
  else
  {
   i=1;
   p=head->next;
   //找到插入位置,并且用p指向它,如果没有找到的话就插在未尾
   while(p!=head && i!=n)
   {
    p=p->next;
    i++;
   }
   insert_first(p->pre,link);
  }
  return head;
 }
}

/** 在链表头部删除一个结点 **/
LinkT* delete_first(LinkT *head)//已测试
{
 LinkT *link;
 //链表有可能为空
 if(!head) return NULL;
 //如果这个链表是一个只有一个结点的链表,那么处理如下
 if(head==head->next)
 {
  erase_node(head);
  return NULL;
 }
 /*其它的情况处理如下*/
 //记录下头结点的下一个结点
    link=head->next;
 //head指向前一个结点的后指针指向link
 head->pre->next=link;
 //link的前指针指向head的前一个结点
 link->pre=head->pre;
 //释放head指向的节点
 erase_node(head);
 //head指向新链表的第一个结点
 head=link;
    return head;
}

/** 在链表尾部删除一个结点 **/
LinkT* delete_tail(LinkT *head)//已测试
{
 /*如果这个链表是一个空的链表的话*/
 if(!head) return NULL;
 /*不为空的话,处理如下*/
    //在尾啊del一个结点相当于把原链表head->pre当做头结点del第一个结点
 head=delete_first(head->pre);
 return head;
}

/** 依照结点的数据内容,在链表中删除一个结点 **/
LinkT* delete_by_data(LinkT *head,LNodeT node)//已测试
{
 //第一步关于head的后移问题,即 head指向的就是要删除的
 LinkT *p=head;
 if(!head) return NULL;
 while(head->data==node)
 {
  head=delete_first(head);
 }
 //删除完成后head可能变成一个空的指针
 if(!head) return head;

 //第二步关于删除和node相同的结点
 p=head->next;
 while(p!=head)
 {
  if(p->data==node)
  {
   //在这个过程式中p已经后移
   p=delete_first(p);
  }
  else
  {
   p=p->next;
  }
 }

 return head;
}

/** 依照结点的顺序,在链表中删除一个结点 **/
LinkT* delete_by_sub(LinkT *head,int n)//已测试
{
 int i;
 LinkT *p;
 if(n<=0)
 {
  printf("删除位置有误!/n");
  return head;
 }
 else
 {
  //对删除位置是不是第一个做出判断
  if(n==1)
  {
   head=delete_first(head);
  }
  else
  {
   i=1;
   p=head->next;
   //找到删除位置,并且用p指向它
   while(p!=head && i!=n)
   {
    p=p->next;
    i++;
   }
   if(p!=head || i==n)//i==n是为了看看最后一个是不是要删的
   {
    p=p->pre;
    delete_first(p);
   }
  }
  return head;
 }
}

/** 取链表的长度(结点数) **/
size_t linklen(LinkT *head)//已测试
{
 size_t num=0;
 LinkT *p;
    if(!head) return 0;
 p=head->next;
 num=1;
 while(p!=head)
 {
  num++;
  p=p->next;
 }
 return num;
}

/** 依照结点的数据内容,遍历链表 **/
LinkT* travel_by_data(LinkT *head,LNodeT node,void (*travel)(LNodeT a))//已测试
{
 //处理开始部分
 LinkT *p=head;
 if(p->data==node)
 {
  (*travel)(head->data);
 }
 //处理其它部分
 p=head->next;
 while(p!=head)
 {
  if(p->data==node)
  {
   (*travel)(p->data);
  }
  p=p->next;
 }
 return head;
}

/** 依照结点的顺序,遍历链表 **/
LinkT* travel_by_seq(LinkT* head,void (*travel)(LNodeT a))//已测试
{
 LinkT *p;
 if(!head) return NULL;
 
 (*travel)(head->data);
 p=head->next;
 while(p!=head)
 {
  (*travel)(p->data);
  p=p->next;
 }
 return head;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值