数据结构和算法笔记 lesson4 线性表链式存储结构 单链表

来源:互联网 发布:避雷针高度计算软件 编辑:程序博客网 时间:2024/04/29 05:42

链式存储结构

   用一组任意的存储单元存储线性表的数据元素,这组存储单元可以存在内存中未被占用的任意位置

   除了存储数据元素信息外(数据域),还要存储它的后继元素的存储地址(指针域)。这两部分信息组成数据元素称为存储映像,称为节点

单链表

每个节点中只包含一个指针域。

第一个结点的存储位置叫头指针 ,最后一个结点指针为空

头结点数据域不存储任何信息

头指针  

  是链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。

  头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)

  无论链表是否为空,头指针均不为空

  头指针是链表的必要元素

头结点 

   是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用于存放链表的长度)

   有了头结点,对在第一个元素结点前插入结点和删除第一结点起操作与其他结点的操作就统一了

   头结点不一定是链表的必要元素

C语言用结构指针来描述单链表

   typedef struct Node   {           ElemType  data ;   //数据域           struct Node *Next ;  // 指针域   } Node;

    typedef struct Node* LinkList;

  假设p是指向线性表第i个元素的指针,则该结点ai的数据域我们可以用p->data的值是一个数据元素,结点ai的指针域可以用p->next来表示,p->next的值是一个指针

单链表的读取

  必须从第一个结点开始找

  思路

  声明一个结点p指向链表第一个结点,初始化j从1开始

  当j<i 时,就遍历链表,让p的指针向后移动,不断指向一个结点,j+1;

  若到链表末尾p为空,则说明第i个元素不存在;

  否则查找成功,返回结点p的数据。

 Status GetElem(LinkList L,int i, ElemType * e) {      int j ;       LinkList p;      j =1 ;      while(p && j<i)      {            p=p->next;             ++j;      }     if(!p || j>i)      {          return 0;      }      *e=p->data;       return 1; }

时间复杂度 O(n)

单链表的插入

s->next = p->next;

p->next = s;

 思路 

    声明一个结点p指向链表头结点,初始化j从1开始;

    当j<1时,就遍历链表,让p的指针向后移动,不断指向下一个结点,累加1;

   若链表末尾p为空,则说明第i个元素不存在;

   否则查找成功,在系统中生成一个空结点S;

   将数据元素e赋值给s->data;

   单链表的插入刚才两个标准语句;

   返回成功。

 Status ListInsert (LinkList *L ,int i , ElemType e ) {    int j ;     LinkList p ,s;    p = *L;     j = 1;    while ( p && j<i)   {      p = p->next;      j ++;   }   if( !p || j>i )   {        return 0;   }    s = (LinkList)malloc (sizeof(Node));    s->data = e;       s->next = p->next;    p->next= s;   return 1; }

单链表的删除

  它的前继结点的指针绕过后继结点

  p->next = p->next->pnext

 思路

  声明结点p指向链表第一个结点,初始化 j = 1;

  当 j < 1 时, 就遍历链表,让P的指针向后移动,不断指向下一个结点,j累加1;

  若到链表末尾p为空,则说明第i个元素不存在

  否则查找成功, 将欲删除节点p->next = q->next;

  将q结点中的数据赋值给e,作为返回;

  释放q结点。

 Status ListDelete(LinkList *L . int i, ElemType * e) {    int j ;    LinkList q, s;    p = *L;     j =1;    while ( p && j<i)   {      p = p->next;      j ++;   }   if( !p || j>i )   {        return 0;   }     q=p->next;     p->next = q->next;       *e = q->data;   free(q); }

删除和插入的时间复杂度都是O(n)

  如果不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储结构是没有太大优势的。

  如果希望从第 i个位置开始,插入连续10个元素,对于顺序存储结构意味着,每一次插入都需要n-i个位置, 所以每次都是O(n)

   而单链表只需要在第一次找到第i个元素的指针,此时为O(n)接下来只是简单地通过赋值移动指针而已,时间复杂度都是O(1).

   对于插入或删除数据越频繁的操作,单链表的效率优势越明显。

单链表的整表创建

   单链表的数据可以是分散在内存各个角落的,增长可以是动态的

   对于每个链表来首,它所占用空间的大小和位置是不要预先分配划定的,可以根据系统的情况和实际的需求即时生成。

  创建单链表的过程是一个动态生成链表的过程,从"空表"的初始状态起,依次建立各元素结点并逐个插入链表

   整表创建思路:

   声明一结点p和计数器变量i;

   初始化一空链表L;

   让L的头结点的指针指向NULL,即建立一个带头结点的单链表

    循环实现后继结点的赋值和插入

 头插法

  从一个空表开始,生成新结点,读取数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到结束为止。

 新元素放在表头后的第一个位置

  先让新节点的next指向头节点之后

  然后让表头的next指向新节点

生成链表中结点的次序和输入的顺序相反

 void CreateListHead(LinkList * L ,  int n) {        LinkList p ;        int i ;         srand( time(0) );        *L = (LinkList ) malloc (sizeof(Node));        for (i = 0 ; i <n ; i++)        {             p = (LinkList)malloc (sizeof(Node));  //生成新节点             p->data = rand()%100+1;             p->next = p ;             (*L)->next = p;        } }      

   尾插法建立单链表

 void CreateListTail (LinkList *L ,int n) {        LinkList p ,r ;         int i ;        srand(time(0));         *L = (LinkList)malloc (sizeof(Node));          r  =  *L ;          for( i = 0 ; i< n ;i++)          {              p =(Node*) malloc(sizeof(Node));              p->data = rand()%100+1;              r->next = p;                 r = p;          }          r->next = NULL;} 

单链表的整表删除

  思路:

    声明结点 p 和 q;

    将第一个结点赋值给p , 下一个结点赋值给q;

    循环执行释放p和将q赋值给p的操作

    Satus ClearList (LinkList *L)   {         LinkList p ,q ;         p  =  (*L)->next ;          while (p)        {           q =  p->next;           free (p);           p = q;        }           (*L)->next = NULL;           return 1;   }

单链表与顺序存储结构

分配方式 :

    顺序存储结构用一段连续的存储单元依次存储线性表的数据元素

    单链表采用链式存储结构,用一组任意的存储单元存储线性表的数据元素


时间性能:

  查找:

     顺序存储结构 O(1)

     单链表O(n)

  插入和删除

     顺序存储结构需要移动平均一半的元素,时间为 O(n)

     单链表在计算出某个位置的指针后,插入和删除时间仅为O(1)


空间性能:

    顺序存储结构需要预分配存储空间,分大了, 容易造成空间浪费,分小了, 容易发生溢出

    单链表不需要分配存储空间,只要 有就可以分配,元素个数也不受限制。


频繁查找,很少进行插入和删除 ,使用顺序存储结构

频繁插删,很少查找,使用链式存储


游戏开发 ,注册个人信息,除了注册时插入数据,绝大多数情况都是读取,所以应该考虑用顺序存储结构

游戏中的玩家的武器或者装备列表,随着玩家的游戏过程中,可能会随时增加或者删除,用单链表


当线性表中的元素个数变化比较大或者根本不知道有多大时,最好用单链表结构,不需要考虑存储空间的大小问题

事先知道线性表的大致长度, 比如一年12个月,一周7天,顺序存储结构效率会更高 

0 0