iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言数据结构不挂科指南之线性表详解
  • 167
分享到

C语言数据结构不挂科指南之线性表详解

2024-04-02 19:04:59 167人浏览 薄情痞子
摘要

目录基本概念线性表的顺序存储线性表的顺序存储的时间复杂度线性表的链接存储线性表在单链表上实现基本运算初始化初始化成功,开始插入元素单链表的时间复杂度循环链表双向循环链表期末考试基本概

基本概念

线性表是由 n(n≥0)个数据元素组成的有穷序列

大白话:在内存上一个个排着,找到一个,剩下的挨着找就行

数据元素又称作结点

吐槽:人类在创造术语的路上就是这么带劲,上节课刚说数据元素又称元素,这又来一个结点,得,记住吧

结点个数叫做表长,那么我们用一张完整的图来说明一下

线性表的基本运算,需要了解一下

  • 初始化 Initiate(L)
  • 求表长 Length(L)
  • 读表元素 Get(L,i)
  • 定位 Locate(L,i)
  • 插入 Insert(L,x,i)
  • 删除 Delete(L,i)

线性表的顺序存储

用顺序存储实现的线性表称为顺序表。一般使用数组来表示顺序表

接下就是刺激的时刻了,比较难的部分来了,因为要用 C 来实现线性表的基本运算

首先假定线性表的数据元素的类型为DataType ,这个DataType 可以是自定义的,也可以是默认的int,char等类型

const int Maxsize = 100 ;  // 预先定义一个足够大的常数
typedef struct{
	DataType data[Maxsize]; // 存放数据的数组
	int length ; // 顺序表的实际长度
} SeqList; // 顺序表类型名为SeqList
SeqList L ;  // 定义一个L为顺序表

实现插入操作,函数名字为InsertSeqList(SeqList L,DataType x,int i) 表示在顺序表第 i(1≤i≤n+1)个元素之前,插入一个新元素。使得线性表长度加 1。

上面是逻辑上的 C 语言实现,接下来咱们先引用一个图,说明一下如何用 C 语言在内存上开辟一块空间,并且向里面存数据

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

const int Maxsize = 10;
typedef struct SeqList{
    int *data; //一个int指针,后面用来初始化数组用
    int length;
} seq;

// 顺序表的初始化函数
seq init(){
    seq s;
    s.data = (int*)malloc(Maxsize*sizeof(int)); // 构造一个空的顺序表,动态申请存储空间
    if(!s.data) // 如果申请失败,退出程序
    {
        printf("初始化失败");
        exit(0);
    }
    s.length = 0; // 空表的长度初始化为0
    return s;
}

上述代码,相当于在内存上做了图示的操作

开辟空间之后,向每个小格子里面添加数字

void display(seq s){
    for(int i=0;i<s.length;i++){
        printf("%d",s.data[i]);
    }
    printf("\n");

}

int main()
{
    seq s = init();
    //添加一个元素进入
    for(int i=1;i<=5;i++){
        s.data[i-1] = i;
        s.length++;
    }
    printf("初始化之后,表的数据为:\n");
    display(s);

    return 0;
}

可以看动画理解

添加元素完成之后,就是删除元素

删除的基本步骤

  • 结点 ai+1,....an依次向左移动一个元素位置
  • 表长度减 1

看一下代码吧

seq delete_seq(seq s,int i){
    if(i<1||i>s.length){
        printf("位置错误");
        exit(0);
    }

    // 第i个元素下标修改为i-1
    for(int j=i;j<s.length;j++){
        s.data[j-1] = s.data[j];
    }

    s.length--;
    return s;
}

接下来实现定位的算法,说白了,就是判断一个值(x)的位置(i)

C 语言的代码如下

// 注意,这个地方需要返回的为int了,也就是位置
int locate(seq s,int x){
    int i =0;
    while((i<s.length)&&(s.data[i]!=x)){
        i++;
    }
    if(i<s.length) return i+1;
    else return -1;
}

线性表的顺序存储的时间复杂度

运算插入删除定位求表长读取元素
时间复杂度O(n)O(n)O(n)O(1)O(1)

具体是怎么来的,需要你自己看看算法的实现喽,通过上述表格知道 顺序表的插入、删除算法在时间性能方面不是很理想,接下来我们就采用线性表的链接存储来看一下,是否存在优化

线性表的链接存储

链式存储结构,上来需要记住有三种常见的 单链表、循环链表、双向循环链表

首先明确,单链表中每个结点由两部分组成

  • data 表示==数据域==
  • next 表示==指针域==或==链域==

