【转+】线性表(顺序表和单链表)

转自大神 权侵删    https://www.cnblogs.com/lichuankai/p/8627665.html

一、定义:是n个类型相同的数据元素的有限序列。

  线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。

  在较复杂的线性表中,一个数据元素可以由若干个数据项组成。

三、线性表的顺序存储结构

  1、顺序存储定义:线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

  2、顺序存储方式

  描述顺序存储结构需要三个属性:

    a、存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置。

    b、线性表的最大存储容量:数组长度MaxSize.

    c、线性表的当前长度:length。

#define MAXSIZE  100
typedef struct{

      ElementType  element[MAXSIZE];//线性表占用的数组空间
      int last;//记录线性表中最后一个元素的下标值,空表置为-1

}List;//别忘分号
一个这个 就是表示的一个线性表(以数组的形式顺序存储)了 想用的话就跟数组没啥区别

(last表示的是下标值,所以表示顺序表中序号为i的元素: l->elem[i-1]   顺序表的长度:l->last+1; )

 

  3、数据长度与线性表长度的区别

    a、数组长度:数组的长度是存放线性表的存储空间的长度存储分配后这个量是一般是不变的。

    b、线性表长度:线性表长度是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的。

  在任意时刻,线性表的长度应该小于等于数组的长度。

  4、地址计算方法

  存储器中的每个存储单元都有自己的编号,这个编号称为地址。

  假设占用的是c个存储单元,那么线性表中第i+1个数据元素的存储位置和第i个数据元素的存储关系满足下列关系(LOC表示获得存储位置的函数)。

  所以对于第i个数据元素a1的存储位置可以由a1推算得出:

    

四、顺序存储结构的初始化 插入 删除 查找

【都是不带头结点的 】滚麻痹数组要个鬼的头结点

初始化:

List MakeEmpty(){
     List p;
     p=(List)malloc(sizeof(List));
     p->last=-1;
     
     return p;//不用指针也阔以的
}

 

查找:

1>按序号查找:直接L->element[i-1]啊;

2>按内容查找

int  SearchPosition(List L,ElementType e){
     
       int i=0;
       while(i<=L->last&&L->element[i]!=e){
           i++;
       }
       if(i<=L->last){
             return i+1;//返回其在序列的序号 最好别省事返回个在数组的下标哈
       }
       else{
           return -1;
       }
}

 

插入:

#define true 1;
#define error 0;
int
Insert(List *L,int i, ElementType e){ //在i之前插入 int k; if(L->last>L->elem[MAXSIZE]-1){ printf("表满无法插入");
return (false); }
else{ if(i<0||i>L->last+1){ printf("插入位置不对");
return (false); }
else{ for(k=L->last;k>=i;k--){ L->elem[k+1]=L->elem[k]; } L->elem[i-1]=e; L->last++;//!! }
return (true); } }
//元素平均移动次数:n/2 【在表头插入 移动n个数据 表尾插入 一个不动】

 

删除:

int Delete(List L,int i,Element *p){//删除第i个元素
      int k;
     if(L->last==-1){
         printf("空表");
         return (false);
     }
     else{
        if(i<1||i>L->last+1){
            printf("元素位置不对");
            return (false);
        }
        else{
            *p=L->element[i-1];
            for(k=i-1;k<L->last;k++){
                L->element[k]=L->element[k+1];
            }
            L->last--;
            return (true);
        }
     }
}
//平均移动次数 n-1/2 【删除表尾不需要移动元素 删除表头移动n-1次】

 

  4、线性表顺序存储结构的优缺点

    优点:

      a、无须为表示表中元素之间的逻辑关系而增加额外的存储空间。

      b、可以快速地存取表中任一位置的元素。

    缺点:

      a、插入和删除操作需要移动大量元素。

      b、当线性表长度变化较大时,难以确定存储空间的容量。

      c、造成存储空间的“碎片”。

 

五、线性表的链式存储结构(动态存储方式)( 就是 单链表)

  1、线性表链式存储结构定义

    为了表示每个数据元素a与其直接后继数据元素之间的逻辑关系,对数据元素a来说,除了存储其本身的信息之外,还需存储一个指示其后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成数据元素a的存储映像,称为结点。

