单链表

1、单链表类型描述
  typedef char DataType; //假设结点的数据域类型为字符
  typedef struct node{   //结点类型定义
       DataType data;    //结点的数据域
       struct node *next;//表示定义一个指向struct  NODE类型的指针
     }ListNode;
  typedef ListNode *    LinkList;
  ListNode *p;
  LinkList head;
  注意:
     ①LinkList和ListNode *是不同名字的同一个指针类型(命名的不同是为了概念上更明确)
     ②LinkList类型的指针变量head表示它是单链表的头指针
     ③ListNode *类型的指针变量p表示它是指向某一结点的指针

2、指针变量和结点变量
  
①生成结点变量的标准函数
     p=( ListNode *)malloc(sizeof(ListNode));
//函数malloc分配一个类型为ListNode的结点变量的空间,并将其首地址放入指针变量p中
②释放结点变量空间的标准函数
      free(p);//释放p所指的结点变量空间
结点分量的访问
    
利用结点变量的名字*p访问结点分量
 方法一:(*p).data和(*p).next
 方法二:p-﹥data和p-﹥next
④指针变量p和结点变量*p的关系
 
    指针变量p的值——结点地址
     结点变量*p的值——结点内容
     (*p).data的值——p指针所指结点的data域的值
     (*p).next的值——*p后继结点的地址
  *((*p).next)——*p后继结点

3、建立单链表
     
假设线性表中结点的数据类型是字符,我们逐个输入这些字符型的结点,并以换行符'\n'为输入条件结束标志符。动态地建立单链表的常用方法有如下两种:

(1) 头插法建表
① 算法思路
     从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。

注意:
     该方法生成的链表的结点次序与输入顺序相反。

② 具体算法实现
    LinkList CreatListF(void)
      {//返回单链表的头指针
          char ch;
          LinkList   head;//头指针
          ListNode *s;  //工作指针
          head=NULL;    //链表开始为空
          ch=getchar(); //读入第1个字符
          while(ch!='\n'){
              s=(ListNode *)malloc(sizeof(ListNode));//生成新结点
              s->data=ch;   //将读入的数据放入新结点的数据域中
              s->next=head;
              head=s;
              ch=getchar();  //读入下一字符
            }
          return head;
       }
(2) 尾插法建表
① 算法思路
     
从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表尾上,直到读入结束标志为止。

注意:
    ⒈采用尾插法建表,生成的链表中结点的次序和输入顺序一致
    ⒉必须增加一个尾指针r,使其始终指向当前链表的尾结点

② 具体算法实现
   
LinkList CreatListR(void)
      {//返回单链表的头指针
          char ch;
          LinkList head;//头指针
          ListNode *s,*r;  //工作指针
          head=NULL;    //链表开始为空
          r=NULL;//尾指针初值为空
          ch=getchar(); //读入第1个字符
          while(ch!='\n'){
              s=(ListNode *)malloc(sizeof(ListNode));//生成新结点
              s->data=ch;   //将读入的数据放入新结点的数据域中
           if (head!=NULL)
               head=s;//新结点插入空表
           else
               r->next=s;//将新结点插到*r之后
              r=s;//尾指针指向新表尾
           ch=getchar();  //读入下一字符
         }//endwhile
        if (r!=NULL)
             r->next=NULL;//对于非空表,将尾结点指针域置空head=s;
         return head;
    }

注意:
  
⒈开始结点插入的特殊处理
       由于开始结点的位置是存放在头指针(指针变量)中,而其余结点的位置是在其前趋结点的指针域中,插入开始结点时要将头指针指向开始结点。
   ⒉空表和非空表的不同处理
      若读入的第一个字符就是结束标志符,则链表head是空表,尾指针r亦为空,结点*r不存在;否则链表head非空,最后一个尾结点*r是终端结点,应将其指针域置空。

(3) 尾插法建带头结点的单链表
①头结点及作用
    
头结点是在链表的开始结点之前附加一个结点。它具有两个优点:
    ⒈由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作就和在表的其它位置上操作一致,无须进行特殊处理;
    ⒉无论链表是否为空,其头指针都是指向头结点的非空指针(空表中头结点的指针域空),因此空表和非空表的处理也就统一了。

②带头结点的单链表

注意:
  
头结点数据域的阴影表示该部分不存储信息。在有的应用中可用于存放表长等附加信息。

③尾插法建带头结点链表算法
 
LinkList CreatListR1(void)
      {//用尾插法建立带头结点的单链表
          char ch;
          LinkList head=(ListNode *)malloc(sizeof(ListNode));//生成头结点
          ListNode *s,*r;  //工作指针
          r=head;    // 尾指针初值也指向头结点
          while((ch=getchar())!='\n'){
              s=(ListNode *)malloc(sizeof(ListNode));//生成新结点
              s->data=ch;   //将读入的数据放入新结点的数据域中
              r->next=s;
              r=s;
            }
          r->next=NULL;//终端结点的指针域置空,或空表的头结点指针域置空
          return head;
       }
注意:
     
上述算法里,动态申请新结点空间时未加错误处理,这对申请空间极少的程序而言不会出问题。但在实用程序里,尤其是对空间需求较大的程序,凡是涉及动态申请空间,一定要加入错误处理以防系统无空间可供分配。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值