线性表 whl

一、顺序表可用一维数组实现

1、初步实现思路

  假设数据类型为ElemType,存储100个元素的数组ElemType elem[100];
  顺序表还需要什么属性,怎样表示顺序表?
 将顺序表定义为:

顺序表定义
1 typedef struct 
2 {
3     ElemType elem[MAXSIZE];//存储顺序表元素
4     int length; //元素数目,也称长度
5 }List;

1.1初始化

ListInit
1 Status ListInit ( List &L )//初步实现——初始化
2 {  
3     L.length= 0 ;    
4     return OK ;   
5 }


1.2插入

  在第i个元素之前插入一个新元素x,线性表长度增1
插入过程
  将线性表L中的第i个至第n个元素后移一个位置
  将结点x插入到结点第(i-1)个位置;
  线性表长度加1;

ListInsert
 1 Status ListInsert (List &L, int i, ElemType x)
 2 {
 3     int j ;
 4     if  ( i < 1 ||  ( i > L.length+1))  
 5         return  ERROR ;
 6     if  ( L.length >= MAX_SIZE)
 7     {
 8         printf("线性表溢出!\n");  
 9         return  ERROR ; 
10     }
11     /* i-1位置以后的所有结点后移  */
12     for  ( j=L.length-1; j>=i-1; --j )
13         L.elem[j+1] = L.elem[j];
14     L.elem[i-1]=x;    /* 在i-1位置插入结点 */
15     L.length++ ;
16     return  OK ;  
17 }

1.3初步实现的问题
  线性表的长度是固定的,实际应用中受到很大限制
考虑问题:
  1、表长是否可以实现动态调整
  2、表长动态调整的情况下,如何实现内存的合理化管理

2、优化实现

优化后的定义方式为:

优化实现
1 typedef struct 
2 {
3     ElemType *elem;   //存储顺序表元素
4    int length;        //元素数目
5    int size;         //当前存储容量
6 }List;

配合使用:

程序
 1 #define  OK   1
 2 #define  ERROR   -1
 3 #define  MAX_SIZE  100
 4 typedef  int  Status ;
 5 typedef  int  ElemType ; 
 6 #define LIST_INIT_SIZE 100 //初始存储空间 
 7 #define LISTINCREMENT 10  //分配增量  
 8 typedef struct 
 9 {
10     ElemType *elem;   //存储顺序表元素
11    int length;        //元素数目
12    int size;         //当前存储容量
13 }List;
14 
15 
16 Status ListInit(List &L) 
17 {
18     L.elem= (ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));
19     if (!L.elem ) 
20         return ERROR;
21     L.length = 0;
22     L.size =  LIST_INIT_SIZE;
23     return OK;
24 }
25 Status ListInsert (List &L, int i ,ElemType x)
26 {
27     int j ;
28     ElemType *buffer; 
29     if  ( i<0 || i>L.length+1)   
30         return  ERROR ;
31     if  ( L.length >= L.size)
32     {  //当前空间已满,分配新空间   
33         buffer = (ElemType*)realloc(L.elem ,(L.size+LISTINCREMENT) * sizeof(ElemType)) ;
34         if ( buffer == NULL ) 
35             return ERROR;
36         L.elem = buffer;
37         L.size += LISTINCREMENT;
38     }
39     ……    //后续插入逻辑与初步实现相同
40 }
41 /*
42 原型:void *realloc( void *memblock, size_t size );  
43 语法:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
44 新的大小一定要大于原来的大小不然的话会导致数据丢失!   
45 
46 /*顺序表删除
47 将第i个元素删除,线性表长度减1
48 删除过程
49 将线性表L中的第i+1个至第n个元素依次向前移动一个位置
50 线性表长度减1
51 */
52 Status ListDelete ( List *L,int i )
53 {
54     int k;
55     if  (L->length==0)
56     {  
57         printf(“线性表L为空!\n”); 
58         return ERROR; 
59     } 
60     else if ( i<1||i>L->length ) 
61     {
62         printf(“要删除的数据元素不存在!\n”) ; 
63         return ERROR ; 
64     }
65     for ( k=i;  k<L->length; k++)//i位置以后的所有结点前移  
66         L->elem[k-1]=L->elem[k];
67     L->length--;  
68     return OK;
69 }
70 /*
71 在线性表 L= (a1,a2,…,an) 中查找值为x的第一个结点
72 查找过程
73 从起点开始顺序查找
74 */
75 Status  ListLocate(List *L,ElemType x)
76 {  
77     int  i; 
78     for ( i=0 ; i < L->length; i++) 
79         if ( L->Elem[i] == x ) 
80             return i; 
81     return 0;    
82 } 
83 Status  ListLocate(List *L,ElemType x,int (*compare)(ElemType,ElemType))
84 {  
85     int  i; 
86     for ( i=0 ; i < L->length; i++) 
87         if ( (*compare)( L->Elem[i], x) == 0 ) 
88             return i; 
89     return 0;    
90 } 

