数据结构-线性表

作者: 1江春水 | 来源:发表于2019-04-15 19:29 被阅读1次

    线性表

    概念:

    线性表是n个数据特性相同元素组成的有限序列,是最基本的也是最常用的一种线性结构(线性表、栈、队列、数组都是线性结构),同时也是其他数据结构的基础。

    对于非空的线性表或线性结构,都有:

    • 存在唯一一个被称为‘第一个’的数据元素
    • 存在唯一一个被称为‘最后一个’的数据元素
    • 除第一个外,结构中的每个数据元素均只有一个前驱
    • 除最后一个外,结构中的每个数据元素均只有一个后继

    两种存储方式

    顺序存储

    概念:用一组地址连续的存储单元依次存储线性表的数据元素,像这种存储结构的线性表成为顺序表。

    特点:逻辑上相邻的数据元素,存储结构上也相邻。

    只要确定好了顺序表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储是一种随机存取的存储结构,因为高级语言中的数组类型也是随机存取的特性,所以通常我们都使用数组来描述数据结构中的顺序存储结构,用动态分配的一维数组来表示线性表。

    顺序表的C语言代码实现

    #define MAXSIZE 10
    typedef int ElementType;
    
    typedef struct {
        ElementType data[MAXSIZE];
        int length;
    }SeqList;
    
    //初始化
    SeqList * initSeqList() {
        SeqList *list;
        list = (SeqList *)malloc(sizeof(SeqList));
        list->length = 0;
        return list;
    }
    
    //插入
    void insertSeqList(SeqList list,ElementType data,int i) {
        if (i > list.length || i < 1) {
            return;
        }
        for (int j = list.length; j>=i; j--) {
            list.data[j] = list.data[j-1];
        }
        list.data[i-1] = data;
        list.length++;
    }
    
    //删除
    void deleteSeqList(SeqList list,int i) {
        if (i > list.length || i < 1) {
            return;
        }
        for (int j = i; j<list.length; j++) {
            list.data[j] = list.data[j-1];
        }
        list.length--;
    }
    
    //定位 找出表中相等于参数的最小下标值
    int locationSeqList(SeqList list,ElementType x) {
        int i = 0;
        while (i<list.length && list.data[i] != x) {
            i++;
        }
        if (i<list.length) {
            return i+1;
        }
        return 0;
    }
    
    线性表的链式存储

    定义:线性表的链式存储是指存储结构是链式的。常见的链式存储有单链表、循环链表和双向链表。

    单链表:单链表是由一个数据域和指针域组成的一个节点集合。data部分称为数据域,用于存放线性表元素;next部分称为指针域,存放一个指针,该指针指向本结点所含数据元素的直接后继结点,所有结点通过指针链接成链表。

    大多数情况下链表会有一个头节点head,head的next指针指向链表的第一个结点,称为首节点,最后一个结点称为尾结点,首尾之间的结点称为表结点;

    一些基本运算:

    结构体定义

    typedef struct {
        int age;
        int score;
    }DataType;
    
    typedef struct node {
        DataType data;//数据域
        struct node *next;//指针域
    }Node,*linkList;
    
    Node head;//头结点
    

    初始化

    linkList initLinkList() {
        linkList head;
        head = malloc(sizeof(Node));
        head->next = NULL;
        return head;
    }
    

    求表长

    int lengthLinkList(linkList head) {
        int count = 0;
        linkList first = head->next;
        while (first->next != NULL) {
            count++;
            first = first->next;
        }
        return count;
    }
    

    读表元素 给定序号i 求表head第i个元素

    Node * getLinkList(linkList head,int i) {
        Node *p;
        p = head->next;//指向首节点
        int k = 1;
        while (k < i && p != NULL) {
            k++;
            p = p->next;
        }
        if (k == i) {
            return p;
        } else {
            return NULL;
        }
    }
    

    定位 给定元素 求index

    int locationLinkList(linkList head,DataType x) {
        Node *p = head->next;
        int k = 0;
        while (p != NULL && p->data.num != x.num) {
            k++;
            p = p->next;
        }
        if (p!=NULL) {
            return k+1;
        }
        return 0;
    }
    

    插入 单链表的插入是将给定值为x的元素插入到head链表 i 序号之前

    void insertLinkList(linkList head,DataType x,int i) {
        Node *insert,*q;
        if (i == 1) {
            q = head;
        } else {
            q = getLinkList(head, i-1);//找到直接前驱
        }
        if (q == NULL) {
            printf("位置不对");
        } else {
            insert = (Node *)malloc(sizeof(Node));
            insert->data = x;
            insert->next = q->next;
            q->next = insert;
        }
    }
    

    删除 给定一个值,从链表中删除i序号的元素

    void deleteLinklist(linkList head,int i) {
        //先获取被删除元素的直接前驱
        Node *p,*q;
        if (i == 1) {
            q = head;
        } else {
            q = getLinkList(head, i-1);//找到直接前驱
        }
        if (q != NULL && q->next != NULL) {
            p = q->next;
            q->next = p->next;
            free(p);//释放空间
        } else {
            printf("找不到要删除的节点");
        }
    }
    
    单链表的三种创建方式
    方法一

    通过插入算法来实现,依次增大插入位置i 使新的结点链入链表中 时间复杂度为O(n^2)

    linkList createLinkList(void) {
        linkList head;
        int i,x;
        head = initLinkList();
        i = 1;
        scanf("%d",&x);
        while (x != 0) {
            insertLinkList(head, x, i);
            i++;
            scanf("%d",&x);
        }
        return head;
    }
    
    方法二 尾插法

    方法一每次插入元素都要从首结点遍历到尾结点,然后插入元素,所以时间复杂度为O(n^2),可以使用一个指针标明尾结点的位置, 这样每次插入元素就不用遍历整个表 顾时间复杂度为O(n)

    linkList createLinkList2() {
        linkList head;
        head = (Node *)malloc(sizeof(Node));
        Node *tail,*q;
        q = head;//头结点
        int x;
        scanf("%d",&x);
        while (x != 0) {
            tail = (Node *)malloc(sizeof(Node));
            tail->data = x;
            q->next = tail;
            q = tail;//指向尾结点
            scanf("%d",&x);
        }
        q->next = NULL;
        return head;
    }
    
    方法三 头插法 时间复杂度也为O(n)
    linkList createLinkList3() {
        linkList head;
        head = (Node *)malloc(sizeof(Node));
        head->next = NULL;
        Node *p;
        int x;
        scanf("%d",&x);
        while (x != 0) {
            p = malloc(sizeof(Node));
            p->data = x;
            p->next = head->next;//新元素放到第一个,充当首结点
            head->next = p;
            scanf("%d",&x);
        }
        return head;
    }
    
    删除重复数据项

    在线性表中,有可能有多个元素值相同;它们是重复结点,可以删除重复结点只保留index最小的结点

    void deleteRepeatLinkList(linkList head) {
        Node *p,*d,*q;
        q = head->next;
        while (q != NULL) {
            p = q;
            while (p->next != NULL) {
                if (p->next->data == q->data) {
                    d = p->next;//被删除结点
                    p->next = d->next;
                    free(d);
                } else {
                    p = p->next;//指针后移
                }
            }
            q = q->next;//指针后移
        }
    }
    

    线性表的顺序实现、链接实现比较

    顺序实现和链接实现都有各自的优缺点,下面就时间复杂度和空间复杂度展开讨论。

    1. 对于按位置查找,顺序表示随机访问,所以平均时间复杂度为O(1),单链表需要对元素进行扫描,它的平均时间复杂度为O(n);
    2. 对于定位运算,基本操作是比较,需要扫描元素,顺序表和单链表的平均时间复杂度相同O(n);
    3. 对于插入和删除操作,在顺序表和单链表中都需要定位,在顺序表中,其基本操作是元素比较和结点的移动,平均时间复杂度为O(n),在单链表中,由于需要定位,尽管不需要移动结点,平均时间复杂度同样是O(n);
    4. 单链表的每个结点包含数据域和指针域,指针域需要占用额外空间但不需要预分配空间;
    5. 顺序表需要预分配空间,如果预分配过大,造成空间浪费;若分配的小,将发生空间溢出;

    相关文章

      网友评论

        本文标题:数据结构-线性表

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