三、数据结构——顺序表的基本操作详解:创建、插入(头插法、尾插法、任意点插法)、删除(头删法、尾删法、任意点删法)、查询(按值查、按下标查、指定位置查)、遍历顺序表和清空顺序表

数据结构之顺序表:从内存角度解析线性表的基本实现

————最后附有全部代码 ————

数据结构是计算机科学中的重要概念,它研究数据的组织、存储和操作方式。其中,顺序表是一种常见的线性表数据结构,它以连续的内存空间存储元素,并通过下标进行访问。本文将详细介绍顺序表的概念、基本实现以及优缺点,帮助读者深入理解顺序表的内部原理和使用方法。

1.顺序表的概念

顺序表是一种线性表的存储方式,它通过连续的内存空间存储元素。顺序表中的元素在内存中按照顺序排列,可以通过下标访问元素。顺序表具有固定的容量,一旦达到容量上限,需要进行扩容操作。

2.顺序表的基本实现

将完整程序分成三个程序来实现,①SeqList.h ②SeqList.c ③SeqList_main.c

①初始化顺序表
②判断顺序表是否已满
③判断顺序表是否为空
④插入元素(1.在头节点插入、2.在尾节点插入、3.在任意位置插入)
⑤删除元素(1.删除头节点、2.删除尾节点、3.删除任意节点)
⑥查找元素(1.按值查找、2. 按下标查找)
⑦修改元素(通过下标访问元素并更新该值)
⑧获取顺序表长度
⑨遍历顺序表
⑩清空表

具体分析

①初始化顺序表
在这里插入图片描述
②判断顺序表是否已满

③判断顺序表是否为空

④插入元素(1.在头节点插入、2.在尾节点插入、3.在任意位置插入)
1.在头节点插入
在这里插入图片描述

2.在尾节点插入
在这里插入图片描述

3.在任意位置插入
在这里插入图片描述

⑤删除元素(1.删除头节点、2.删除尾节点、3.删除任意节点)
1.删除头节点
在这里插入图片描述

2.删除尾节点
在这里插入图片描述

3.删除任意节点
在这里插入图片描述

⑥查找元素(1.按值查找、2. 按下标查找)
1.按值查找
在这里插入图片描述

2.按下标查找
在这里插入图片描述

⑦修改元素(通过下标访问元素并更新该值)
在这里插入图片描述

⑧获取顺序表长度

⑨遍历顺序表

⑩清空表
在这里插入图片描述

3.优点

随机访问性:顺序表通过下标进行元素访问,具有高效的随机访问性能。
修改、和查询算法比较简单
内存连续性:顺序表使用连续的内存空间存储元素,对于大规模数据的处理更加高效。

4.缺点

容量限制:顺序表的容量是固定的,一旦达到容量上限,需要进行扩容操作,可能涉及数据的搬迁和内存重新分配。

插入和删除操作的效率低:在顺序表中插入或删除元素时,需要移动其他元素的位置,导致时间复杂度较高。

5.总结

顺序表作为一种常见的线性表数据结构,通过连续的内存空间存储元素,提供了高效的随机访问能力。在插入和删除操作上可能存在效率低下和容量限制的问题,因此在实际应用中需要根据具体需求选择合适的数据结构。通过了解顺序表的基本实现和特点,我们能够更好地理解其内部原理,并能够灵活应用于实际开发中。

①SeqList.h

#ifndef _SEQLIST_H_
#define _SEQLIST_H_
#include <stdio.h>
#include <stdlib.h>
//声明一个顺序表结构体
typedef struct SeqList {
    int data;//数据域
    int tail;//记录节点个数
    int capacity;//记录表容量
    struct SeqList *next;//指针域
} SeqList, *PSeqList;

// 初始化顺序表
void init(PSeqList *list, int capacity);

// 判断顺序表是否为空
int isEmpty(PSeqList list);

// 判断顺序表是否已满
int isFull(PSeqList list);

// 在头部插入元素
void insertAtHead(PSeqList *list, int value);

// 在尾部插入元素
void insertAtTail(PSeqList *list, int value);

// 在任意下标插入元素
void insertAtPosition(PSeqList *list, int position, int value);

// 删除头节点
void deleteAtHead(PSeqList *list);

// 删除尾节点
void deleteAtTail(PSeqList *list);

