数据结构_线性表的顺序表示和链式表示

/********************************************************************************************************************/

声明:

  (1)*.h文件是代码声明, *.cpp文件是代码实现;

  (2)一般头文件的内容有: ①类型声明; ②函数声明; ③枚举; ④常量; ⑤宏

  (3)以下说明是为了方便代码文件的管理而设定的一些规则, 以后代码都会按照此规则编写:

    1)Pubuse.h 是几乎所有实验中都涉及到的, 包括一些常量定义, 系统函数原型声明和类型, Status重定义, 结果状态代码等;

    2)数据结构定义: 以Def.h为文件名;

    3)基本操作和算法: 以Algo.h为文件名;

    4)调用基本操作的主程序: 以Use.cpp为文件名;

    以上四个文件完成一个程序. 为了方便用户,再特地写一个cpp_1.cpp, 为单文件可运行程序.

/*-----------------------------此声明借鉴CSDN上"Rain-晴天"博主的规则,特此申明-------------------------------------/

/*********************************************************************************************************************/

<一>线性表的顺序表示和实现

1) 定义

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素. 特点是: 以元素在计算机内的"物理地址相邻"来表示线性表中数据元素之间的逻辑关系, 即在逻辑关系上相邻的两个元素在物理位置上也相邻. 由此, 只要确定了存储线性表的起始位置, 线性表中任一数据元素都可随机存取, 所以线性表的顺序存储结构是一种随机存取的存储结构.

然而从另一方面来说, 顺序线性表的特点也铸成了顺序存储结构的弱点: 在作插入和删除操作时, 需要移动大量元素.

2) 实现代码

