数据结构(一)线性表顺序存储实现

(一)线性表的抽象数据类型定义

定义:由零个(空表)或多个数据元素组成的序列
ADT: 
    线性表List
Data:
    线性表的数据对象集合为{a1,a2,...,an},每个元素类型为DataType。除了第一个无前驱,最后一个无后继,
    其他每个元素都有一个字节前驱和直接后继结点。数据元素间关系一对一。
Operation:
    InitList(*L);//初始线性表,创建空表
    ClearList(*L);//清空线性表数据
    ListEmpty(L);//判断列表是否为空
    ListLength(L);//获取线性表的长度
    GetElem(L,i,* e);//获取指定位置的元素,返回在指针元素中
    LocateElem(L,e);//查找元素在线性表中的位置
    ListInsert(*L,i,e);//向线性表中指定位置插入元素
    ListDelete(*L, i, *e);//删除指定位置处的元素

(二)线性表的实现

#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 20 //线性表最大存储空间,使用数组实现

typedef int Status;    //用Status来表示函数返回状态码,是上面定义的OK等数据状态
typedef int ElemType;    //ElemType是表示我们存储数据的类型,根据情况而定,这里设置为int

typedef struct
{
    ElemType data[MAXSIZE];    //数组存储数据元素
    int length;    //线性表当前长度
}SqList;    //表名

//四个基本操作,初始,清空,判断是否为空,获取长度
Status InitList(SqList* L);
Status ClearList(SqList* L);
Status ListEmpty(SqList L);
int ListLength(SqList L);

//四个元素操作,插入,删除,两种查找
Status GetElem(SqList L,int i,ElemType* e);
int LocateElem(SqList L, ElemType e);
Status ListInsert(SqList* L, int i, ElemType e);
Status ListDelete(SqList* L, int i, ElemType* e);


void PrintList(SqList L);

/*
复习注意:
我们要进行初始,增加,删除,是需要在原来线性表中进行,所以需要用到引用对原数据进行操作,
而对于查找,获取长度,不需要对原来数据进行操作,我们直接对赋值后的局部变量(含有原线性表的所有数据)参数进行操作,即可
*/

//四个基本操作,初始,清空,判断是否为空,获取长度
//初始线性表
Status InitList(SqList* L)
{
    L->length = 0;

    for (int i = 0; i < MAXSIZE; i++)
    {
        L->data[i] = 0;
    }

    return OK;
}

//清空线性表数据
Status ClearList(SqList* L)
{
    L->length = 0;
    for (int i = 0; i < MAXSIZE; i++)
        L->data[i] = 0;

    return OK;
}

//判断列表是否为空
Status ListEmpty(SqList L)
{
    if (L.length == 0)
        return TRUE;
    return FALSE;
}

//获取线性表的长度
int ListLength(SqList L)
{
    return L.length;
}

//四个元素操作,插入,删除,两种查找
//获取指定位置的元素,返回在指针元素中
Status GetElem(SqList L, int i, ElemType* e)
{
    if (L.length == 0 || i<1 || L.length < i)
        return ERROR;
    *e = L.data[i - 1];
    return OK;
}

//查找元素在线性表中的位置
int LocateElem(SqList L, ElemType e)
{
    int i = 0;

    if (L.length == 0)
        return 0;

    for (; i < MAXSIZE;i++)
        if (L.data[i] == e)
            break;
    if (i >= MAXSIZE)
        return 0;

    return i+1;    //注意线性表中的位置不是按照数组一样从0开始,而是按照我们正常习惯1开始的
}

//向线性表中指定位置插入元素
Status ListInsert(SqList* L, int i, ElemType e)
{
    int k;
    if (L->length == MAXSIZE)//线性表满
        return ERROR;
    if (i<1 || i>L->length+1)
        return ERROR;
    //开始进行插入操作,先判断元素插入位置,在将后面元素向后移动一位,进行插入
    if (i <= L->length)
    {
        for (k = L->length; k >= i;k--)    //只考虑插入到i=1(数组0),会简单点
            L->data[k] = L->data[k - 1];
    }
    L->data[i - 1] = e;
    L->length++;
    return OK;
}

