美文网首页
数据结构梳理 — 线性表

数据结构梳理 — 线性表

作者: 11e17ad00a2a | 来源:发表于2017-11-06 20:22 被阅读13次

    线性表:由 >=0 个数据元素组成的有限序列(线性表有两种存储结构:顺序存储结构和链式存储结构)

    一. 顺序存储结构的线性表

    • 图示:


      顺序表.png
    • 地址计算方法:
      如:已知数组int a[n]的初始地址是location(a[0]),求第i个元素的地址location(a[i])
    location(a[i]) = location(a[0]) + (i-1)
    

    因此,顺序存储结构的线性表 查询操作的时间复杂度为 O(1)

    为了描述顺序表表,我们首先要声明一个结构,如下

    /**
     首先声明一个顺序表的结构
     */
    #define LIST_INIT_SIZE 10 //线性表存储空间的初始分配量
    #define LISTINCREMENT 5   //线性表存储空间的分配增量(当存储空间不够时要用到)
    typedef int ElemType;      //数据元素的类型,假设是int型的
    typedef struct{
        ElemType *elem;       //存储空间的基地址
        int length;      //当前线性表的长度
        int listsize;    //当前分配的存储容量
    }SqList;
    

    1.创建线性表

    /**
     
     创建线性表
     
     */
    int InitList(SqList *L)
    {
        L->elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));//开辟一个存储空间,并把这块存储空间的基地址赋值给elem
        if (L->elem == NULL)
        {
            return -1;//空间分配失败
        }
        
        L->length = 0;//线性表的当前长度
        L->listsize = LIST_INIT_SIZE;//当前分配量
        
        return 0;
    }
    
    

    2.1查找元素(按位置 or 地址查找),时间复杂度为O(1)

    /**
     
     查找元素(按位置 or 地址查找),时间复杂度为O(1)
     查找第i个元素,存储在e中
     成功为0,失败为-1
     
     */
    int GetElem(SqList *L, int i, ElemType *e)
    {
        
    #if 0  //按位置查找
        //判断查找的合法性
        if (i<1 || i>L->length)
        {
            return -1;
        }
        
        *e = L->elem[i-1];
        
        return 0;
    #endif
        
        //按地址查找
        
        //判断查找的合法性
        if (i<1 || i>L->length)
        {
            return -1;
        }
        
        *e = *(L->elem+(i-1));
        return 0;
    }
    
    

    2.2查找元素(按值查找),时间复杂度为O(n)

    /**
     
     查找元素(按值查找),时间复杂度为O(n)
     返回元素的坐标
     
     */
    int LocateElem(SqList *L, ElemType x)
    {
        int pos = -1;
        for (int i = 0; i < L->length; i++)
        {
            if (L->elem[i] == x)
            {
                pos = i;
            }
        }
        
        return pos;
    }
    

    3.插入元素,时间复杂度为O(n)

    /**
     
     插入元素,时间复杂度为O(n)
     返回该坐标的元素
     
     */
    
    int ListInsert(SqList *L, int i, ElemType e)
    {
        //判断插入位置的合法性
        if (i<1 || i>L->length+1) return -1;
        //判断存储空间是否够用
        if (L->length >= L->listsize)
        {
            ElemType *newbase = (ElemType *)realloc(L->elem, (L->listsize + LISTINCREMENT)*sizeof(ElemType));
            if (!newbase) return -1;//存储空间分配失败
            L->elem = newbase;//新基址
            L->listsize += LISTINCREMENT;//增加存储容量
        }
        //插入操作
        ElemType *q, *p; //定义2个指针变量
        q = &(L->elem[i-1]); //q为插入位置(注意形参i是序号,序号是从从1开始的,而下标是从0开始的,因此这里转成下标后是i-1)
        for (p = &(L->elem[L->length - 1]); p >= q; --p) //从ai到an-1依次后移,注意后移操作要从后往前进行
        {
            *(p + 1) = *p;
        }
        *q = e;
        ++L->length;//表长加1
        return 0;
    }
    
    

    4.删除元素,时间复杂度为O(n)

    /**
     
     删除元素,时间复杂度为O(n)
     删除第i个元素,并将删除的元素保存在e中
     */
    int ListDelete(SqList *L, int i, ElemType *e)
    {
        //判断删除位置是否越界
        if (i<1 || i>L->length)
        {
            return -1;
        }
        
        ElemType *p = &(L->elem[i]);//将p指向i位置的元素
        *e = *p;//将p指向i位置的元素保存在e中
        ElemType *q = &(L->elem[L->length-1]);//将q指向最后的元素
        for (; p <= q; p++)
        {
            *p = *(p+1);//将顺序表中的元素依次平移一个位置
        }
        
        return 0;
    }
    
    

    5.清空顺序表

    /**
     
     清空顺序表
     */
    int ListClean(SqList *L)
    {
        L->length = 0;//把当前线性表的长度设置为0,表示清空
        return 0;
    }
    

    测试代码如下:

    int main(int argc, const char * argv[]) {
        
        SqList *l = (SqList *)malloc(sizeof(SqList));
        
        //创建顺序表
        InitList(l);
        
    
        //从第1个元素开始,先顺序插入10个元素
        for (int i=1; i<11; i++)
        {
            ListInsert(l, i, 11-i);
        }
        
        //输出线性表
        for (int i = 0; i < 10; i++)
        {
            printf("%d ", l->elem[i]);
        }
        printf("\n");
        
        //在坐标为3的位置插入元素
        int status = ListInsert(l, 3, 99);
        
        //输出线性表
        for (int i = 0; i < l->length; i++)
        {
            printf("%d ", l->elem[i]);
        }
        printf("\n");
        
            
        //在顺序表中查找元素为7的坐标
        int pos = LocateElem(l, 7);
        printf("l->elem[%d] = %d", pos, l->elem[pos]);
        
        //删除第5个元素,并且删除的元素保存在e中
        ElemType e = 0;
        ListDelete(l, 5, &e);
        printf("\n");
    
        
        //输出线性表
        for (int i = 0; i < l->length; i++)
        {
            printf("%d ", l->elem[i]);
        }
        printf("\n");
        
        //查找第3个元素,并且结果保存在find_e中
        ElemType find_e = 0;
        GetElem(l, 3, &find_e);
        printf("find_e = %d ", find_e);
        printf("\n");
    
        
        
        if (l != NULL)
        {
            //清空顺序表
            ListClean(l);
            
            free(l->elem);
            free(l);
            l = NULL;
        }
            
        return 0;
    }
    
    

    二. 链式存储结构的线性表

    解释俩概念头指针头节点

    头指针:指向链表的第一个节点的指针,若链表有头节点,则是只想头节点的指针。头指针的变量名字常用来表识链表的名字。无论链表是否为空,头指针均不为空。(是链表的必要元素)
    头节点:头节点是为了从左统一和方便而设立的,放在第一个元素的节点之前,其数据域一般无意义(但也可以用来存放链表的长度)。

    单链表

    单链表、空链表图例:


    单链表.png

    为了描述单链表,我们首先要声明一个结构,如下

    /**
     首先声明一个单链表的结构
     */
    
    typedef int ElemType;      //数据元素的类型,假设是int型的
    
    typedef struct{
        ElemType data;//数据域
        struct LNode *next;//指针域
        
    }LNode;
    
    typedef LNode LinkList;
    
    

    1.创建单链表
    头插法

    /**
     
     1.1创建链表(头插法) 时间复杂度O(n)
     
     */
    LinkList * CreateListHead(int n)
    {
        LNode *L = NULL;
        L = (LNode *)malloc(sizeof(LNode));
        L->next = NULL;
        
        LNode *p = NULL;//p为新结点,指向最后一个元素
        
        for (int i=1 ; i<=n; ++i)
        {
            p = (LNode *)malloc(sizeof(LNode));
            p->data = i;
            p->next = L->next;//将p的next指向L的next
            L->next = p;//将L的next指向p
        }
        
        return L;
    }
    

    尾插法

    /**
     
     1.2创建链表(尾插法) 时间复杂度O(n)
     
     */
    LinkList * CreateListTail(int n)
    {
        LNode *L = NULL;
        L = (LNode *)malloc(sizeof(LNode));
        L->next = NULL;
        
        LNode *r = L;
        
        LNode *p = NULL;
        
        for (int i=1 ; i<=n; ++i)
        {
            p = (LNode *)malloc(sizeof(LNode));
            p->data = i;
            p->next = NULL;
            
            r->next = p;//将r的next指向p
            r = p;//将r指向p的指向
        }
            
        return L;
    }
    

    2.单链表查找

    /**
     
     2.查找元素(取第i个元素) 时间复杂度O(n)
     
     */
    
    int GetElemFromLinkList(LinkList *L, int i, ElemType *e)
    {
        LNode *node = L;//设node为第i-1个结点
    
        while (i!=0 && node!=NULL) {
            --i;
            node = node->next;
        }
        
        if (i==0 && node != NULL) {
    
            *e = node->data;
            return 0;
            
        } else {
            
            return -1;//第i个元素不存在
        }
    }
    
    

    3.单链表插入

    /**
     
     3.插入元素 时间复杂度为O(n),但是频繁插入时,为O(1)
        在第i个元素位置,插入数据e
     
     */
    
    int LinkListInsert(LinkList *L, int i, ElemType e)
    {
        LNode *p = L;//设p为第i-1个结点
    
        while(i!=0 && p!=NULL) {
            --i;
            p = p->next;
        }
        
        if (i==0 && p!=NULL) {
            LNode *s = (LNode*)malloc(sizeof(LNode));
            s->data = e;
            
            s->next = p->next;//s的直接后继指向p原来的直接后继
            p->next = s;//p的直接后继指向s
            
            return 0;
        } else {
            return -1;
        }
    }
    
    

    4.单链表删除

    /**
     
     4.插入元素 时间复杂度为O(n),但是频繁删除时,为O(1)
        删除第i个节点
     
     */
    int LinkListDelete(LNode *L, int i, ElemType *e)
    {
        LNode *p = L;//设p为第i-1个结点
        
        while(i!=0 && p!=NULL) {
            --i;
            p = p->next;
        }
        
        if (i==0 && p!=NULL) {
            LNode *q = p->next;
            p->next = q->next;
            *e = q->data;
            
            return 0;
        } else {
            return -1;
        }
    }
    
    

    5.清空单链表

    /**
     
     5.清空单链表 时间复杂度为O(n)
     
     */
    int ClearLinkList(LinkList *L)
    {
        LNode *p, *q;
        p = L->next;
        
        while (p != NULL) {
            q = p->next;
            free(p);
            p = q;
        }
        
        L->next = NULL;
        
        return 0;
    }
    
    

    测试代码如下:

        // 创建链表:头插法
        LinkList *l1 = CreateListHead(10);
        
        //输出链表中的数据
        while (l1->next != NULL) {
            LNode *node = l1->next;
            printf("%d ", node->data);
            l1 = node;
        }
        printf("\n");
        
        // 创建链表:尾插法
        LinkList *l2 = CreateListTail(10);
        
        //输出链表中的数据
        while (l2->next != NULL) {
            LNode *node = l2->next;
            printf("%d ", node->data);
            l2 = node;
        }
        printf("\n");
        
        // 查找第7个节点
        LinkList *l3 = CreateListTail(10);
        int xdata = 0;
        int status = GetElemFromLinkList(l3, 7, &xdata);// 返回7
        
        // 查找第7个节点位置插入777
        status = LinkListInsert(l3, 7, 777);
        
        GetElemFromLinkList(l3, 8, &xdata);// 返回777
    
        // 删除第i个节点
        status = LinkListDelete(l3, 4, &xdata);
        
        // 清空单链表
        status = ClearLinkList(l3);
        //输出链表中的数据
        while (l3->next != NULL) {
            LNode *node = l3->next;
            printf("%d ", node->data);
            l2 = node;
        }
        printf("\n");
    

    demo地址

    相关文章

      网友评论

          本文标题:数据结构梳理 — 线性表

          本文链接:https://www.haomeiwen.com/subject/fphrmxtx.html