n个结点(a的存储映像)链结成一个链表,即为线性表的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表

 把链表中的第一个结点的存储位置叫做头指针。规定,线性链表的最后一个结点指针为“空”。 习惯上用头指针代替单链表

有时,为了方便对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息。

  

  2、头指针与头结点的异同

  头指针:

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

    b、头指针具有标识作用,所以常用头指针冠以链表的名字。

    c、无论链表是否为空,头指针均不为空。头指针是链表的必要元素。

  头结点:

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

    b、有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就同一了。

    c、头结点不一定是链表必须元素

单链表的存储结构:

typedef struct Node{

     ElementType data;
     struct Node *next;
}Node, *ListNode;

//这个表示的是一整个单链表中的一个元素 由一堆这个构成一个单链表

 

初始化单链表:

ListNode InitList (ListNode L){

       L=(ListNode)malloc(sizeof(Node));

       L->next=NULL;//带头结点
       L=NULL;//不带头结点
       return L;
//L是单链表的头指针,它指向表中的第一个节点(对于带头结点的单链表,则指向单链表的头结点)(头指针是一定有的 头结点不一定)
}

 

创建单链表://这是用链表示线性表  而不是数组  typedef那只是一个链表元素

1>头插法:

带头结点 不带头结点

2>尾插法:带头结点 不带头结点

 

查找

1>按序号

ListNode Get(ListNode L,int i){//查找第i个节点

      int j=0;
      ListNode p;
      p=L;
      if(i<=0) return NULL;
      while(p->next!=NULL&&j<i){//下标是i-1
         p=p->next;
         j++;
      }
      if(i==j){ return p;}
      else{
         return NULL;
      }
}时间复杂度为O(n)

 

2>按值查找:

ListNode Get(ListNode L,ElementType e){//查找

      ListNode p;
      p=L->next;
      while(p!=NULL){
        if(p->data==e){
            return p;
        }
        else{
            p=p->next;
        }
      }
}//时间复杂度为O(n)

 

求单链表的长度:

int Length(ListNode L){
     int i=0;
     ListNode p;
     p=L->next;
     while(p!=NULL){
         i++;
         p=p->next;
     }
     return i;
}
//时间复杂度为O(n)

 

单链表的插入:(头结点)

 

void Insert(ListNode L,int i,ElementType e){//在第i个元素之前插入节点

     ListNode p,q;
     p=L;
     int k=0;
     while(p!=NULL&&k<i-1){
        p=p->next;
        k++;
     }
     if(p==NULL){ printf("数据错误超出")};

     q=(ListNode)malloc(sizeof(Node));
     q->data=e;
     q->next=p->next;
     p->next=q;
    // return L;

}

 

删除第i个元素:

void Delete(ListNode L,int i,ElementType *e){//将线性表第i个元素删除 并付给e
    
          ListNode p,q,temp;
          int k=0;
          p=L;
          while(p!=NULL&&k<i){
              p=p->next;
              k++;
          }
          if(p==NULL){
             printf("位置不合理");
          }
          temp=p->next;
          p->next=temp->next;
          
          *e=temp->data;//加不加*就是值传递和引用传递的区别
         
          free(temp);//因为创建链表各个元素的时候是用malloc来的 所以不用时要清除掉空间

}

 

删除整个表:

void Delete(ListNode L){

    ListNode p,temp;
    p=L->next;
    while(p->next!=NULL){
        temp=p;
        p=p->next;
        free(temp);
    }
    L->next=NULL;
}

 

 

八、单链表结构与顺序存储结构优缺点

  1、单链表结构和顺序存储结构对比:

    a、存储分配方式:

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

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

    b、时间性能:

    查找:顺序存储结构O(1),单链表O(n)。

    插入和删除:顺序存储结构需要平均移动表长一半的元素,时间为O(n)。单链表在线出某位置的指针后,插入和删除时间仅为O(1)。

c、空间性能:顺序存储结构需要预分配存储空间,分大了,浪费,分小了易发生上溢。单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

posted @ 2019-03-11 17:50  像走了一光年  阅读(441)  评论(0编辑  收藏  举报