/*    Pubuse.h*/
  //    几乎所有实验中都涉及到的,包含一些常量定义,系统函数原型声明和类型,重定义(typedef)和结果代码等
  //==================================系统函数原型声明===================================
 #include<string>  
 #include<ctype.h>  
 #include<malloc.h>
 #include<limits.h>  
 #include <stdio.h>
 #include <stdlib.h>
 #include<io.h>
 #include<iostream>
 #include<math.h> 
 #include<process.h>
 using namespace std;

 //==================================函数结果状态代码=================================== 
 #define TRUE             1  
 #define FALSE            0  
 #define OK               1  
 #define ERROR            0  
 #define INFEASIBLE      -1  
 //#define OVERFLOW      -2 
 //    因为在math. h 中已定义OVERFLOW 的值为3,故去掉此行
 
 //================================类型重定义代码========================================
 typedef int Status; 
 //    Status 是函数的类型,其值是函数结果状态代码,如OK 等
 typedef int Boolean;
 //    Boolean 是布尔类型,其值是TRUE 或FALSE  

 /*    Def.h*/
  //    数据结构定义
  //=========================数据结构定义=====================================================
  #define LIST_INIT_SIZE           100         //线性表存储空间的初始分配量
  #define LISTINCREMENT            10          //线性表存储空间的分配增量
  
   typedef  struct Student    //定义一个学生的数据类型
  {
      char name[20];    //名字
      char num[20];      //学号
      float score;          //成绩
  }ElemType;                  //定义student数据类型的别名为ElemType      

  typedef struct Lnode   //定义一个线性表结构
  {
      ElemType *elem;          
         //    其中ElemType表示任意类型的变量,在前面已经定义了student类型的结构体变量,惯常定义它的别名为ElemType
 
     int length;             
         //    当前长度
 
     int listsize;          
         //    当前分配的存储容量(以sizeof(elemtype)为单位
 }SqList;

 /*    Algo.h*/
       //基本操作和算法
   //=================================基本操作和算法==============================
   Status InitList(SqList &L)        
   //    构造一个空的线性表L
   {
       L.elem=(ElemType *)malloc(LIST_INIT_SIZE *sizeof(ElemType));
       if(!L.elem)             //存储分配失败
           exit(OVERFLOW);
      L.length=0;        
          //    空表长度为0
  
      L.listsize=LIST_INIT_SIZE;   
          //    初始存储容量
      return OK;
  }
  
  Status ListDestroy(SqList &L)      
  //    销毁线性表
  {
      if(L.elem)
          free(L.elem);
      return OK;
  }
  
  Status ClearList(SqList &L)
  //    清空线性表
  {
          L.length=0;
          return true;
  }
  
  Status EmptyList(SqList L)
  //    检验线性表是否为空
  {
          if(L.length==0)
              return true;
          return false;
  }
  
  Status ListInsert_Sq(SqList &L,int i,ElemType e)     
  //    在顺序表L中第i个位置前插入新元素e
  {
      //i的合法值为1<=i<=ListLength_Sq(L)+1
  
      ElemType *newbase,*p,*q;
      if(i<1||i>L.length+1)
          return ERROR;                      //i值不合法
      if(L.length>=L.listsize)                   //当前存储空间已满,增加分配
      {
          newbase=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT) *sizeof(ElemType));
          if(!newbase)                          //存储分配失败
              exit(OVERFLOW);
          L.elem=newbase;                  //新基址
          L.listsize+=LISTINCREMENT;    //增加存储容量
      }
      q=&(L.elem[i-1]);             
          //    q为插入位置
      for(p=&(L.elem[L.length-1]);p>=q;--p)      
          //    插入位置及之后的元素后移
      {
          *(p+1)=*p;
      }
      *q=e;                    //插入e
      ++L.length;           //表长+1
      return OK;
  }
  
  Status ListDelete_Sq(SqList &L,int i,ElemType e)            
  //    删除第i个元素并用e返回其值
  {
      //    i的合法值为1<=i<=ListLength_Sq(L)
      ElemType *p,*q;
      if((i<1)||(i>L.length))       //i值不合法
          return ERROR;
      p=&(L.elem[i-1]);      
          //    p为被删元素的位置
      e=*p;                     
          //    被删元素的值赋给e
      q=L.elem+L.length-1;       
          //    表尾元素的位置
      for(++p;p<=q;++p)         
          //    被删元素之后的元素前移
      {
          *(p-1)=*p;
      }
      --L.length;                      //表长-1
      return OK;
  }
  
  Status GetElem_Sq(SqList L,int i,ElemType e)
  {
      if(!L.length)
          return ERROR;
      e=L.elem[i-1];
      return OK;
  }
  
  int compare(ElemType e1,ElemType e)       
  //    比较条件:两元素相同则返回真
 {
     if(strcmp(e1.name,e.name)==0 && strcmp(e1.num ,e.num)==0 && (e1.score ==e.score ))
         return 1;
     else
         return 0;
 }
 int LocateElem_Sq(SqList L,ElemType e)            
 //    查找第一个值与e满足聪明compare()元素的位序
 {
     int i;
     ElemType *p;
     i=1;    
         //    i的初值为第一个元素的位序
     p=L.elem;     
         //    p的初值为第一个元素的存储位置
     while(i<=L.length&&!(*compare)(*p++,e))
         ++i;
     if(i<=L.length)
     {
         cout<<"该元素的位序为:"<<i<<endl;
         return i;
     }
     else
         return 0;
 }
 
 Status ListTraver(SqList L)    
 //    遍历表中所有元素
 {
     for(int i=0;i<L.length;i++)
     {
         cout<<"姓名:"<<L.elem[i].name<<"\t学号:"<<L.elem[i].num<<"\t成绩:"<<L.elem[i].score<<endl;
     }
     cout<<"该表长为:"<<L.length<<endl;
     return OK;
 }