一些简单的结点概念

线性表在单链表上实现基本运算

接下来重头戏来了,我们要用代码实现一个简单的单链表

空的单链表由一个头指针和一个头结点组成

初始化

初始化之前,我们需要先用 C 语言定义一个新的结构体

//链表中的每个结点的实现
//包含数据域与指针域
typedef struct node{
	int data;// 数据域,为了计算方便,类型设置为数字
	struct node *next; // 指针域,指向后继元素
} Node,*LinkList;

结构体定义好了之后,就可以开始初始化操作了 头结点初始化其实就是在内存上开辟一块空间,然后将指针域指向 NULL

请看代码,注意返回的是一个指针类型,说白了就是头结点的地址

// 初始化
LinkList init(){
    Node *L; // 定义一个头结点
    L =(LinkList)malloc(sizeof(Node)); //头结点申请地址
    if(L == NULL){
        printf("初始化失败!\n");
        exit(0);
    }
    L->next =NULL;
    return L;
}
复制代码

初始化成功,开始插入元素

插入元素,有头插入、尾插、任意插

先说一下头插,当头结点初始化完毕之后,第一个元素插入进来就比较简单了,看动图

这是插入一个元素,在用头插法插入第二个元素呢?

新生成的 pnew2 首先将自己的指针域指向头结点的指针域pnew2->next = L.next,然后L.next = pnew2 即可

上述的逻辑写成代码如下

// 头插入法
void insert_head(Node *L){
    int i,n,num;  // n表示元素的个数
    Node *pnew;
    printf("请输入要插入的元素个数:n = ");
    scanf("%d",&n);
    for(i=0;i<n;i++){
        printf("请输入第%d个元素: ",i+1);
        scanf("%d",&num);
        pnew = (LinkList)malloc(sizeof(Node));
        pnew->data = num; // 将数字存储到数据域
        pnew->next = L->next; // 指针域指向L(头结点)的指针域
        L->next = pnew; // 头结点指针域指向新结点地址

    }
}

接下来看一下尾插法,其实理解起来也不难,说白了就是在链表后面追加元素即可 !

代码如下,这个地方看一下里面有一个p=L请问直接使用 L 可以吗?为什么不直接用,搞清楚了,你也就明白了

// 尾插法
void insert_tail(Node *L){
    int i,n,num;
    Node *p,*pnew;
    p = L;

    printf("要输入元素的个数:n = ");
    scanf("%d",&n);
    for(i=0;i<n;i++){

        printf("请输入第%d个元素:",i+1);
        scanf("%d",&num);
        pnew = (LinkList)malloc(sizeof(Node));
        if(pnew == NULL){
            printf("初始化失败");
            exit(0);
        }
        pnew->data = num;
        p->next = pnew;
        p = pnew;

    }
    p->next = NULL;

}

剩下的算法实现就比较简单了,例如求表长,通过循环的方式,计算一下即可

//求表长
int get_length(LinkList L){
    LinkList p;
    int length = 0;
    p = L->next;   // p 指向第一个结点
    while(p){
        printf("单链表的数据为%d\n",p->data);
        length++;
        p = p->next;
    }
    return length;
}

读表中的元素

// 读表中的元素
LinkList get_element(LinkList L,int i){
    // 在单链表L中查找第i个结点,若找到,则返回指向该结点的指针,否则返回NULL
    Node *p;
    p = L->next;
    int position = 1;
    while((position<i)&&(p!=NULL)){ // 当未到第i结点且未到尾结点时继续后移
        p = p->next;
        position++;
    }
    if(i==position) return p; //找到第i个结点
    else return NULL;   // 查找失败
}

读取表的元素,还可以按照值去找,返回位置,尝试一下吧,写起来都是比较容易的

int get_element_by_data(LinkList L,int x){
    Node *p;
    p = L->next;
    int i = 0;
    while(p!=NULL && p->data == x){
        p = p->next;
        i++;
    }
    if (p!=NULL) return i+1;
    else return 0;
}

写个复杂点的,在任意位置插入一个元素,这个还是好玩一些的

/在任意位置插入元素,x为要插入的内容,i为插入的位置
void insert(LinkList L,int x,int i){

    Node *p,*q; //p表示要插入的元素,q表示要被插入的元素
    if(i==1) q = L; //如果i==1,那么p=L进行初始化操作
    else q = get_element(L,i-1); // 找到第i-1个元素

    if(q==NULL){
        printf("找不到位置");
        exit(0);
    }
    else{
         p =(LinkList)malloc(sizeof(Node));
         p->data = x;
         p->next = q->next; // 新生成的p指向q的下一个结点
         q->next = p;//q的指针域指向新生成的p

    }
}

