C语言超详细讲解数据结构中的线性表

 更新时间:2022年05月18日 10:04:33   作者:对象new不出来  
线性表,数据结构中最简单的一种存储结构,专门用于存储逻辑关系为"一对一"的数据。线性表是基于数据在实际物理空间中的存储状态,又可细分为顺序表(顺序存储结构)和链表

前言

计算机专业都逃不了数据结构这门课,而这门课无疑比较难理解,所以结合我所学知识,我准备对顺序表做一个详细的解答,为了避免代码过长,采用分文件编写的形式,不仅可以让代码干净利落还能提高代码可读性,先解释部分代码的含义,最后放上代码实现和效果图,让我们开始操作吧!!!

一、分文件编写

1、分文件编写概念

在Visual Stdio 编译器中我们可以通过创建.h头文件和.cpp源文件来实现程序运行,使代码更美观,可读性高,如图所示:

SqList.h 头文件 和 Sq.List.cpp 源文件分别存放全局变量、结构体及函数的声明 和 对应函数的完整实现代码。我们需要注意的是头文件和源文件的名称要一致,而且源文件要引用头文件(#include"SqList.h"),使用""而不用<>的原因是头文件是我们自己写的,只能用""引用。

2、代码展示

SqList.cpp内容如下:

tips: #pragma once 代码是为了避免重复引入头文件,我们稍作记忆即可

#pragma once
#include<stdio.h>  
#include<stdlib.h>  
#include<malloc.h>
#define LIST_INIT_SIZE  10  
#define LISTINCREMENT   10    
#define OK              1  
#define ERROR           0  
typedef struct SqList {
    int* elem;
    int len;
    int size;
}SqList;
int InitList_Sq(struct SqList* L);//初始化顺序表
int ListInsert_Sq(struct SqList* L, int i, int e);// 向顺序表中插入数据
int ListDelete_Sq(struct SqList* L, int i, int* e);//删除顺序表中的数据  
void ListShow_Sq(struct SqList* L, const char* s);//输出顺序表中的数据  
void DestroyList(SqList* L);//销毁表

SqList.cpp部分内容如下:

#include"SqList.h"
int InitList_Sq(struct SqList* L)
{
    L->elem = (int*)malloc(LIST_INIT_SIZE * sizeof(int));
    if (!L->elem)exit(0);
    L->len = 0;
    L->size = LIST_INIT_SIZE;
    return OK;
}

二、动态分布内存malloc

1、初识malloc

C语言中malloc是动态内存分配函数。

函数原型:void *malloc(unsigned int num_bytes);

参数:num_bytes无符号整型,用于表示分配的字节数。

返回值:如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。void* 表示未确定类型的指针,void *可以指向任何类型的数据,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int等等)

2、使用方法

typedef struct SqList {
    int* elem;
    int len;
    int size;
}SqList;
int InitList_Sq(struct SqList* L)
{
    L->elem = (int*)malloc(LIST_INIT_SIZE * sizeof(int));
    if (!L->elem)exit(0);
    L->len = 0;
    L->size = LIST_INIT_SIZE;
    return OK;
}

我们可以看到此行代码"L->elem = (int*)malloc(LIST_INIT_SIZE * sizeof(int));"这里的L->elem就是形参结构体变量L调用int * elem 属性,因此malloc需要返回(int *)类型的指针,同时malloc右边括号放的是内存空间,大小就是宏定义的数值乘以整型(int)所占字节数,在这里说白了就是10*4个字节。模板可以这样看:(分配类型 *)malloc(分配元素个数 *sizeof(分配类型)) 如果成功,则返回该空间首地址,该空间没有初始化,如果失败,则返回0

三、创建链表并进行增删操作

1、初始化链表

int InitList_Sq(struct SqList* L)

{
    L->elem = (int*)malloc(LIST_INIT_SIZE * sizeof(int));
    if (!L->elem)exit(0);
    L->len = 0;
    L->size = LIST_INIT_SIZE;
    return OK;
}

首先为 int*elem分配内存空间,如果失败返回零,成功就返回内存空间首地址,并把链表长度置为零,链表最大长度设为 LIST_INIT_SIZE(大小为10)

2、在链表中增加数据

int ListInsert_Sq(struct SqList* L, int i, int e)
{
    if (i<0 || i>L->len)
        return ERROR;
    if (L->len >= L->size) {
        int* newbase = (int*)realloc(L->elem, 
        (LIST_INIT_SIZE + LISTINCREMENT) * sizeof(int));
        if (!newbase)exit(0);
        L->size += LISTINCREMENT;
    }
    int* q = &(L->elem[i]);
    *q = e;
    L->len++;
    return OK;
}