补充:比较函数 compare( ElemType , ElemType ),前一个元素"大于"、"等于"或"小于"后一个元素时分别返回1,0或-1。

优点

  逻辑上相邻的元素,物理上也相邻

  可随机存取顺序表中的任一元素

缺点

  插入和删除操作需要通过移动元素来实现,效率低

二、链式表

/*
二、线性表的链式表示与实现
1、线性表的链式存储结构
用一组任意的存储单元存放线性表的数据元素
每个节点由数据域和指针域组成
数据域存储数据元素信息
指针域存储后继元素的存储位置
通过指针域可将n个节点按其逻辑顺序链接在一起,称为链表
2、链表的特点
链表中结点,逻辑相邻不一定物理相邻
数据元素之间的逻辑关系由链表中的指针域指示,而节点在存储器中的存储位置时随意的
顺序存取,不能实现随机存取

3、带头结点的单链表
    每一个结点只包含一个指针域的链表,称为单链表
    为操作方便,一般会在链表的第一个结点之前附设一个头结点(头指针)head指向第一个结点,头结点的数据域可以不存储任何信息,也可或存储链表长度等信息
4、为何需要带头结点
    对于无头结点的链表被函数处理时,如果头结点发生移动,头指针即被改动,因此需要函数将更新后的头结点采用某种形式传回,增加了函数的负担,降低了通用性
    采用带有头结点的链表,则不必担心头指针被修改,解决了上述问题
*/
typedef struct LNode 
{
    ElemType data;        //结点的数据域
    struct LNode *next; //结点的指针域
}LNode,*LinkList;        //LinkList为指向结构体LNode的指针类型


//初始化带头结点单链表
//分配头结点空间
Status ListInit(LinkList &L)
{
    L=(LinkList)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点
    L.next = NULL;//头结点的指针域置空
}

Status ListInsert(LinkList &L, int i, ElemType e) 
{  
    // 在带头结点的单链线性表L的第i个元素之前插入元素e
    LinkList  p, q;
    p = L;   
    int j = 0;
    while (p && j < i-1) 
    {  // 寻找第i-1个结点
        p = p->next;
        ++j;
    } 
    if (!p || j > i-1)
        return ERROR;      // i小于1或者大于表长
    q = (LinkList)malloc(sizeof(LNode));  // 生成新结点
    q->data = e; 
    q->next = p->next;      // 插入L中
    p->next = q;
    return OK;
} 
Status  ListDelete( LinkList &L, int i, ElemType &e) 
{
    //在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
    LinkList  p, q;
    p = L;
    int j = 0;
    while (p->next && j < i-1) 
    {  // 寻找第i个结点,并令p指向其前趋
        p = p->next;
        ++j; }
    if (!(p->next) || j > i-1) 
        return ERROR;  // 删除位置不合理
    q = p->next;
    p->next = q->next; // 删除并释放结点
    e = q->data;
    free(q);
    return OK;
} 

//获取指定结点数据
Status ListGetElem (LinkList &L, int i,  ElemType &e) 
{  
    // L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
    LinkList  p;
    p = L->next;   
    int j = 1;           // 初始化,p指向第一个结点,j为计数器
    while (p && j<i) {   
        // 顺指针向后查找,直到p指向第i个元素或p为空
        p = p->next;  ++j;}
    if ( !p || j>i ) return ERROR;  // 第i个元素不存在
    e = p->data;   // 取第i个元素
    return OK;
} 
/*
链表的形态
单链表
循环链表
    最后一个结点的指针指向头结点,形成一个环
双向链表
    结点包含两个指针域,一个指向直接后继结点,一个指向直接前驱结点

链式存储结构的优缺点
优点
    结点空间可以动态申请和释放
    数据元素的逻辑次序靠结点的指针来指示,插入和删除时不需要移动数据元素
缺点
    每个结点的指针域需要占用额外空间
    只能顺序访问,不能随机访问

顺序表 v.s. 链表
    顺序表基于数组实现;链表基于指针实现
    顺序表是一种随机存取结构,对表中任一结点都可以直接定位并存取;链表中的结点则需要从头结点开始顺链扫描
    若线性表的操作主要是查询,表建好后很少涉及插入、删除操作,宜采用顺序表结构;若要频繁进行插入、删除操作,则宜采用链表结构

*/
原文地址:https://www.cnblogs.com/whl2012/p/2786261.html