简单说明一下吧 大白话为 要在第 i 个位置插入一个元素 x,那么需要找到i-1位置的元素,这个元素叫做 q

让新元素p(数据域为 x,指针域为空)的指针域指向第i 元素,也就是q原先的指针域,==防止丢失掉==

然后在叫q的指针域指向p的地址即可,如果还不明白,看图

对于删除任意位置的节点,这个就要留给你自己了

如果将 ai移除链表,需要找到直接前驱,让直接前驱的指针域指向 ai+1的地址就可以了

记得,通过free(p)释放结点

删除全部结点也需要自己完成一下,尽量把代码写完哦~~~

单链表的时间复杂度

  • insert(LinkList L,int x,int i) 时间复杂度为 O(n^2^)
  • 头插法和尾插法时间复杂度为 O(n)

循环链表

环状链表只需要将表中最后一个结点的指针指向头结点,链表就形成了一个环 如图

循环链表如何想研究一下可以去实现约瑟夫环,由于本教材中不是重点,所以选修即可

双向循环链表

双向循环链表就是在单链表中的每个结点在增加一个指向直接前驱的指针域prior ,这样每个结点就有两个指针了

注意点

  • 双向循环链表是一种对称结构,即可以直接访问前驱结点又可以直接访问后继结点,所以找前驱和后继结点的时间复杂度都是 O(1),也可以得到结论双向循环链表适合应用在需要经常查找结点的前驱和后继场合
  • p = p->prior->next = p->next->prior

教材中重点给出了删除和插入的两个逻辑,我们看一下

// p表示的是待删除的结点
p->prior->next = p->next;
p->next->prior = p->prior;
free(p)

图示如下

大白话 先让 p 等于要删除的结点,然后把 p 删除前,需要将 p 的前驱和后继结点连接起来,刚才的代码干的就是这个事情!

插入逻辑

在 p 所指的结点后面插入一个新结点*t,需要修改四个指针:

t->prior = p;
p->next = t;  // 这两个步骤将t和p连接起来了

t->next = p->next;
p->next->prior = t; //这两个步骤将t和p后继结点连接起来了

期末考试

这章是期末考试或者自考的一个比较重要的考试章节,一般会出现算法设计题,难度系数挺高的

建议,在能力范围内用 C 语言实现顺序表的基本运算,实现单链表的基本运算

以上就是C语言数据结构不挂科指南之线性表详解的详细内容,更多关于C语言数据结构线性表的资料请关注编程网其它相关文章!

--结束END--

本文标题: C语言数据结构不挂科指南之线性表详解