//删除指定位置处的元素 Status ListDelete(SqList
* L, int i, ElemType* e) { int k; if (L->length == 0 || i < 1 || i > L->length) return ERROR; //删除元素,是将后面元素前移,再将最后的元素删除 *e = L->data[i - 1]; if (i < L->length) { for (k = i; k < L->length; k++) L->data[k - 1] = L->data[k]; } L->data[L->length-1] = 0; L->length--; return OK; } //打印链表数据 void PrintList(SqList L) { printf("print list begin...\n"); for (int i = 0; i < L.length; i++) { printf("%d ", L.data[i]); } printf("\nprint list end...\n"); } int main() { SqList L; ElemType e; printf("1.InitList\n"); InitList(&L); printf("2.1 ListInsert 1-10\n"); for (int i = 1; i <= 10; i++) ListInsert(&L, i, i*i + 1); printf("2.2 ListInsert 5\n"); ListInsert(&L, 5, 256); printf("2.3 ListInsert 19 Error\n"); if (ListInsert(&L, 19, 99) == ERROR) printf("ListInsert(&L, 19, 99) Error:Out of range"); PrintList(L); printf("3.ListDelete the first:"); ListDelete(&L, 1, &e); printf("%d\n", e); printf("4.ListDelete the end:"); ListDelete(&L, ListLength(L), &e); printf("%d\n", e); PrintList(L); printf("5.1 find element use GetElem by index(6): "); GetElem(L, 6, &e); printf("%d\n", e); printf("5.1 find element:256 index by LocateElem:"); printf("%d\n", LocateElem(L, 256)); printf("6.Get List length:%d\n", ListLength(L)); printf("7.ClearList\n"); ClearList(&L); if (ListEmpty(L)==OK) printf("8.ListEmpty\n"); system("pause"); return 0; }
1.InitList
initial List....
2.1 ListInsert 1-10
2.2 ListInsert 5
2.3 ListInsert 19 Error
ListInsert(&L, 19, 99) Error:Out of rangeprint list begin...
2 5 10 17 256 26 37 50 65 82 101
print list end...
3.ListDelete the first:2
4.ListDelete the end:101
print list begin...
5 10 17 256 26 37 50 65 82
print list end...
5.1 find element use GetElem by index(6): 37
5.1 find element:256 index by LocateElem:4
6.Get List length:9
7.ClearList
8.ListEmpty
请按任意键继续. . .
输出结果

注意点:

我们进行操作线性表是从1开始的,而数组索引是从0开始的,这是我们需要注意的地方

(三)实现两个线性表的并集

//实现两个线性表的并集
void unionL(SqList* La, SqList Lb)
{
    ElemType e;
    int i = 0;
    int La_length = ListLength(*La);
    int Lb_length = ListLength(Lb);
    for (i = 1; i <= Lb_length; i++)
    {
        GetElem(Lb, i, &e);
        if (!LocateElem(*La, e))
            ListInsert(La, La_length++, e);
    }
}
int main()
{
    SqList La,Lb;
    InitList(&La);
    InitList(&Lb);
    for (int i = 1; i <= 10; i++)
    {
        ListInsert(&La, i, 3 * i);
        ListInsert(&Lb, i, 2 * i);
    }

    unionL(&La, Lb);

    PrintList(La);

    system("pause");
    return 0;
}
print list begin...
3 6 9 12 15 18 21 24 27 2 4 8 10 14 16 20 30
print list end...
请按任意键继续. . .
输出结果

 (四)地址计算方法(查找操作)时间复杂度

假设ElemType类型数据占用C个字节存储单元,那每个元素相隔C个字节Loc(ai+1)=Loc(ai)+C
地址计算方法Loc(ai)=Loc(a1)+(i-1)*C -->  获取存储位置的时间性能为O(1)

 (五)插入,删除操作时间复杂度

若是在表尾部直接插入或者删除<应用时存取数据>,则此时的时间复杂度为O(1),是最好的情况,
若是在表头进行插入或者删除,此时是最坏的情况,意味着需要移动所有的元素向前或者向后,这时的事件复杂度是O(n),
否则若是在中间进行插入删除,位置为i,需要对元素进行遍历,将元素进行后移或者前移n-i,此时的平均移动次数和最中间元素移动相同,为(n-1)/2,平均时间复杂度还是O(n)

(六)总结

线性表的顺序存储结构,
在存(是默认放在最后位置)或读取数据时,时间复杂度都是O(1)
在插入或者删除时时间复杂度都是O(n).
所以他比较适合于元素个数不太变化,而更多是存取数据的应用。

 (七)优缺点

优点

1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间。
2.可以快速的存取表中的任意位置的元素。

缺点

1.插入和删除操作需要移动大量的元素
2.当线性表长度变化较大时,难以确定存储空间的容量
3.任意造成存储空间的碎片

 

posted @ 2018-08-03 22:18  山上有风景  阅读(1059)  评论(1编辑  收藏  举报