// 删除任意下标的节点
void deleteAtPosition(PSeqList *list, int position);

// 按值查找元素的下标
int searchByValue(PSeqList list, int value);

// 按下标查找元素的值
int searchByIndex(PSeqList list, int position);

// 修改指定下标的元素值
void modify(PSeqList list, int position, int value);

// 获取顺序表的长度
int getLength(PSeqList list);

// 遍历顺序表
void traverse(PSeqList list);

//释放顺序表内存
void freeList(PSeqList *list);

#endif

②SeqList.c

#include "Seqlist.h"
// 初始化顺序表
void init(PSeqList *list, int capacity){
    *list = (PSeqList)malloc(sizeof(SeqList));
    if(NULL == *list){
        perror("init malloc");
        return ;
    }   
    (*list)->tail = 0;
    (*list)->capacity = capacity;
    (*list)->next = NULL;

}

// 判断顺序表是否为空
int isEmpty(PSeqList list){
    //容错判断
    if(NULL == list){
        puts("isEmpty arg err!");
        return -1; 
    }   
    //判断头节点的下一个节点是否为空
    if(list == NULL || list->tail == 0){ 
        return 1;//真空
    }else{
        return 0;//非空
    }   
}

// 判断顺序表是否已满
int isFull(PSeqList list){
    if(NULL == list){
        puts("isFull arg err");
        return -1; 
    }   
    if(list == NULL || list->tail == list->capacity){
        puts("顺序表已满!");
        return 1;//顺序表已满
    }else{
        return 0;//顺序表未满
    }   
}

// 在头部插入元素
void insertAtHead(PSeqList *list, int value){
    //容错判断
    if(NULL == *list){
        puts("insert head arg err!");
        return ;
    }   
    //判满
    if(isFull(*list)){
        //puts("表已满");
        return ;
    }   
    PSeqList newNode = (PSeqList)malloc(sizeof(SeqList));//开辟一个新节点newNode
    if(newNode == NULL){
        puts("insert head malloc");
        return ;
    }   

    newNode->data = value;//将新值赋值给新节点的数据区
    newNode->next = (*list)->next;//先将新节点的下一个节点连接好
    (*list)->next = newNode;//再将头节点连接到新节点上

    (*list)->tail++;//表中的节点个数加一


}

// 在尾部插入元素
void insertAtTail(PSeqList *list, int value){
    //容错判断
    if(NULL == *list){
        puts("insert tail arg err!");
        return ;
    }
    //判满
    if(isFull(*list)){
        //puts("表已满");
        return ;
    }
    PSeqList newNode = (PSeqList)malloc(sizeof(SeqList));//开辟一个新节点newNode
    if(newNode == NULL){
        perror("insert tail malloc");
        return ;
    }

    newNode->data = value;//赋值新值
    newNode->next = NULL;//在尾巴上插入,则该节点的下一个节点就是NULL,先连接上;
    PSeqList current = *list;//重新定义一个新的结构体指针current指向头节点

    //循环判断插入的位置,找到表的尾节点
    while(current->next != NULL){//current的下一个节点不为NULL,则循环,直到找到一个节点的下一个节点是NULL为止
        current = current->next;//current指针不断向后移一位
    }
    current->next = newNode;//current指针此时的位置是尾节点,将它指向新开辟的newNode节点

    (*list)->tail++;//插入后表的节点数加一

}

// 在任意位置插入元素
void insertAtPosition(PSeqList *list, int position, int value){
    //容错判断
    if(NULL == *list){
        puts("insert position arg err!");
        return ;
    }
    //判满
    if(isFull(*list)){
        //puts("表已满");
        return ;
    }
    //判断插入的位置是否有效
    if(position < 0 || position > (*list)->tail + 1){//位置在头节点、小于头节点或位置大于表的长度
         puts("没有该下标,位置无效!");
         return ;
    }

    PSeqList newNode = (PSeqList)malloc(sizeof(SeqList));//开辟一个新节点newNode
    if(newNode == NULL){
        perror("insert position malloc");
        return ;
    }

    newNode->data = value;//赋值新值
	
    PSeqList current = *list;//重新定义一个新的结构体指针current指向头节点
    //遍历position位置
    for(int i = 0; i < position - 1; i++){
        current = current->next;//current指针往后移动
    }

    newNode->next = current->next;//先将新节点连接插入的位置的下一个节点
    current->next = newNode;//再将新节点与前一节点连接

    (*list)->tail++;//插入后表中的节点数加一

}