本文链接: https://www.lsjlt.com/news/168524.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言数据结构不挂科指南之线性表详解
    目录基本概念线性表的顺序存储线性表的顺序存储的时间复杂度线性表的链接存储线性表在单链表上实现基本运算初始化初始化成功,开始插入元素单链表的时间复杂度循环链表双向循环链表期末考试基本概...
    99+
    2024-04-02
  • C语言数据结构不挂科指南之队列详解
    目录队列队列基本概念循环队列顺序队列的 C 语言实现链式队列的 C 语言实现自考要点队列 这篇博客主要介绍一下队列的概念,并且采用 C 语言,编写两种存储实现方式:顺序存储和链式存储...
    99+
    2024-04-02
  • C语言数据结构不挂科指南之栈&队列&数组详解
    目录学习目标栈基本概念栈的基本运算栈的顺序实现双栈栈的链接实现考试要点小结学习目标 自考重点、期末考试必过指南,这篇文章让你理解什么是栈、什么是队列、什么是数组 掌握栈、队列的顺序存...
    99+
    2024-04-02
  • C语言数据结构线性表教程示例详解
    目录线性表顺序表线性表 数据结构里我们时常看到什么什么表,线性表是最基本、最简单、也是最常用的一种数据结构,其他各种表的万恶之源就是这个线性表,他是个啥其实顾名思义: 一个线性表是n...
    99+
    2024-04-02
  • C语言数据结构之线性表的链式存储结构
    1.什么是线性表的链式存储结构 —链表 存储结点:包括元素本身的信息,还有元素之间的关系逻辑的信息 这个结点有:数据域和指针域 一个指针域:指向后继结点, 单链表 二个指针域: 指向...
    99+
    2024-04-02
  • C语言超详细讲解数据结构中的线性表
    目录前言一、分文件编写1、分文件编写概念2、代码展示二、动态分布内存malloc1、初识malloc2、使用方法三、创建链表并进行增删操作1、初始化链表2、在链表中增加数据3、删除链...
    99+
    2024-04-02
  • 【Dev-c++】C语言数据结构实验——线性表
    实验一 线性表 一、实验目的     1、深刻理解线性结构的特点,以及在计算机内的两种存储结构。     2、熟练掌握线性表的顺序存储结构和链式存储结构,及其它们的基本操作,重点掌握查找、插入和删除等操作。 二、实验要求     1、认真...
    99+
    2023-10-06
    链表 服务器 java
  • 详解C语言数据结构之栈
    目录栈的链式实现主要内容代码实现:总结栈的链式实现 主要内容 (1) 栈包含7个元素,依次是67,3,88,6,1,7,0,采用尾插入法创建 栈,为该栈设置两个指针,一个bottom...
    99+
    2024-04-02
  • C语言数据结构之单链表操作详解
    目录1、插入操作2、删除操作3、查找操作4、修改操作5、完整代码1、插入操作 (1)创建一个新的要插入的结点 (2)将新结点的 next 指针指向插入位置后的结点 (3)将插入位置前...
    99+
    2024-04-02
  • C语言数据结构之单链表存储详解
    目录1、定义一个链表结点2、初始化单链表3、输出链表数据4、完整代码如果说,顺序表的所占用的内存空间是连续的,那么链表则是随机分配的不连续的,那么为了使随机分散的内存空间串联在一起形...
    99+
    2024-04-02
  • C语言数据结构哈希表详解
    #include <stdio.h> #include <stdlib.h> #include <string.h> // 哈...
    99+
    2024-04-02
  • C语言数据结构之单向链表详解分析
    链表的概念:链表是一种动态存储分布的数据结构,由若干个同一结构类型的结点依次串连而成。 链表分为单向链表和双向链表。 链表变量一般用指针head表示,用来存放链表首结点的地址。 每个...
    99+
    2024-04-02
  • C语言线性表之双链表详解
    目录定义1.删除2.插入3.建立4.查找总结定义 链表是通过一组任意的存储单元来存储线性表中的数据元素,每一个结点包含两个域:存放数据元素信息的域称为数据域,存放其后继元素地址的域称...
    99+
    2024-04-02
  • C语言数据结构之堆排序详解
    目录1.堆的概念及结构2.堆的实现2.1 堆的向下调整算法2.2 堆的向上调整算法2.3 建堆(数组)2.4 堆排序2.5 堆排序的时间复杂度1.堆的概念及结构 如果有一个关键码的集...
    99+
    2024-04-02
  • C语言数据结构之二叉树详解
    目录1. 树概念及结构1.1树概念1.2树的表示2. 二叉树概念及结构2.1概念2.2数据结构中的二叉树2.3特殊的二叉树2.4二叉树的存储结构2.5二叉树的性质3. 二叉树顺序结构...
    99+
    2024-04-02
  • Java数据结构之栈的线性结构详解
    目录一:栈二:栈的实现三:栈的测试四:栈的应用(回文序列的判断)总结一:栈 栈是限制插入和删除只能在一个位置上进行的表,此位置就是表的末端,叫作栈顶。 栈的基本操作分为push(入...
    99+
    2024-04-02
  • C++数据结构之链表详解
    目录前言一、删除链表中给定值为key的节点二、反转链表三、返回链表的中间节点四、删除链表的倒数第K个节点五、分割链表六、合并两个有序链表七、删除有序链表中重复节点八、环形链表九、相交...
    99+
    2024-04-02
  • 如何理解C语言数据结构中线性表的链式存储结构
    如何理解C语言数据结构中线性表的链式存储结构,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1.什么是线性表的链式存储结构 —链表存储结点:包括元素本身的信息,还有元素之间的关系...
    99+
    2023-06-21
  • C语言数据结构中的线性表怎么使用
    这篇文章主要介绍“C语言数据结构中的线性表怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言数据结构中的线性表怎么使用”文章能帮助大家解决问题。一、分文件编写1、分文件编写概念在Visua...
    99+
    2023-06-30
  • C语言编程数据结构线性表之顺序表和链表原理分析
    目录线性表的定义和特点线性结构的特点线性表顺序存储顺序表的元素类型定义顺序表的增删查改初始化顺序表扩容顺序表尾插法增加元素头插法任意位置删除任意位置添加线性表的链式存储数据域与指针域...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作