形参 i 对应L->len 也就是初始长度 ,e 对应插入的值,只看第一个if条件我们会觉得条件永远成立,实际上下面插入数据后会进行加一操作,因此插入数据只能挨个插入;第二个if不难理解,如果链表长度达到最大长度,进行空间扩容,从而可以插入更多数据;后面其实是尾插法,让*q指向链表的最后一个位置,把数据放到里面,然后长度加一,插入数据结束。

3、删除链表中指定位置数据

int ListDelete_Sq(struct SqList* L, int i, int* e) {
    if (i<1 || i>L->len) return ERROR;
    int* p = &(L->elem[i - 1]);
    *e = *p;
    int* q = L->elem + L->len - 1;
    for (++p; p <= q; ++p)
        *(p - 1) = *p;
    L->len--;
    return OK;
}

这里 i 代表链表中的位置,*e 是该位置的数据,这样我们就能知道删除元素的值了,然后我定义*q为链表中最后一个元素的地址,随后重复让链表删除位置后的元素前移,最后链表总长度减一,删除结束。修改链表利用插入和删除操作结合就可以完成,这里没有单独定义方法,具体内容会在下面的总代码体现。

四、代码展示与运行效果

1、代码展示

//1、SqList.h:
#pragma once
#include<stdio.h>  
#include<stdlib.h>  
#include<malloc.h>
#define LIST_INIT_SIZE  10  
#define LISTINCREMENT   10    
#define OK              1  
#define ERROR           0  
typedef struct SqList {
    int* elem;
    int len;
    int size;
}SqList;
int InitList_Sq(struct SqList* L);//初始化顺序表
int ListInsert_Sq(struct SqList* L, int i, int e);// 向顺序表中插入数据
int ListDelete_Sq(struct SqList* L, int i, int* e);//删除顺序表中的数据  
void ListShow_Sq(struct SqList* L, const char* s);//输出顺序表中的数据  
void DestroyList(SqList* L);//销毁表
//2、SqList.cpp
#include"SqList.h"
int InitList_Sq(struct SqList* L)
{
    L->elem = (int*)malloc(LIST_INIT_SIZE * sizeof(int));
    if (!L->elem)exit(0);
    L->len = 0;
    L->size = LIST_INIT_SIZE;
    return OK;
}
int ListInsert_Sq(struct SqList* L, int i, int e)
{
    if (i<0 || i>L->len)
        return ERROR;
    if (L->len >= L->size) {
        int* newbase = (int*)realloc(L->elem, (LIST_INIT_SIZE + LISTINCREMENT) * sizeof(int));
        if (!newbase)exit(0);
        L->size += LISTINCREMENT;
    }
    int* q = &(L->elem[i]);
    *q = e;
    L->len++;
    return OK;
}
int ListDelete_Sq(struct SqList* L, int i, int* e) {
    if (i<1 || i>L->len) return ERROR;
    int* p = &(L->elem[i - 1]);
    *e = *p;
    int* q = L->elem + L->len - 1;
    for (++p; p <= q; ++p)
        *(p - 1) = *p;
    L->len--;
    return OK;
}
void ListShow_Sq(struct SqList* L, const char* s) {
    printf("%s", s);
    int i;
    for (i = 0; i < L->len; i++) {
        printf("%d ", L->elem[i]);
    }
    putchar('\n');
}
void DestroyList(SqList* L)
{
    free(L->elem);
    L->elem = NULL;
    L->len = 0;
    L->size = 0;
}
//3、链表操作.cpp
#include"SqList.h"
void mainview_user()//界面函数
{
    struct SqList L;
    InitList_Sq(&L);
    int c;
    printf("     ------------------------------------\n");
    printf("      |**********线性表***************|\n");
    printf("      |********1   输入数据***********|\n");
    printf("      |********2   查看数据***********|\n");
    printf("      |********3   删除数据***********|\n");
    printf("      |********4   改数据    *********|\n");
    printf("      |********5   插入数据***********|\n");
    printf("      |********0   退出系统***********|\n");
    printf("     ------------------------------------\n");
    printf("\n");
    while (1)
    {
        printf("请选择:");
        scanf_s("%d", &c);
        switch (c)
        {
        case 1: {
            int n = 0;
            printf("输入要插入的数据个数:");
            scanf_s("%d",&n);
            for (int i = 0; i < n; i++) {
                int t;
                scanf_s("%d", &t);
                ListInsert_Sq(&L, L.len, t);
            }
        }break;
        case 2: {
            ListShow_Sq(&L, "现在的数据为:");
            system("pause"); break;
        }
        case 3: {
            int s, v;
            printf("请输入数据删除的位置s :");
            scanf_s("%d", &s);
            if (ListDelete_Sq(&L, s, &v))
                printf("删除成功.删除的数据是:%d\n", v);
            else
                printf("删除失败.位置有误.");
            break;
        }
        case 4: {
            printf("请输入想要修改的位置:");
            int s, v;
            scanf_s("%d", &s);
            if (s<1 || s>L.len)
                printf("数据非法");
            else {
                ListDelete_Sq(&L, s, &v);
                printf("请输入修改的数据:");
                scanf_s("%d", &v);
                ListInsert_Sq(&L, s-1, v);
                ListShow_Sq(&L, "修改后为:");
            }
            break;
        case 5: {
            int i, b;
            printf("输入插入的位置:");
            scanf_s("%d", &i);
            if (i<1 || i>L.len)
                printf("数据非法");
 
            else {
                printf("插入的元素:");
                scanf_s("%d", &b);
                ListInsert_Sq(&L, i, b);
                ListShow_Sq(&L, "插入后的数据为:");
                break;
            }
        }
        case 0: {
            DestroyList(&L);
            return;
        }
        default:printf("输入错误,请重新输入!\n"); system("pause"); printf("请重新选择:"); scanf_s("%d", &c);
        }
              system("PAUSE");
        }
    }
}
int main()
{
    mainview_user();
}