// 删除头节点
void deleteAtHead(PSeqList *list){
    //容错判断
    if(NULL == *list){
        puts("delete head arg err!");
        return ;
    }
    //判断表是否为空
    if(isEmpty(*list)){
        puts("表是空的,无法删除!");
        return ;
    }

    PSeqList current = (*list)->next;//重新定义一个新的结构体指针current指向头节点的下一个节点(就是定义current记录将要删除的节点
    (*list)->next = current->next;//直接断掉current节点,将头节点指向NULL

    free(current);//释放删除的节点current的内存

    (*list)->tail--;//删除节点后表的节点数减一

}

// 删除尾节点
void deleteAtTail(PSeqList *list){
    //容错判断
    if(NULL == *list){
        puts("delete head arg err!");
        return ;
    }
    //判断表是否为空
    if(isEmpty(*list)){
        puts("表是空的,无法删除!");
        return ;
    }

    PSeqList current = *list;//重新定义一个新的结构体指针current指向头节点
    //寻找尾节点
    while(current->next->next != NULL){//如果current != NULL
        current = current->next;//current指针往后
    }

    PSeqList tailNode = current->next;//重新定义一个新的节点tail指向被删除节点(current是被删除节点的前一节点)
    current->next = NULL;//断开后重新连接
    free(tailNode);//释放已被删除的节点内存

    (*list)->tail--;//节点数减一
}

// 删除任意位置的节点
void deleteAtPosition(PSeqList *list, int position){
    //容错判断
    if(NULL == *list){
        puts("delete position arg err!");
        return ;
    }
    //判断表是否为空
    if(isEmpty(*list)){
        puts("表是空的,无法删除!");
        return ;
    }

    PSeqList current = *list;//重新定义一个新的结构体指针current指向头节点

    for(int i = 0; i < position; i++){
        current = current->next;
    }

    PSeqList deleteNode = current->next;//
    current->next = deleteNode->next;//

    free(deleteNode);
    (*list)->tail--;
}

// 按值查找元素的下标
int searchByValue(PSeqList list, int value){
    //容错判断
    if(NULL == list){
        puts("ceach ByValue arg err!");
        return -1;
    }

    PSeqList current = list->next;//重新定义一个新的结构体指针current指向头节点的下一个节点
    int position = 0;//定义一个下标position初始化为0
    while(current != NULL){//节点存在时进入while循环
        if(current->data == value){//如果节点的数据==查询的的数据
            return position;//返回对应节点下标
        }
        current = current->next;//节点往后移动
        position++;
    }
    return -1;//未找到

}

// 按下标查找元素的值
int searchByIndex(PSeqList list, int position){
    //容错判断
    if(NULL == list){
        puts("ceach ByIndex arg err!");
        return -1;
    }

    if(position < 0 || position > list->tail){
        puts("下标无效!");
        return -1;
    }

    PSeqList current = list->next;//

    for(int i = 0; i < position; i++){
        current = current->next;
    }
	
    return current->data;//将返回查找到的下标的对应的值

}

// 修改指定下标的元素值
void modify(PSeqList list, int position, int value){
    //容错判断
    if(NULL == list){
        puts("modify arg err!");
        return ;
    }
    //判断表是否为空
    if(isEmpty(list)){
        puts("表是空的,无法修改!");
        return ;
    }

    if(position < 0 || position > list->tail){
        puts("下标无效!");
        return ;
    }

    PSeqList current = list->next;//重新定义一个新的结构体指针current指向头节点的下一个节点
    for(int i = 0; i < position; i++){
        current = current->next;
    }

    current->data = value;
}

// 获取顺序表的长度
int getLength(PSeqList list){
    //容错判断
    if(NULL == list){
        puts("getLength arg err!");
        return -1;
    }
    //判断表是否为空
    if(isEmpty(list)){
        puts("表是空的,长度为0");
        return -1;
    }
    return list->tail;//直接返回节点数即表的长度

}

