数据结构—线性表的实现

数据结构—线性表的实现

昨天开始看《大话数据结构》中的线性表章节,根据物理结构大概分为两种:顺序存储结构和链式存储结构 其中顺序结构数组来实现,在链式存储结构中又 分为 静态链表和动态链表 其中静态链表也是用数组所实现的,再具体还能分成 单链表,循环链表,双向链表,其中单链表在我之前的博客中已经提到过,就不再赘述了,下面 我大概介绍 线性表中的 顺序存储结构,静态链表,和双向循环链表


1.顺序存储结构

定义:用一段地址连续的存储单元依次存放线性表的数据元素。
实现方式:使用以下的结构体来实现顺序存储结构线性表的功能:

#define MAX 20

typedef struct {
    int date[MAX];
    int length;
}SqList;

结构体中包含了两个成员:存放数据的数组,和数据长度。
插入数据元素思路:插入时,先找到插入位置,再从最后一位到这个位置上的数组元素全部向后移动一位,将数据写入这个位置,最后给数据长度加一。时间复杂度O(n)
删除数据元素思路:删除时,找到这个元素的位置,从这个位置开始之后到最后一位中间的元素全部向前移动一位,最后给数据长度减一。时间复杂度O(n)
以下是代码实现:

#include<stdio.h>
#define MAX 20

typedef struct {
    int date[MAX];
    int length;
}SqList;

SqList Creat(SqList list){
    printf("请输入数据元素个数:");
    scanf("%d",&list.length);
    for(int i = 0; i < list.length; i++)
        scanf("%d",&list.date[i]);
    return list;
}

void print_list(SqList list){
    for(int i = 0; i < list.length; i++){
        printf("表中第%d个数据元素:",i+1);
        printf("%d\n",list.date[i]);
    }
}

SqList insert(SqList list){
    if(list.length == 0){
        printf("目前为空表!");
        return list;
    }
    int site;
    int elem;
    printf("请输入要插入的位置:");
    scanf("%d",&site);
    if(site < 1 || site > list.length){
        printf("位置输入有误!");
        return list;
    }
    printf("要插入的数据:");
    scanf("%d",&elem);
    for(int i = list.length-1; i >= site-1; i--)
        list.date[i+1] = list.date[i];
    list.date[site-1] = elem;
    list.length++;
    printf("插入成功!");
    return list;
}

SqList delete_elem(SqList list){
    if(list.length == 0){
        printf("目前为空表!");
        return list;
    }
    int site;
    printf("请输入要删除的位置:");
    scanf("%d",&site);
    if(site < 1 || site > list.length){
        printf("位置输入有误!");
        return list;
    }
    for(int i = site-1; i < list.length; i++)
        list.date[i] = list.date[i+1];
    list.length--;
    return list;
}

int main(){
    SqList list;
    list = Creat(list);
    print_list(list);
    list = insert(list);
    print_list(list);
    list = delete_elem(list);
    print_list(list);
    return 0;
} 

2.静态链表

定义:用数组描述的链表叫做静态链表。
实现方式:使用以下的结构体数组来实现静态链表的功能:

#define MAX 1000

typedef struct{
    int date;
    int cur;
}node,list[MAX];

每个结构体中包含两个成员:date代表数据,cur代表指向,cur拥有类似于单链表中节点指针域的作用,他存放下一个节点的下标
另外,在静态链表中,结构体数组的第一位和最后一个都不存放数据元素,第一位的成员cur代表 备用节点的位置(即还未存放数据的节点的下标)。最后一位的成员cur代表 静态链表的开始节点下标(拥有类似于头节点的作用)。
插入数据元素思路:插入时,新开辟一个节点并存入数据,找到要插入位置的前一个节点,将这个节点中的cur指向最新开辟的这个节点的下标,然后将要插入位置节点的下标赋值给新开辟节点的cur,这样不需要移动大量元素就可以实现新节点的插入。
删除数据元素思路:删除时,先找到要删除节点的位置和要删除节点的前一个节点的位置,将前一个节点的cur指向要删除节点的cur,再释放掉要删除的节点(将其纳入备用节点中,并改变其cur为下一个备用节点下标)。
以下是代码实现:

#include<stdio.h>
#define MAX 1000

typedef struct{
    int date;
    int cur;
}node,list[MAX];                                                                 //静态链表 

void InitList(list space){
    for(int i = 0; i < MAX-1; i++)
        space[i].cur = i+1;
    space[MAX-1].cur = 0;
}

void creat(list space){
    int index;
    int i; 
    printf("请输入数据元素个数:");
    scanf("%d",&index);
    for(i = 1; i <= index; i++)
        scanf("%d",&space[i].date);
    space[i-1].cur = 0;
    space[0].cur = index+1;
    space[MAX-1].cur = 1;
}

void print_list(list space){
    int index = 0;
    int end = MAX-1;
    while(space[end].cur != 0){
        end = space[end].cur;
        printf("第%d个数据元素:",++index);
        printf("%d\n",space[end].date);
    }
}

int malloc_node(list space){
    int i = space[0].cur;
    if(space[0].cur)
        space[0].cur = space[i].cur;

    return i;
}