2、运行效果

总结

这个案例充分包含了结构体与函数、指针、地址传递、链表的知识,非常适合大家的进阶,何不静下心来自己来实现这个程序呢,有什么问题一定私信我,我也不敢确保这个程序没有bug,如果有高见,共同交流,进步,感谢支持!!!

到此这篇关于C语言超详细讲解数据结构中的线性表的文章就介绍到这了,更多相关C语言线性表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ Boost Coroutine使用协程详解

    C++ Boost Coroutine使用协程详解

    通过Boost.Coroutine,可以在C++中使用协程。协程是其他编程语言的一个特性,通常使用关键字yield来表示协程。在这些编程语言中,yield可以像return一样使用
    2022-11-11
  • C语言数据结构之堆排序的优化算法

    C语言数据结构之堆排序的优化算法

    堆排序Heap Sort就是利用堆进行排序的方法,下面这篇文章主要给大家介绍了关于C语言数据结构之堆排序的优化算法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-04-04
  • C语言实现扫雷游戏及其优化

    C语言实现扫雷游戏及其优化

    这篇文章主要为大家详细介绍了C语言实现扫雷游戏及其优化,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • C++实现页面的缓冲区管理器

    C++实现页面的缓冲区管理器

    这篇文章主要介绍了C++实现页面的缓冲区管理器,文章围绕主题展开详细的内容介绍具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • C++利用两个栈实现队列的方法

    C++利用两个栈实现队列的方法

    这篇文章主要给大家介绍了关于C++利用两个栈实现队列的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • c++ 类中const成员变量的赋值方法

    c++ 类中const成员变量的赋值方法

    下面小编就为大家带来一篇c++ 类中const成员变量的赋值方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • 详解约瑟夫环问题及其相关的C语言算法实现

    详解约瑟夫环问题及其相关的C语言算法实现

    这篇文章主要介绍了详解约瑟夫环问题及其相关的C语言算法实现,也是ACM当中经常会引用到的基础题目,文中共介绍了三种C语言解答,需要的朋友可以参考下
    2015-08-08
  • 实现Dijkstra算法最短路径问题详解

    实现Dijkstra算法最短路径问题详解

    这篇文章主要介绍了实现Dijkstra算法最短路径问题详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C语言库函数strcpy的使用及模拟实现

    C语言库函数strcpy的使用及模拟实现

    本文主要介绍了C语言库函数strcpy的使用及模拟实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04
  • C++中引用的相关知识点小结

    C++中引用的相关知识点小结

    引用是C++一个很重要的特性,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价,这篇文章主要给大家总结介绍了C++中引用的相关知识点,需要的朋友可以参考下
    2022-03-03

最新评论