zoukankan      html  css  js  c++  java
  • 数据结构之线性表再思考

    数据结构学起来妙不可言,贼有意思。

    很久没写博客了,今天来一篇长的。前面写的关于线性表的代码和思路,经过我多次反复思考,又有了新的收获,与大家一起分享。

    1、线性表的定义

    首先要明白什么是线性表,一种常用且最简单的数据结构。

    数据结构通俗来说就是:装水的杯子,有的是圆的、有的是方的。

    官方定义:线性表是n个数据元素的有限序列。

    把这个样的一个数据模型映射到计算机,等同找一块存储空间给它,而线性表一般是申请动态内存,因此就是在堆上给它分配一块内存。

    看下图

     

    通过图我们可以了解到,线性表数据结构如何映射在计算机中。有了这个基础就可以进入代码逻辑部分。/* 图像中的堆是为了更好讲解 */

    2、线性表的操作思路

    在严奶奶的书有如下操作集合

                    1、初始化

                    2、销毁

                    3、置空表

                    4、空表检测

    5、返回元素个数

    6、返回指定位序元素的值

    7、返回表中第一个与指定值满足compare()的数据元素的位序,若不存在,返回0

    8、返回指定元素的前驱

    9、返回指定元素的后驱

    10、插入元素

    11、删除元素

    12、遍历

    13、表A和表B,A = A U B  减去 A n B(A和B的补集)

    14、已知非递减序列表A和表B为合并非递减序列表的表C , C = A U B

    在所有的操作之前,需要定义一个表的结构体:

    结构体:

                    表首地址

                    表使用长度

                    表最大长度

    代码实现:

    typedef struct ChangeList{                         

         ElemType *elem;        

         int Length;            

         int ListSize;     

    }CL;

    初始化

                    1、表首地址指向申请内存的首地址及定义申请的内存多少

                    2、进行动态内存分配成功的检测

                    3、表使用长度归0

                    4、表最大长度被赋值申请内存长度

    /*  动态内存的申请通过malloc函数来实现,需配套使用free函数释放内存  */

    销毁

    1、释放动态内存

    2、表首地址指向NULL(消除表头指针)

    3、表使用长度归0       (消除表使用长度)

    4、表最大长度归0       (消除表最大长度)

    /*

     2的作用是假设1失败,作为补救手段;

                    销毁就是表的所有结构全部销毁,包括首指针、使用长度、最大长度

    结构体在内存中存储是按照最大变量类型所占字节依次排序的,如果表的三个变量没有全部销毁,则会造成内存浪费。

    */

    置空表

                    1、使用长度归0

    空表判断

                    1、判断使用长度是否为0

    返回元素个数

                    1、返回使用长度

    返回指定位序元素的值             

                    1、判断位序是否合法

                    2、返回指定位序的值

    7、返回表中第一个与指定值满足compare()的数据元素的位序,若不存在,返回0

                    在此之前,需要写一个compare函数

                    Compare(  ElemType ElemOne , ElemType ElemTwo )

                    函数作用:两数相等,返回TURE;两数不等,返回FALSE

                    1、在使用长度的范围内判断第一个满足要求的元素,并返回该元素的位序

    返回前驱

                    1、位序2至使用长度之间判定

                    2、若有与指针值相等的元素,返回其元素前驱

    返回后驱

                    1、位序1至 (使用长度 – 1)之间判定

                    2、若有与指针值相等的元素,返回其元素前驱

    插入元素

                    1、判断插入位序是否合法

                    2、判断存储空间是否满了,如果满,追加动态内存

    3、追加动态分配成功检测,失败结束程序

    4、新基址

                    5、最大长度加一

    删除元素

                    1、判断删除序号是否合法

                    2、返回删除元素

                    3、删除位置之后的元素左移

                    4、表长减一

    遍历

                    1、visit函数(自己编写,打印单个指定位置的值)

                    2、把visit函数作为函数参数,遍历

    表A和表B,A = A U B  减去 A n B(A和B的补集)

                    1、获取B中的元素

                    2、进行LocateEem判断

                    3、插入不符合LocateEem判断的B中的元素到A中

                    4、A实际长度增加

    已知非递减序列表A和表B为合并非递减序列表的表C , C = A U B

                    1、为C申请动态内存,分配成功检测

                    2、合并算法

                                    2-1、比较大小

                                    2-2、插入C,C表增加

                                    2-3、A、B是否有一方先插完,再继续插入另一表

    代码:

       1 /***************************************
      2 程序日期:2018/10/04/21:47 to
      3 程序功能:实现动态线性表的各类操作,详细参见各函数注解
      4 程序结构:作为晚辈,我自己的代码很多没有严奶奶的精炼。
      5 程序作者:Jamu Foo
      6 ***************************************/
      7 #include<stdio.h>
      8 #include<stdlib.h>
      9 /**************************************/
     10 #define Status int        /* 方便对线性表的数据类型更改,本实验代码为整数型 */    
     11 #define ElemType int
     12 /**************************************/
     13 #define MaxSize 100        /* MaxSize为固态线性表最大长度 100 */
     14 #define Increment 10    /* Increment为固态线性表单次增长量 10 */
     15 /**************************************/    
     16
     17 #define OVERFLOW -1        /* 溢出标志 */
     18 #define INFEASIBLE -2    /* 不可实行标志 */
     19 #define TURE 0            /* 操作正确标志1 */
     20 #define FALSE 1            /* 操作错误标志1 */
     21 #define OK 0            /* 操作正确标志2 */
     22 #define ERROR 1            /* 操作错误标志2 */
     23 /**************************************/
     24 typedef struct ChangeList{ /* 动态线性表定义 */                          
     25     ElemType *elem;        /* 存储空间的首地址 */
     26     int Length;            /* 当前长度 */
     27     int ListSize;        /* 当前分配的存储容量 */
     28 }CL;
     29 /**************************************/
     30 /*函数声明集合*/
     31
     32 Status compare( ElemType ElemOne, ElemType ElemTwo );    /* 两数相等返回0,否则返回1 */
     33 Status InitList( CL *L );        /* 初始化 */
     34 Status DestoryList( CL *L );    /* 销毁表 */
     35 Status ClearList( CL *L );        /* 置空表 */
     36 Status ListEmpty( CL *L );        /* 空表检测 */
     37 Status LisTLength( CL *L );        /* 返回表长 */
     38 Status GetElem( CL *L, int i, ElemType *e); /* 用e返回第i个位置的元素的值 */
     39 Status LocateElem( CL *L, ElemType e, Status ( *compare)( ElemType, ElemType ) ); /* 返回表中数据元素与e满足conpare关系的位序 */
     40 Status PriorElem( CL *L,ElemType cur_e, ElemType *pre_e ); /* cur_e为L中的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败 */
     41 Status NextElem( CL *L,ElemType cur_e, ElemType *next_e ); /* cur_e为L中的数据元素,且不是最后个,则用next_e返回它的后驱,否则操作失败 */
     42 Status ListInsert ( CL *L, int i, ElemType ie ); /* 在序号i之前插入ie */
     43 Status ListDelete ( CL *L, int i, ElemType *de );/* 删除序号i的元素 */
     44 void visit( ElemType *p ); /* 打印单个指定位置的值 */
     45 Status ListTraverse( CL *L, void ( *visit )( ElemType* ) ); /* 遍历 */
     46 void Union(CL *La, CL *Lb);
     47 void MergeList( CL *La, CL *Lb, CL *Lc );/* 已知线性表La和Lb的元素按值非递减排列(俗话:按从小到大排列) */
     48                                         /*  将La和Lb合并为Lc,Lc的元素按值非递减排列 */
     49
     50 /*************************************/
     51 int main(void){    /*主函数*/
     52     CL Ldemo;
     53     ElemType e = 11;
     54     int i = 1;
     55     int ie = 100;
     56     int flag = 1;
     57     int gete = 0;
     58     ElemType deOne = 0;
     59     InitList( &Ldemo );
     60
     61     flag = LisTLength( &Ldemo );
     62     printf( "表长%d ", flag );
     63
     64     for( i; i <= 10; i++ ) {    /* 插入实验 */
     65         ListInsert( &Ldemo, i, ie );
     66         flag = LisTLength( &Ldemo );
     67         printf( " 表长 %d ", flag );
     68         ie++;
     69     }/* 第一个for循环后i的值变11,因此要重新给i赋值1 */
     70     for( i = 1 ; i <= 10; i++ ){
     71         GetElem( &Ldemo, i, &gete );
     72         printf( "获取到的元素的值为%d ", gete );
     73     }
     74     return 0;
     75 //    ListDelete( &Ldemo, 1, &deOne );    
     76 /* 疑点,当ListDelete函数的第二个参数为声明且初始化为0的指针变量,会出现莫名错误 */                            
     77 /* 解答,因为指针初始化=0,表示指向0X00(不可读不可写的内存),进入delete函数后,拷贝了一个不影响的副本变量并赋值给*de,这时候的*de指向内存0X00,*de = *p这个操作,便出错了 */
     78 /* 每个指针只能指向一个地址,假设 指针a 初始化指向0X00,指针b指向i=1的地方,那么经过指针a = 指针b的操作,就变成了指针b指向指针a,指针a指向i=1的地方*/
     79 }
     80 /**************************************/
     81 Status compare( ElemType ElemOne, ElemType ElemTwo ){
     82     if( ElemOne != ElemTwo ){    /* 两数相同返回TURE,否则返回FALSE */
     83         return FALSE;
     84     }
     85     else{
     86         return TURE;
     87     }
     88 }
     89 /**************************************/
     90 Status InitList( CL *L ){    
     91     /* malloc 申请内存成功 则返回 void* 类型的内存首地址,否则返回NULL,void* 可强制转换为任何类型 */
     92     L->elem = ( ElemType * )malloc( MaxSize * sizeof(ElemType));
     93     if( L->elem == NULL ) exit(OVERFLOW); /* 分配失败,结束程序 */
     94     L->Length = 0;
     95     L->ListSize = MaxSize;
     96     printf("动态线性表初始化成功 ");
     97     return OK;
     98 }
     99 /**************************************/
    100 Status DestoryList( CL *L ){ /* 线性表定义中三个变量都要回到初始化 */
    101     free( L->elem );    /* 使用后该指针变量一定要重新指向NULL,防止野指针出现,有效 规避误操作 */
    102                         /* 指向NULL的指针,NULL其实就是地址为0X0000的内存,这里不可读不可写, */
    103     L->elem = NULL;        /* 如果释放成功,则返回NULL,无论继续free多少次都没关系,如果释放不成功,再次free会出错 */
    104     L->Length = 0;        /*  */
    105     L->ListSize = 0;
    106     printf("销毁线性表成功 ");
    107     return OK;
    108 }
    109 /**************************************/
    110 Status ClearList( CL *L ){
    111     L->Length = 0;    /*表当前长度归0*/
    112     return OK;
    113 }
    114 /**************************************/
    115 Status ListEmpty( CL *L ){    /* 空表判断 */
    116     if( L->Length == 0 ){    /* 为空表 */
    117         printf("List is empty ");
    118         return TURE;
    119     }
    120     else{                    /* 非空表 */
    121         printf("List is not empty ");
    122         return FALSE;
    123     }
    124 }
    125 /**************************************/
    126 Status LisTLength( CL *L ){ /* 返回表的长度,严书这里并没有要求做表长是否合法的判断 */
    127     return L->Length;
    128 }
    129 /**************************************/
    130 Status GetElem( CL *L, int i, ElemType *e ){
    131     if( i < 1 || i > MaxSize ){    /* 判断i是否合法 */
    132         exit( ERROR );    
    133     }
    134     *e = *( L->elem + i - 1 );    /* 用*e返回序号为i的元素的值 */
    135     return OK;
    136 }
    137 /**************************************/
    138 Status LocateElem( CL *L, ElemType e,Status ( *compare )( ElemType, ElemType ) ){
    139     int i = 1;
    140     while( i++ <= L->Length  ){        /* 最多只能加 L->Length次 */
    141         if( ! compare( *( L->elem ++ ) , e ) ){
    142         return i;
    143         }
    144     }
    145     return FALSE;
    146 }
    147 /**************************************/
    148 Status PriorElem( CL *L, ElemType cur_e, ElemType *pre_e ){
    149     int i = 2;
    150     ElemType *p = L->elem + 1;
    151     while( i++ <= L->Length ){
    152         if( cur_e == *( p ++ ) ){
    153             *pre_e = *( p -- );
    154             return *pre_e;
    155         }
    156     }
    157     printf("操作失败 ");
    158     return FALSE;
    159 }
    160 /**************************************/
    161 Status NextElem( CL *L,ElemType cur_e, ElemType *next_e ){
    162     int i = 1;
    163     ElemType *p = L->elem;
    164     while( i++ < L->Length ){
    165         if( cur_e == *( p ++ ) ){
    166             *next_e = *( p ++ );
    167             return *next_e;
    168         }        
    169     }
    170         printf("操作失败 ");
    171         return FALSE;
    172 }
    173 /**************************************/
    174 Status ListInsert ( CL *L, int i, ElemType ie ){
    175
    176     ElemType *newbase = 0, *p = 0, *q = 0;
    177     if( i < 1 || i > MaxSize + 1   ){    /* 插入序号i不合法 */
    178         printf(" 错误的插入序号 " );
    179         return ERROR;
    180     }
    181     if( L->Length >= L->ListSize ){        /* 存储空间满,增加分配 */
    182         newbase = ( ElemType * )realloc( L->elem, ( L->ListSize + Increment ) * sizeof(ElemType) );    
    183         /* newbase的作用:无法提前得知旧内存后是否有足够的内存用于动态内存调整。假设没有足够内存,
    184             L->elem = ( ElemType * )realloc( L->elem, ( L->ListSize + Increment ) * sizeof(ElemType) );
    185             一旦realloc分配失败,返回NULL,但参数中的L->elem并没有被释放。如果直接把realloc的返回值赋给L->elem
    186             则会造成L->elem原来指向的内存丢失,造成泄露
    187         */
    188         /* realloc 函数 如果分配失败,则第一个参数返回NULL */
    189         if( !newbase ){            /* 分配失败检查 */
    190             exit( OVERFLOW );    /* 存储分配失败 */
    191         }
    192         L->elem = newbase;            /* 新基址 */    
    193         L->ListSize += Increment;    /* 增加存储容量 */
    194     }
    195     q = L->elem + i - 1;    /* q 为插入位置 */
    196     for( p = L->elem + L->Length - 1; p >= q; --p){ /* 插入位置及元素的右移动 */
    197         *( p + 1) = * p;
    198     }
    199     *q = ie;    /* 插入ie */
    200     ++L->Length;    /* 表长加1 */
    201     ++L->ListSize;    /* 表最大长度加1 */
    202     printf( "第%d位插入%d", i, ie );
    203     return OK;
    204     /*
    205         realloc函数
    206         原型:extern void *realloc(void *mem_address, unsigned int newsize);
    207         功能:动态内存调整;内存是有限的。
    208     分配成功:返回 指向 被分配内存的指针    
    209         newsize > oldsize:
    210             1、malloc申请的动态内存后面有足够的续接内存。则newbase = oldbsae,size = newsize
    211             2、malloc申请的动态内存后面无足够的续接内存。则申请一块新内存,把旧内存的数据复制过去
    212                 newbase = 新内存的首地址,size = newsize ,free(oldbase)
    213         newsize < oldsize:
    214             1、newbase = oldbase;size = newsize,把旧内存中的数据复制到新内存,会造成数据丢失,
    215         
    216         oldbase = ( int* )malloc ( oldsize * sizeof(int));
    217         newbase = ( int* )realloc ( oldbase, newsize * sizeof(int) );
    218     1、当realloc中第一个参数为NULL,第二个参数不为0,等同与malloc函数
    219     2、当第一个参数不为NULL,第二个参数为0,相当于free函数
    220     分配失败:返回NULL
    221     */
    222 }
    223 /**************************************/
    224 Status ListDelete ( CL *L, int i, ElemType *de ){
    225     ElemType *p = 0, *q = 0;
    226     if( i < 1 || i > L->Length ){    /* 删除序号检查 */
    227         printf(" 错误的删除序号 " );
    228         return ERROR;
    229     }
    230     p = L->elem + i - 1;
    231     *de = *p;    /* 用*de返回被删除的元素 */
    232     q = L->elem + L->Length - 1;
    233     for( ++p; p <= q; ++p ){    /* 被删除后的元素左移 */
    234         *( p - 1 ) = *p;
    235     }
    236     L->Length--;
    237     printf( "删除序号为%d的元素,其值为%d ", i, *de );
    238     return    OK;
    239 }
    240 /**************************************/
    241 void visit( ElemType *p ){
    242     printf("%d ", *p );
    243 }
    244 /**************************************/
    245 Status ListTraverse( CL *L, void ( *visit )( ElemType* ) ){
    246     ElemType *p = 0;
    247     int i = 0;
    248     p = L->elem;
    249     for( i = 1; i < L->Length; i++){
    250         visit(p++);
    251     }
    252     printf(" ");
    253     return OK;
    254 }
    255 /**************************************/
    256 void Union(CL *La, CL *Lb){
    257     int La_len = 0, Lb_len = 0;
    258     int i = 0;
    259     ElemType e = 0;
    260     La_len = La->Length;
    261     Lb_len = Lb->Length;
    262     for( i; i < La_len; i++ ){    
    263         GetElem( Lb, i, &e );    //一次返回Lb中的所有元素
    264         if( !LocateElem( La, e, compare ) ){ //与La不相同的插入La中
    265             ListInsert( La, ++La_len, e );
    266         }
    267     }
    268 }
    269 /**************************************/
    270 void MergeList( CL *La, CL *Lb, CL *Lc ){
    271     int *pa = La->elem;    /* 表a首指针 */
    272     int *pb = Lb->elem; /* 表b首指针 */
    273     int *pc = Lc->elem; /* 表c首指针 */
    274     int *pa_last = La->elem + La->Length - 1; /* 表a尾指针 */
    275     int *pb_last = Lb->elem + Lb->Length - 1; /* 表b尾指针 */
    276     Lc->ListSize = Lc->Length = La->Length + Lb->Length;    /* Lc的长度为(La + Lb)的长度 */
    277     Lc->elem = ( ElemType* )malloc( Lc->ListSize * sizeof( ElemType ) ); /* 申请的动态内存最大长度为Lc->Length */
    278     if( !Lc->elem){
    279         exit( OVERFLOW );    /* 分配失败 */
    280     }/* 合并算法 */
    281     while( pa <= pa_last && pb <= pb_last ){    /* 此处为严蔚敏奶奶的代码,相当的有意思 */
    282         if( *pa <= *pb ){                        /* 忽略面向过程,直接面向了对象 */
    283             *pc++ = *pa++;/* 思路:假设思考路线为个个比较过程,表的数量一旦过大,那么将十分复杂 */
    284         }      /* 无论表做多少容量的合并工作,都是要从两个表的首指针到尾指针,提取出来作为循环判断条件 */
    285         else{ /* 有了循环条件,还需要判断排序条件,提取出来谁大谁排序。循环和判断条件都满足,第一个while就出来了 */    
    286             *pc++ = *pb++;
    287         } /* 第一个while的判断跳出条件,为其中一个表排序完成,因此还需要为另外一个表剩余元素做排序,得到后两个while*/
    288     }
    289     while( pa <= pa_last ){
    290         *pc++ = *pa++;
    291     }
    292     while( pb <= pb_last ){
    293         *pc++ = *pb++;
    294     }
    295 }
    296 /**************************************/
    本文由作者原创,如需转载注明出处!
  • 相关阅读:
    Gym
    HDU 4087 ALetter to Programmers (三维坐标旋转 矩阵 + 矩阵快速幂)
    POJ 3845 Fractal (向量旋转,好题)
    HDU 1700 Points on Cycle (向量旋转 + 圆内接三角形周长和面积最大的是正三角形)
    POJ 1271 Nice Milk (半平面交应用)
    POJ 2540 Hotter Colder (半平面交应用 + 向量旋转)
    luoguP3705 [SDOI2017]新生舞会
    luoguP4123 [CQOI2016]不同的最小割
    luoguP2046 [NOI2010]海拔
    luoguP3227 [HNOI2013]切糕
  • 原文地址:https://www.cnblogs.com/Fsiswo/p/9785072.html
Copyright © 2011-2022 走看看