void insert(list space){
    int site;
    int elem;
    int pre = MAX-1;
    int obj = malloc_node(space);
    printf("请输入要插入的位置:");
    scanf("%d",&site);
    printf("请输入要插入的数据:");
    scanf("%d",&elem);
    if(obj){
        space[obj].date = elem;
        for(int i = 1; i <= site-1; i++)
            pre = space[pre].cur;
        space[obj].cur = space[pre].cur;
        space[pre].cur = obj;
    }
    printf("插入成功!\n");
}

void free_node(list space,int k){
    space[k].cur = space[0].cur;
    space[0].cur = k;
}

void delete_node(list space){
    int site;
    int pre = MAX-1;
    printf("请输入要删除的位置:");
    scanf("%d",&site);
    for(int i = 1; i <= site-1; i++)
        pre = space[pre].cur;
    int now = space[pre].cur;
    space[pre].cur = space[now].cur;
    free_node(space,now);
    printf("删除成功!\n");
}

int main(){
    list space;
    InitList(space);
    creat(space);
    print_list(space);
    insert(space);
    print_list(space);
    delete_node(space);
    print_list(space);
    return 0;
}

3.双向循环链表

定义:每个节点,既有前驱,又有后继,且链表末尾指向头节点的链表。
实现方式:在单链表的前提下为每个节点新增一个指向前一个节点的指针:

struct NODE{
    int date;
    struct NODE *prior;
    struct NODE *next;
};
typedef struct NODE *node;
int count = 0;

在使用双向循环链表后,许多操作会方便很多,因为现在既可以向后遍历,也可以向前遍历,头节点和尾节点相连更是提高了算法的时间性能。说白了就是用空间来换时间
插入,删除数据思路与单链表相同
以下是代码实现:

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

struct NODE{
    int date;
    struct NODE *prior;
    struct NODE *next;
};
typedef struct NODE *node;
int count = 0;

node Creat(node pHead){
    int index;
    node pNew = NULL,pEnd = NULL;
    pHead = (node)malloc(sizeof(struct NODE));
    pHead->next = pHead;
    pHead->prior = pHead;
    pEnd = pHead;
    printf("请输入数据元素个数:");
    scanf("%d",&index);
    for(int i = 0; i < index; i++){
        count++;
        pNew = (node)malloc(sizeof(struct NODE));
        scanf("%d",&pNew->date);
        pNew->next = pHead;
        pHead->prior = pNew;
        pEnd->next = pNew;
        pNew->prior = pEnd;
        pEnd = pNew;
    }
    return pHead;
}

void print_list(node pHead){
    int choice;
    int index = 0;
    node pRear,pTemp;
    pRear = pHead->prior;
    pTemp = pHead->next;
    printf("请选择1.正序输出  或  2.倒序输出\n");
    scanf("%d",&choice);
    if(choice == 1){
        while(pTemp != pHead){
            printf("第%d个数据元素:",++index);
            printf("%d\n",pTemp->date);
            pTemp = pTemp->next;
        }
    }
    else if(choice == 2){
        while(pRear != pHead){
            printf("第%d个数据元素:",count-index++);
            printf("%d\n",pRear->date);
            pRear = pRear->prior;
        }
    }
}

node insert(node pHead){
    int site;
    node pNew,pTemp,pPre;
    pTemp = pHead->next;
    printf("请输入要插入的位置:");
    scanf("%d",&site);
    for(int i = 1; i < site; i++)
        pTemp = pTemp->next;
    pNew = (node)malloc(sizeof(struct NODE));
    printf("请输入要插入的数据:");
    scanf("%d",&pNew->date);
    pPre = pTemp->prior;
    pNew->prior = pPre;
    pNew->next = pTemp;
    pTemp->prior = pNew;
    pPre->next = pNew;
    count++;
    printf("插入成功!\n");
    return pHead;
}

node delete_node(node pHead){
    int site;
    node pTemp,pPre;
    pTemp = pHead->next;
    printf("请输入要删除的位置:");
    scanf("%d",&site);
    for(int i =1; i < site;i++)
        pTemp = pTemp->next;
    pPre = pTemp->prior;
    pPre->next = pTemp->next;
    pTemp->next->prior = pPre;
    free(pTemp);
    printf("删除成功!\n");
    return pHead;
}

int main(){
    node pHead = NULL;
    pHead = Creat(pHead);   
    print_list(pHead);
    pHead = insert(pHead);
    print_list(pHead);
    delete_node(pHead);
    print_list(pHead);
    return 0;
}

以上介绍了三种线性表的定义和实现方法,其各自有各自的优缺点,顺序存储结构的线性表,在查找数据时效率高,直接使用下标实现,时间复杂度为O(1),但在插入和删除操作中,需要移动数组中大量元素,时间复杂度为O(n)。对于静态链表,虽然它在插入和删除操作中不需要移动元素,但是它不能动态分配存储空间,可能会造成空间资源浪费 或 数据溢出。 使用动态链表可以进行对内存的动态分配,并且使用双向循环链表更可以减少遍历所需要的时间。但是其查找的时间复杂度仍是O(n),总之,各种结构都有特点,对于插入或删除操作越频繁的操作,链表的优势就越明显。

猜你喜欢

转载自blog.csdn.net/wintershii/article/details/80245617