/*    Use.cpp*/
       //调用基本操作的主程序
   //=====================线性表的顺序存储===========================
   
   #include"Pubuse.h"
   #include"Def.h"
   #include"Algo.h"
   
  
  //=====================函数原型声明=============================
  Status InitList(SqList &L);
      //构造一个空的顺序表
  Status ListDestroy(SqList &L);
  Status ListInsert_Sq(SqList &L,int i,ElemType e);
  Status ListDelete_Sq(SqList &L,int i,ElemType e);
  Status GetElem_Sq(SqList L,int i,ElemType e);
  int compare(ElemType e1,ElemType e);
  int LocateElem_Sq(SqList L,ElemType e);
  Status ListTraver(SqList L);
  void print();
  
  //=====================主函数==============================
  int main(void)
  {
      SqList L;    //定义一个线性表L
      ElemType p[1],e;
      char ch;
      InitList(L);  //构造一个空链表
      print();
      cin>>ch;
      while (ch!='6')
      {
          switch(ch)
          {
          case('1'):  //查找
              {
                  cout<<"请输入要查找的元素的信息:"<<endl;
                  cout<<"姓名\t"<<"学号\t"<<"成绩\t"<<endl;
                  cin>>e.name>>e.num>>e.score;
                  LocateElem_Sq(L,e);
                  print();
                  cin>>ch;
              }
              break;
          case('2'):    //插入
              {
                  //将数组p中的元素添加至链表L中
                  cout<<"姓名\t"<<"学号\t"<<"成绩\t"<<endl;
                  cin>>p[1].name>>p[1].num>>p[1].score ;
                  ListInsert_Sq(L,1,p[1]);    
                  print();
                  cin>>ch;
              }
              break;
          case('3'):    //删除
              {
                  ListDelete_Sq(L,1,e);
                  print();
                  cin>>ch;
              }
              break;
          case('4'):    //销毁
              {
                  ListDestroy(L);
                  print();
                  cin>>ch;
              }
              break;
          case('5'):    //遍历
              {
                  ListTraver(L);
                  print();
                  cin>>ch;
              }
              break;
          default:
              {
                  cout<<"您输入的数据有误!青重新选择!"<<endl;
                  print();
                  cin>>ch;
              }
          }
      }
      system("pause");
      return 0;
  }
  
  //=============================函数定义============================
  void print()
  {    
      cout<<"----------------------------------------------"<<endl;
      cout<<"-----------------学生信息表------------------"<<endl;
      cout<<"------------------1.查找---------------------"<<endl;
     cout<<"------------------2.插入---------------------"<<endl;
     cout<<"------------------3.删除---------------------"<<endl;
     cout<<"------------------4.销毁---------------------"<<endl;
     cout<<"------------------5.遍历---------------------"<<endl;
     cout<<"------------------6.退出---------------------"<<endl;
     cout<<"----------------------------------------------"<<endl;
 }

 

<二>线性表的链式表示和实现

1) 定义

线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的, 也可以是不连续的). 因此, 为了表示每一个数据元素与其直接后继数据元素之间的逻辑关系, 对于一个数据元素来说, 除了存储其本身的信息之外, 还需存储一个指示其直接后继的信息(即直接后继的存储位置). 这两部分信息组成该数据元素的存储映像, 称为结点(node). 它包括两个域: 其中存储数据元素信息的域称为数据域; 存储其直接后继存储位置的域称为指针域. 指针域中存储的信息称为指针或链. n个结点链结成一个链表, 即线性表的链式存储结构. 又因此链表中的每个结点中只包含一个指针域, 故又称线性链表或单链表.

在线性链表中, 整个链表的存取必须从头指针开始进行, 头指针指示链表中第一个结点(即第一个数据元素的存储映像)的存储位置. 同时, 由于最后一个数据元素没有直接后继,则线性链表中最后一个结点的指针为"空"(NULL). 用线性链表表示线性表时, 数据元素之间的逻辑关系是由结点中的指针指示的. 在单链表中,取得第 i 个数据元素必须从头指针出发寻找, 因此, 单链表是非随机存取的存储结构. 它不要求逻辑上相邻的两个元素在物理位置上也相邻, 因此它没有顺序存储结构所具有的弱点, 但同时也失去了顺序表可随机存取的优点.

2) 实现代码

 

/*    Pubuse.h*/
  //    几乎所有实验中都涉及到的,包含一些常量定义,系统函数原型声明和类型,重定义(typedef)和结果代码等
  //==================================系统函数原型声明===================================
 #include<string>  
 #include<ctype.h>  
 #include<malloc.h>
 #include<limits.h>  
 #include <stdio.h>
 #include <stdlib.h>
 #include<io.h>
 #include<iostream>
 #include<math.h> 
 #include<process.h>
 using namespace std;

 //==================================函数结果状态代码=================================== 
 #define TRUE             1  
 #define FALSE            0  
 #define OK               1  
 #define ERROR            0  
 #define INFEASIBLE      -1  
 //#define OVERFLOW      -2 
 //    因为在math. h 中已定义OVERFLOW 的值为3,故去掉此行
 
 //================================类型重定义代码========================================
 typedef int Status; 
 //    Status 是函数的类型,其值是函数结果状态代码,如OK 等
 typedef int Boolean;
 //    Boolean 是布尔类型,其值是TRUE 或FALSE  

 /*    Def.h*/
  //    数据结构定义
  //=========================数据结构定义=====================================================
typedef  struct Student   
{
    char name[20];   
    char num[20];    
    float score;    
}ElemType;             

typedef struct Lnode 
{
    ElemType date;         
    struct Lnode *next;     
}LNode,*LinkList;

 /*    Algo.h*/
       //基本操作和算法
   //=================================基本操作和算法==============================
