STRUCT DATA

来源:互联网 发布:白石洲新塘网络 编辑:程序博客网 时间:2024/06/05 15:01

1.1.1       链表

链接存储方法
     链接方式存储的线性表简称为链表(Linked List)。
     链表的具体存储表示为:
  ①用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
  ②链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link)
注意:
  链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。

2、链表的结点结构
  ┌──┬──┐
  │data│next│
  └──┴──┘
     
 data--存放结点值的数据域
       next--存放结点的直接后继的地址(位置)的指针域(链域)
注意:
    链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
     每个结点只有一个链域的链表称为单链表(Single Linked List)。
【例】线性表(batcateatfathatjatlatmat)的单链表示如示意图

1.1.2       双链表

1、双向链表(Double Linked List
     双(向)链表中有两条方向不同的链,即每个结点中除next域存放后继结点地址外,还增加一个指向其直接前趋的指针域prior
 
注意:
     双链表由头指针head惟一确定的。
     带头结点的双链表的某些运算变得方便。
     将头结点和尾结点链接起来,为双(向)循环链表。

2、双向链表的结点结构和形式描述
结点结构(见上图a)
      
形式描述
    typedef struct dlistnode{
         DataType data;
         struct dlistnode *prior,*next;
      }DListNode;
    typedef DListNode *DLinkList;
    DLinkList head;

3
、双向链表的前插和删除本结点操作
     由于双链表的对称性,在双链表能能方便地完成各种插入、删除操作。
双链表的前插操作
    
    void DInsertBefore(DListNode *p,DataType x)
      {//
在带头结点的双链表中,将值为x的新结点插入*p之前,设p≠NULL
        DListNode *s=malloc(sizeof(DListNode));//①
        s->data=x;//②
        s->prior=p->prior;//③
        s->next=p;//④
        p->prior->next=s;//⑤
        p->prior=s;//⑥
       }
双链表上删除结点*p自身的操作
    
    void DDeleteNode(DListNode *p)
      {//
在带头结点的双链表中,删除结点*p,设*p为非终端结点
          p->prior->next=p->next;//①
          p->next->prior=p->prior;//②
          free(p);//③
      }
注意:
     与单链表上的插入和删除操作不同的是,在双链表中插入和删除必须同时修改两个方向上的指针。
     上述两个算法的时间复杂度均为O(1)

1.1.3       循环链表(Circular Linked List

    循环链表是一种首尾相接的链表。
1、循环链表
1)单循环链表——在单链表中,将终端结点的指针域NULL改为指向表头结点或开始结点即可。
2)多重链的循环链表——将表中结点链在多个环上。

2、带头结点的单循环链表
注意:
     判断空链表的条件是head==head->next;
3
、仅设尾指针的单循环链表
     用尾指针rear表示的单循环链表对开始结点a1和终端结点an查找时间都是O(1)。而表的操作常常是在表的首尾位置上进行,因此,实用中多采用尾指针表示单循环链表。带尾指针的单循环链表可见下图。
注意:
     判断空链表的条件为rear==rear->next;
4
、循环链表的特点
     循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。
【例】在链表上实现将两个线性表(a1a2an)和(b1b2bm)连接成一个线性表(a1anb1…bm)的运算。

     分析:若在单链表或头指针表示的单循环表上做这种链接操作,都需要遍历第一个链表,找到结点an,然后将结点b1链到an的后面,其执行时间是O(n)。若在尾指针表示的单循环链表上实现,则只需修改指针,无须遍历,其执行时间是O(1)

      
   
 相应的算法如下:
      LinkList Connect(LinkList A,LinkList B)
       {//
假设AB为非空循环链表的尾指针
          LinkList p=A->next;//①保存A表的头结点位置
          A->next=B->next->next;//②B表的开始结点链接到A表尾
          free(B->next);//③释放B表的头结点
          B->next=p;//④
          return B;//
返回新循环链表的尾指针
    }
注意:
     循环链表中没有NULL指针。涉及遍历操作时,其终止条件就不再是像非循环链表那样判别pp-next是否为空,而是判别它们是否等于某一指定指针,如头指针或尾指针等。
    在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。

Hash


3、头指针head和终端结点指针域的表示
     单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。
注意:
     链表由头指针唯一确定,单链表可以用头指针的名字来命名。
【例】头指针名是head的链表可称为表head
  终端结点无后继,故终端结点的指针域为空,即NULL

4、单链表的一般图示法
     由于我们常常只注重结点间的逻辑顺序,不关心每个结点的实际位置,可以用箭头来表示链域中的指针,线性表(batcatfathatjatlatmat)的单链表就可以表示为下图形式。
5、单链表类型描述
  typedef char DataType; //假设结点的数据域类型为字符
  typedef struct node{   //结点类型定义
       DataType data;    //结点的数据域
       struct node *next;//结点的指针域
     }ListNode;
  typedef ListNode *LinkList;
  ListNode *p;
  LinkList head;
 
注意:
     ①LinkListListNode *是不同名字的同一个指针类型(命名的不同是为了概念上更明确)
     ②LinkList类型的指针变量head表示它是单链表的头指针
     ③ListNode *类型的指针变量p表示它是指向某一结点的指针

6、指针变量和结点变量

┌────┬────────────┬─────────────┐
        指针变量        结点变量    
├────┼────────────┼─────────────┤
│ 
定义  │在变量说明部分显式定义  │在程序执行时,通过标准    │
│        │                        │
函数malloc生成            │
├────┼────────────┼─────────────┤
│ 
取值  │ 非空时,存放某类型结点实际存放结点各域内容      │
│        │
的地址                  │                          │
├────┼────────────┼─────────────┤
操作方式通过指针变量名访问     │通过指针生成、访问和释放
└────┴────────────┴─────────────┘
  
生成结点变量的标准函数
     p=( ListNode *)malloc(sizeof(ListNode))
//函数malloc分配一个类型为ListNode的结点变量的空间,并将其首地址放入指针变量p
释放结点变量空间的标准函数
   
 free(p)//释放p所指的结点变量空间
结点分量的访问
     利用结点变量的名字*p访问结点分量
 方法一:(*p).data(*p).next
 
方法二:p-datap-next
指针变量p和结点变量*p的关系
     指针变量p的值——结点地址
     结点变量*p的值——结点内容
     (*p).data的值——p指针所指结点的data域的值
     (*p).next的值——*p后继结点的地址
  *((*p).next)——*p后继结点
注意:
    若指针变量p的值为空(NULL),则它不指向任何结点。此时,若通过*p来访问结点就意味着访问一个不存在的变量,从而引起程序的错误。
    有关指针类型的意义和说明方式的详细解释,【参考C语言的有关资料】。

原创粉丝点击