// 遍历顺序表
void traverse(PSeqList list){
    //容错判断
    if(NULL == list){
        puts("traverse arg err!");
        return ;
    }
    //判断表是否为空
    if(isEmpty(list)){
        puts("表是空的!");
        return ;
    }

    PSeqList current = list->next;
	    while(current != NULL){
        printf("%d ",current->data);
        current = current->next;
    }
    printf("\n");

}


//释放顺序表内存(清空)
void freeList(PSeqList *list){
    //容错判断
    if(NULL == *list){
        puts("freeList arg err!");
        return ;
    }
    PSeqList current = (*list)->next;
    while(current != NULL){
        PSeqList temp = current;
        current = current->next;
        free(temp);
    }
    free(*list);
    *list = NULL;
}

③Seqlist_main.c

#include "Seqlist.h"
#include "Seqlist.c"

int main(int argc, char *argv[])
{
    PSeqList list;
    int choice, value, position, capacity;

    printf("请输入顺序表的大小:");
    scanf("%d",&capacity);

    init(&list, capacity);

    while (1) {
        printf("\n*****************************顺序表操作菜单*****************************\n");
        printf("1. 在头部插入元素在         2. 尾部插入元素         3. 在任意下标插入元素\n");
        printf("4. 删除头节点               5. 删除尾节点           6. 删除任意下标的节点\n");
        printf("7. 按值查找元素的下标       8. 按下标查找元素的值   9. 修改指定下标的元素值\n");
        printf("10. 获取顺序表的长度        11. 遍历顺序表          12. 清空表并退出程序\n");
        printf("*****************************顺序表操作菜单*****************************\n");

        printf("请输入操作编号:");
        scanf("%d", &choice);

        switch (choice) {
            case 1:
                printf("请输入要插入的元素值:");
                scanf("%d", &value);
                insertAtHead(&list, value);
                if(!isFull(list)){
                    printf("在头部插入元素 %d\n", value);
                    break;
                }else{
                    printf("好了!顺序表已经被你填满了!\n");
                }
                break;
            case 2:
                printf("请输入要插入的元素值:");
                scanf("%d", &value);
                insertAtTail(&list, value);
                if(!isFull(list)){
                    printf("在尾部插入元素 %d\n", value);
                    break;
                }else{
                    printf("好了!顺序表已经被你填满了!\n");
                }
                break;
            case 3:
                printf("请输入要插入的下标:");
                scanf("%d", &position);
                printf("请输入要插入的元素值:");
                scanf("%d", &value);
                insertAtPosition(&list, position, value);
                if(!isFull(list)){
                    printf("在下标 %d 插入元素 %d\n", position, value);
                    break;
                }else{
                    printf("好了!顺序表已经被你填满了!\n");
                }
                break;

           case 4:
                deleteAtHead(&list);
                if(!isEmpty){
                    printf("已经删除头节点\n");
                    break;
                }
                break;
            case 5:
                deleteAtTail(&list);
                if(!isEmpty){
                    printf("已经删尾节点\n");
                    break;
                }
                break;
            case 6:
                printf("请输入要删除的下标:");
                scanf("%d", &position);
                deleteAtPosition(&list, position);
                break;
            case 7:
                printf("请输入要查找元素下标的值: ");
                scanf("%d", &value);
                position = searchByValue(list, value);
                if (position != -1) {
                    printf("%d 值的下标是: %d\n", value, position);
                } else {
                    printf("在列表中没有找不到 %d\n", value);
                }
                break;
            case 8:
                printf("请输入要查找元素的下标: ");
                scanf("%d", &position);
                value = searchByIndex(list, position);
                if (value != -1) {
                    printf("该下标 %d 的值是 %d\n", position, value);
                } else {
                    printf("无效下标!\n");
                }
                break;
            case 9:
                printf("请输入要修改的元素下标: ");
                scanf("%d", &position);
                printf("请输入新的值: ");
                scanf("%d", &value);
                modify(list, position, value);
                break;
            case 10:
                printf("该表的长度为 %d\n", getLength(list));
                break;
            case 11:
                printf("表的所有元素为: ");
                traverse(list);
                break;
            case 12:
                freeList(&list);
                printf("表已被清空...\n");
                exit(0);
            default:
                printf("输入无效!请按照菜单选择!\n");
                break;
        }
    }
    return 0;
}

编译Seqlist_main.c就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小羊客栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值