Status InitList_L(LinkList &L)    
{//    构造一个空的线性表L
    L=(LinkList)malloc(sizeof(LNode));    
    if(!L)     
        exit(OVERFLOW);
    L->next =NULL;
    return OK;
}
Status InsertList_L(LinkList &L,int i,ElemType e)    
{//    插入数据
    LinkList p,s;      
    int j=0;
    p=L;         
    while(p&&j<i-1)   
    {
        p=p->next ;
        ++j;
    }
    if(!p||j>i-1)
        return ERROR;
    s=(LinkList)malloc(sizeof(LNode));
    strcpy_s(s->date.name ,e.name );
    strcpy_s(s->date.num  ,e.num );
    s->date.score  =e.score ;
    //再将链表p位置i之后的数据转移至链表s
        //再将链表s的所有数据(包括所插入的数据)复制至链表p位置i之后
    s->next =p->next ;
    p->next =s;
    return OK;
}
Status ListDelete(LinkList &L,int i,ElemType &e)     
{//    删除数据
    LinkList p,q;
    p=L;
    int j=0;
    while(p->next &&j<i-1)
    {
        p=p->next ;
        ++j;
    }
    if(!p->next &&j>i-1)
        return ERROR;
    q=p->next ;              //将要删除的p后元素赋给q
    p->next =q->next ;   //删除q位置的元素
    e=q->date ;             //由e返回其删除的元素值
    free(q);                   //释放结点q
    return OK;
}
int ListLength(LinkList L)       
{//    返回表中数据元素的个数
    int i=0;
    LinkList p;
    p=L->next ;
    while(p)
    {
        i++;
        p=p->next ;
    }
    return i;
}
Status ListEmpty(LinkList L)        
{//    检查该表是否为空表
    if(L->next ==NULL)
        return TRUE;
    else
        return FALSE;
}
Status ListClear(LinkList &L)      
{//    将线性表重置为空表
    free(L->next );
    L->next =NULL;
    return OK;
}
Status ListDestroy(LinkList &L)      
{//    销毁线性表
    free(L);
    return OK;
}
Status ListGetElem(LinkList L,int i,ElemType &e)      
{//    用e返回表中第i个数据元素的值
    LinkList p;
    int j=1;
    p=L->next ;
    while(p&&j<i)
    {
        p=p->next ;
        j++;
    }
    if(!p||j>i)
        return ERROR;
    e=p->date;
    return OK;
}
int compare(ElemType e1,ElemType e)       
{//    比较条件:两元素相同则返回真
    if(strcmp(e1.name,e.name)==0 && strcmp(e1.num ,e.num)==0 && (e1.score ==e.score ))
        return 1;
    else
        return 0;
}
int compare_num(ElemType e1,ElemType e)            
{//       按学号查找
    if(strcmp(e1.num,e.num)==0)
        return 1;
    else
        return 0;
}

Status LocateElem(LinkList L, ElemType e)     
{//    返回表中第一个与e满足compare条件中的数据元素的位序,若不存在则返回0
    LinkList p;
    int j=0;
    p=L->next ;
    while(p)
    {   
        j++;
        if(compare(p->date ,e)==1)   
        {
            cout<<"该元素位于表中第"<<j<<""<<endl;
            return j;
        }
        p=p->next ;
    }
    cout<<"没有找到该元素!"<<endl;
    return FALSE;
}

Status delete_num(LinkList &L,ElemType e)
{
    LinkList p;
    int j=0;
    p=L->next ;
    while(p)
    {   
        j++;
        if(compare_num(p->date ,e)==1)   
        {
            ListDelete(L,j,p->date);
            return j;
        }
        p=p->next ;
    }
    cout<<"没有找到该元素!"<<endl;
    return FALSE;
}

Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e)  
{//    返回数据元素cur_e的前驱
    LinkList p,q;
    p=L->next ;
   while(p->next)                              // p所指结点有后继
   {
     q=p->next;                            // q指向p的后继
     if(compare(q->date ,cur_e)==1)   // p的后继为cur_e
     { 
       pre_e=p->date ;                   // 将p所指元素的值赋给pre_e
       return OK; 
     }
     p=q;                                         // p的后继不为cur_e,p向后移
   }
   return ERROR; 
}
Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e)  
{//    返回数据元素cur_e的后继
    LinkList p;
    p=L->next ;
    while(p->next )
    {
        if(compare(p->date ,cur_e)==1)
        {
            next_e=p->next ->date ;
            return OK;
        }
        p=p->next ;
    }
    return FALSE;
}

Status ListTraver(LinkList L)    
{//    遍历表中所有元素
    LinkList p;
    int len=ListLength(L);
    p=L->next ;

    while(p)         //输出表中所有信息
    {
        cout<<"姓名:"<<p->date .name <<"\t学号:"<<p->date .num <<"\t成绩:"<<p->date .score <<endl;
        p=p->next ;
    }
    cout<<"该表长为:"<<len<<endl;
    return OK;
}

/*    Use.cpp*/
       //调用基本操作的主程序
   //=====================线性表的顺序存储===========================
   
   #include"Pubuse.h"
   #include"Def.h"
   #include"Algo.h"
   
  
  //=====================函数原型声明=============================
Status InitList_L(LinkList &L);
Status InsertList_L(LinkList &L,int i,ElemType e);
Status ListDelete(LinkList &L,int i,ElemType &e);
int ListLength(LinkList L);
Status ListEmpty(LinkList L);
Status ListClear(LinkList &L);
Status ListDestroy(LinkList &L);
Status ListGetElem(LinkList L,int i,ElemType &e);
int compare(ElemType e1,ElemType e);
int compare_num(ElemType e1,ElemType e);
Status LocateElem(LinkList L, ElemType e);
Status delete_num(LinkList &L,ElemType e);
Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e);
Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e);
Status ListTraver(LinkList L);
void print();

  //=====================主函数==============================
  int main(void)
  {
    LinkList L;    //定义一个线性表L
    ElemType p[1],e;
    char ch;
    InitList_L(L);  //构造一个空链表
    print();
    cin>>ch;
    while (ch!='6')
    {
        switch(ch)
        {
        case('1'):  //查找
            {
                cout<<"请输入要查找的元素的信息:"<<endl;
                cout<<"姓名\t"<<"学号\t"<<"成绩\t"<<endl;
                cin>>e.name>>e.num>>e.score;
                LocateElem(L,e);
                print();
                cin>>ch;
            }
            break;
        case('2'):    //插入
            {
                //将数组p中的元素添加至链表L中
                cout<<"姓名\t"<<"学号\t"<<"成绩\t"<<endl;
                cin>>p[1].name>>p[1].num>>p[1].score ;
                InsertList_L(L,1,p[1]);    
                print();
                cin>>ch;
            }
            break;
        case('3'):    //删除
            {
                cout<<"请输入要删除的学生学号:"<<endl;
                cin>>e.num;
                delete_num(L,e);
                print();
                cin>>ch;
            }
            break;
        case('4'):    //销毁
            {
                ListDestroy(L);
                print();
                cin>>ch;
            }
            break;
        case('5'):    //遍历
            {
                ListTraver(L);
                print();
                cin>>ch;
            }
            break;
        default:
            {
                cout<<"您输入的数据有误!青重新选择!"<<endl;
                print();
                cin>>ch;
            }
        }
    }
    system("pause");
    return 0;
}
  //=============================函数定义============================
void print()
{    
    cout<<"----------------------------------------------"<<endl;
    cout<<"-----------------学生信息表------------------"<<endl;
    cout<<"------------------1.查找---------------------"<<endl;
    cout<<"------------------2.插入---------------------"<<endl;
    cout<<"------------------3.删除---------------------"<<endl;
    cout<<"------------------4.销毁---------------------"<<endl;
    cout<<"------------------5.遍历---------------------"<<endl;
    cout<<"------------------6.退出---------------------"<<endl;
    cout<<"----------------------------------------------"<<endl;
}

 

 

<三>附加

1) 循环链表: 另一种形式的链式存储结构. 它的特点是表中最后一个结点的指针域指向头结点, 整个链表形成一个环. 循环链表的操作与线性链表基本一致, 差别仅在于算法中的循环条件不是p或p->next是否为空, 而是它们是否等于头指针.

 

2) 双向链表: 顾名思义, 在双向链表的结点中有两个指针域, 其一指向直接后继, 另一指向直接前驱.

在双向链表中, 有些操作如: ListLength(链长), GetElem(返回表中某一元素), 和LocateElem(定位元素的位序)等仅需设计一个方向的指针, 则它们的算法描述与线性链表的操作相同, 但在插入,删除时有很大不同, 在双向链表中需同时修改两个方向上的指针.

 

 实现代码

 

 
posted @ 2017-04-06 00:01  辞镜_Chill  阅读(1421)  评论(0)    收藏  举报