您的位置:首页 >资讯 >

顺序表与链表

2023-08-17 05:37:25    来源:博客园


【资料图】

顺序表与链表

前言

基础数据结构的学习主要包括两个部分,即【结构定义】与【结构操作】。顾名思义,结构定义就是定义某种或多种性质,再通过相关的结构操作去维护这种性质。对于初学者来说数据结构的学习不能抽象的理解,还需要结合动态的、可视化的工具去理解。下面给出美国旧金山大学数据结构可视化的网站,帮助理解。   美国旧金山大学数据结构可视化网站

顺序表

【概念】

顺序表就是线性表的顺序存储形式,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

【结构特点】

  • 支持随机存取

  • 插入删除不方便

  • 存储密度高

【结构操作】

  • 初始化【init】
Vector *init(int n) {    Vector *v = (Vector *)malloc(sizeof(Vector));    v->data = (int *)malloc(sizeof(int) * n);    v->len = 0;    v->size = n;    return v;}//数组空间初始化
  • 删除【delete】
int delete(Vector *v, int ind) {    if (v == NULL) return 0;    if (ind < 0 || ind >= v->len) return 0;    for (int i = ind; i < v->len; i++) {        v->data[i] = v->data[i + 1];        }    v->len--;    return 1;}//删除指定位置的元素
  • 销毁【clear】
void clear(Vector *v) {    if (v == NULL) return ;    free(v->data);    free(v);    return ;}//销毁顺序表
  • 插入【insert】
int insert(Vector *v, int ind, int val) {    if (v == NULL) return 0;    if (ind < 0 || ind > v->len) return 0;    if (v->len == v->size) {        //扩容操作        if (!expand(v)) return 0;//扩容失败        printf("expand is successfuly ~~~\n");    }    for (int i = v->len; i > ind; i--) {        v->data[i] = v->data[i - 1];    }    v->data[ind] = val;    v->len++;    return 1;}//插入元素
  • 扩容【expand】
int expand(Vector *v) {    int tmp_size = v->size;    int *p;    while (tmp_size) {        p = (int *)realloc(v->data, sizeof(int) * (tmp_size + v->size));//重新分配空间        if (p) break;        tmp_size /= 2;    }    if (p == NULL) return 0;    v->data = p;    v->size += tmp_size;    return 1;}//扩容操作
  • 查找【search】
int search(Vector *v, int ind) {    if (v == NULL) return 0;    if (ind < 0 || ind >= v->len) return 0;    return v->data[ind];}//获取指定位置元素的值

【问题记录】

  • 当使用realloc重新分配数组空间失败后,返回的是什么值?【NULL】
  • 当calloc、malloc、realloc申请的空间为0个能不能申请成功?
    • 可以申请成功,并且不为空。
    • 注意:但是此时申请的地址空间不可使用
  • 顺序表插入一个新元素val到ind位置的思路:1.判断ind是否合法,即0 ≤ ind < vector.len;2.判断顺序表的长度是否大于空间大小(vector.len ≥ vector.size)3.从顺序表最后一个元素开始向后移动一位,直到移动到下标为ind位置为止4.将vector[ind] = val; 完成顺序表元素插入5.vector.len += 1;
  • 顺序表删除指定位置ind的思路:1.判断要删除的位置ind是否合法,即 0 ≤ ind < vector.len;2.指针走到下标为ind的位置,将ind + 1的元素复制到ind位置,直到指针走到顺序表最后一个元素。3.vector.len -= 1;
  • 顺序表数组空间扩容的思路:1.判断顺序表长度是否等于数组空间的大小,即 vector.len ?= vector.size2.若len 等于 size 则触发扩容操作,将原始空间大小 size 保存下即:tmp_size = size;3.为了防止重新分配空间失败,申请指针p , 采用while循环通过不断缩减temp_size的大小(tmp_size /= 2)申请重新分配地址空间。4.退出循环后判断p是否为NULL, 不为空就将p赋值给原始指针。并更新空间大小。
  • 顺序表数组空间销毁的思路:1.首先判断vector是否为NULL2.不为空则销毁数据空间3.最后销毁vector
  • 顺序表查询指定位置元素的思路:1.判断查询位置是否合法(即:0 ≤ ind < vector.len)2.直接根据索引即可查询到(即:return vector.data[ind])

顺序表完整代码

点击查看代码
#include#include#include/*顺序表:初始化、插入、删除、销毁、扩容、查找*/typedef struct Vector {      int *data;      int len, size;}Vector;Vector *init(int n) {    Vector *v = (Vector *)malloc(sizeof(Vector));    v->data = (int *)malloc(sizeof(int) * n);    v->len = 0;    v->size = n;    return v;}//数组空间初始化int expand(Vector *v) {    int tmp_size = v->size;    int *p;    while (tmp_size) {        p = (int *)realloc(v->data, sizeof(int) * (tmp_size + v->size));//重新分配空间        if (p) break;        tmp_size /= 2;    }    if (p == NULL) return 0;    v->data = p;    v->size += tmp_size;    return 1;}//扩容操作int insert(Vector *v, int ind, int val) {    if (v == NULL) return 0;    if (ind < 0 || ind > v->len) return 0;    if (v->len == v->size) {        //扩容操作        if (!expand(v)) return 0;//扩容失败        printf("expand is successfuly ~~~\n");    }    for (int i = v->len; i > ind; i--) {        v->data[i] = v->data[i - 1];    }    v->data[ind] = val;    v->len++;    return 1;}//插入元素int delete(Vector *v, int ind) {    if (v == NULL) return 0;    if (ind < 0 || ind >= v->len) return 0;    for (int i = ind; i < v->len; i++) {        v->data[i] = v->data[i + 1];        }    v->len--;    return 1;}//删除指定位置的元素int search(Vector *v, int ind) {    if (v == NULL) return 0;    if (ind < 0 || ind >= v->len) return 0;    return v->data[ind];}//获取指定位置元素的值void clear(Vector *v) {    if (v == NULL) return ;    free(v->data);    free(v);    return ;}//销毁顺序表void output(Vector *v) {    if (v == NULL) return ;    printf("Vector[%d] ==> [", v->len);    for (int i = 0; i < v->len; i++) {        i && printf(",");        printf("%d", v->data[i]);    }    printf("]\n");    return ;}//遍历顺序表int main() {    #define max_op 10    Vector *v = init(max_op);/*初始化数组空间*/    srand(time(0));    int op, ind, val;    for (int i = 0; i < max_op; i++) {        op = rand() % 4;        ind = rand() % (v->len + 1);        val = rand() % 100;        switch (op) {            case 0:            case 1:            case 2: {                /*插入元素*/                printf("Vector insert val[%d] into ind[%d] is %s\n", val, ind, insert(v, ind, val) ? "successfully" : "fail");            } break;            case 3: {                /*删除指定位置的元素*/                printf("Vector delete ind[%d] is %s\n", ind, delete(v, ind) ? "successfully" : "fail");            } break;        }        output(v);        }        printf("请输入要查找的位置:");    while (~scanf("%d", &ind)) {        val = search(v, ind);        if (val) printf("search ind[%d] is val[%d]\n", ind, val);        else printf("对不起, 你查找的位置不合法。请重新输入!\n");        printf("请输入要查找的位置:");    }    putchar(10);    #undef max_op    clear(v);/*销毁顺序表*/    return 0;}

链表

【概念】

使用任意的存储单元存储线性表的数据元素,该存储单元可以是连续的也可以是不连续的。采用结点表示每一个线性表的元素,结点包括指针域、数据域。数据域存储数据元素的值、指针域存储下一个结点的地址。

【结构特点】

  • 插入删除效率高

  • 内存利用率高

  • 操作灵活

【结构操作】

  • 初始化【init】
List *init() {    List *l = (List *)malloc(sizeof(List));    l->head = (Node *)malloc(sizeof(Node));    l->head->next = NULL;    l->len = 0;    return l;}/*初始化链表*/
  • 插入【insert】
int insert(List *l, int ind, int val) {    if (l == NULL) return 0;    if (ind < 0 || ind > l->len) return 0; /*插入位置不合法*/    Node *p = l->head, *q;    while (ind--) p = p->next;    q = p->next;    p->next = getNewNode(val);    p = p->next;    p->next = q;    l->len++;    return 1;}/*插入新节点*/
  • 销毁【clear】
void clearNode(Node *head) {    if (head == NULL) return ;    clearNode(head->next);    free(head);    return ;}/*销毁结点*/void clear(List *l) {    if (l == NULL) return ;    clearNode(l->head);    free(l);    return ;}/*销毁链表*/
  • 删除【delete】
int delete(List *l, int ind) {    if (l == NULL) return 0;    if (ind < 0 || ind >= l->len) return 0;//删除位置不合法    Node *p = l->head, *q;    while (ind--) p = p->next;    q = p->next;    p->next = q->next;    free(q);    l->len--;    return 1;}/*删除结点*/

问题记录

  • 链表如何插入一个新节点node到指定位置ind?
    • 判断ind是否合法(即 ind > 0 && ind < list.len);
    • 申请两个指针p 、q;
    • p指向虚拟头结点,根据ind向后迭代 while (ind—)p = p→next;
    • q指向p→next(防止内存泄漏)
    • 将node插到p→next位置 :p→next = node;
    • 此时p指向 p→next; p = p→next
    • 重新挂载后面的数据 p→next = q;
  • 链表删除指定元素?
    • 指针p走到待删除的前一个结点位置。
    • 将待删除结点的后面数据使用指针q指向
    • 将指针p指向的结点的next指针域覆盖为待删除的结点后面的q;

链表完整代码

点击查看代码【单链表】
#include#include#include/*单链表:初始化、获取新节点、插入新节点、删除结点、销毁链表、遍历结点*/typedef struct Node {    int data;    struct Node *next;}Node;typedef struct List {    Node *head;//虚拟头结点    int len;}List;/*获取新节点*/Node *getNewNode(int val) {    Node *p = (Node *)malloc(sizeof(Node));    p->data = val;    p->next = NULL;    return p;}List *init() {    List *l = (List *)malloc(sizeof(List));    l->head = (Node *)malloc(sizeof(Node));    l->head->next = NULL;    l->len = 0;    return l;}/*初始化链表*/int insert(List *l, int ind, int val) {    if (l == NULL) return 0;    if (ind < 0 || ind > l->len) return 0; /*插入位置不合法*/    Node *p = l->head, *q;    while (ind--) p = p->next;    q = p->next;    p->next = getNewNode(val);    p = p->next;    p->next = q;    l->len++;    return 1;}/*插入新节点*/int delete(List *l, int ind) {    if (l == NULL) return 0;    if (ind < 0 || ind >= l->len) return 0;//删除位置不合法    Node *p = l->head, *q;    while (ind--) p = p->next;    q = p->next;    p->next = q->next;    free(q);    l->len--;    return 1;}/*删除结点*/void output(List *l) {    if (l == NULL) return ;    printf("Linklis[%d] == [", l->len);    int flag = 0;    for (Node *p = l->head->next; p; p = p->next) {        flag && printf("->");        printf("%d", p->data);        flag = 1;    }    printf("]\n");    return ;}/*遍历链表结点*/void clearNode(Node *head) {    if (head == NULL) return ;    clearNode(head->next);    free(head);    return ;}/*销毁结点*/void clear(List *l) {    if (l == NULL) return ;    clearNode(l->head);    free(l);    return ;}/*销毁链表*/int main() {        srand(time(0));    int op, ind, val;    #define max_op 20    List *l = init();    for (int i = 0; i < max_op; i++) {        op = rand() % 4;        ind = rand() % (l->len + 1);        val = rand() % 100;        switch (op) {            case 0:            case 1:            case 2: {                printf("Linklist[%d] insert val = %d in the ind = %d is %s\n", l->len, val, ind, insert(l, ind, val) ? "YES" : "NO");            } break;            case 3: {                printf("Linklist[%d] delete the ind = %d is %s\n", l->len, ind, delete(l, ind) ? "YES" : "NO");            } break;        }           output(l);    }    clear(l);    #undef max_op    return 0;}
点击查看代码【双向链表】
//双向链表:初始化、插入、删除、销毁、遍历、获取ind位置的元素#include#include#includetypedef struct Node {    int data;    struct Node *next, *pir;}Node;typedef struct DLinkList {    Node *head;    int len;}DLinkList;//获取新节点Node *getNewNode(int val) {    Node *p = (Node *)malloc(sizeof(Node));    p->next = p->pir = NULL;    p->data = val;    return p;}//获取新的双链表DLinkList *getNewDLinkList() {    DLinkList *DL = (DLinkList *)malloc(sizeof(DLinkList));    DL->head = getNewNode(0);//头结点    DL->len = 0;    return DL;}//插入int insert(DLinkList *DL, int ind, int val) {    if (DL == NULL) return 0;    if (ind < 0 || ind > DL->len) return 0;//插入位置不合法    Node *p = DL->head, *q, *new_node = getNewNode(val);    while (ind--) p = p->next;    new_node->next = p->next;    new_node->pir = p;    if (p->next) p->next->pir = new_node;    p->next = new_node;    DL->len += 1;    return 1;}//删除int delete(DLinkList *DL, int ind) {    if (DL == NULL) return 0;    if (ind < 0 || ind >= DL->len) return 0;//删除的位置不合法    Node *p = DL->head;    while (ind--) p = p->next;    Node *q = p->next;    p->next = q->next;    if (q->next) q->next->pir = p;    DL->len--;    free(q);    return 1;}//搜索第ind位置的元素int search(DLinkList *DL, int ind) {    if (DL == NULL) return 0;    if (ind < 0 || ind >= DL->len) return 0;    Node *p = DL->head;    while (ind--) p = p->next;    return p->data;}//遍历void output(DLinkList *DL) {    if (DL == NULL) return ;    printf("[");    Node *p = DL->head->next;    int i = 0;    for (Node * p = DL->head->next; p; p = p->next) {        i++ && printf(", ");        printf("%d", p->data);    }    printf("] <== DL[%d]\n", DL->len);    return ;}//销毁双向链表void clearNode(Node *head) {    if (head == NULL) return ;    clearNode(head->next);    return ;}void clear(DLinkList *DL) {    if (DL == NULL) return ;    clearNode(DL->head);    free(DL);    return ;}int main() {    srand(time(0));    #define max_op 20    int op, val, ind;    DLinkList *DL = getNewDLinkList();//获取一个双向链表    for (int i = 0; i < max_op; i++) {        op = rand() % 4;        ind = rand() % (DL->len + 1);        val = rand() % 100;        printf("op = %d ind = %d val = %d\n", op, ind, val);        switch (op) {            case 0:            case 1: {                //插入                printf("insert the ind = %d ,val = %d to the DL %s!\n", ind + 1, val, insert(DL, ind, val) ? "YES" : "NO");            } break;            case 2: {                //删除                printf("delete the element of ind = %d from the DL %s!\n", ind + 1, delete(DL, ind) ? "YES" : "NO");            } break;            case 3: {                //查找                val = search(DL, ind);                printf("search the element of ind = %d from the DL is %d!\n", ind + 1, search(DL, ind));            } break;        }        output(DL);    }    clear(DL);    #undef max_op    return 0;}

标签:

精彩放送

木卫四协议第四章植入体信息获得攻略

《元气骑士》禁用宠物挑战因子介绍

《三国志战棋版》焚粮截道战法影响范围介绍

《汉字找茬王》热梗人物墙通关攻略

《节奏地牢》衍生作:游戏《Rift of the NecroDancer》宣布延期明年发售

《OW2》限定公版RTX 4090公布 玩家吐槽大可不必

虚拟桌宠模拟器心情值增加方法 悲伤怎么办

原神镜中的王国安的故事任务攻略

原神枫丹主城灰河传送点开启方法详解

《原神》博物通志水国寻迹第四关攻略 斩棘克芜之章指南

《原神》4.0版世界任务水色潮痕攻略

《原神》全水神瞳收集图文攻略 4.0全水神瞳位置汇总

刷钱刷物品闹乌龙,暴雪一度紧急关闭《暗黑4》游戏交易系统

逆水寒手游2023七夕活动任务有哪些

原神泡泡桔采集点在哪

代号鸢2023七夕活动有哪些

《逆水寒手游》怎么完成汴京词话汴京河口

暗链检测工具Libra

阿蒙森搏鱼大师评测(阿蒙森)

《原神》博物通志水国寻迹活动玩法攻略推荐

原神三人行先寻明师任务介绍

《崩坏星穹铁道》地城探宝活动玩法介绍

原神林尼值得抽吗 林尼技能强度解析

最佳11人最强阵容搭配 最强组合推荐

小精灵训练师新手攻略大全2023 新手快速养成秘籍

鹅鸭杀钻盔甲的角色一览

云顶之弈s8灵能星守阵容搭配攻略

Xbox推出处罚警告系统 行为不当玩家最高处罚1年

《战地2042》新赛季恐怖风新图曝光 归来活动将上线

《原神》4.0主线乐斯总部秘境宝箱收集攻略 魔神第四章秘境宝箱位置

《原神》4.0 版本今日上线:全新区域“枫丹”,新增 4 个新角色

《罪恶装备:Strive》开发者访谈 2D格斗游戏已经做到极致

玩家自制《星空》主题电脑:非常酷炫 官方点赞

辐射76辐射鹿头壁饰在哪

辐射76制革工匠测试怎么通过

四朝帝王,君临乱世!《乱世王者》六周年庆今日震撼开启

蚂蚁庄园今日答案最新8.7 支付宝蚂蚁庄园今日答案8.7

西游梗传吞丹化容颜通关攻略

命运方舟刀锋铭刻怎么搭配

上海十大必逛商场 上海最大的商场

每体:阿森纳有意坎塞洛,但巴萨很乐观,因为已与球员谈妥

石家庄这个路段即将施工,工期40天

三国杀英魂英姿是谁的技能 英魂英姿技能对比解析

原神铁甲熔火帝皇在哪里在哪

《原神》4.0版问题修复介绍

塞尔达传说王国之泪伊希希怎么打

自在西游礼包码兑换码大全

原神倾落伽蓝乱世轮舞全流程攻略

原神前期值得培养的角色有哪些

曙光英雄李靖玩法攻略

曙光英雄上单英雄推荐

王者荣耀太乙真人玩法攻略

航海王热血航线兑换码永久可用(15个豪华礼包码分享)

《原神》新角色林尼PV:枫丹时代首位火系大C

热度暴跌!《暗黑4》新赛季上线后玩家流失数十万

喜欢兽耳娘的有福了!养成游戏《兽人化少女》宣传片公开

博德之门3二环法术灼热射线有什么特点

辐射76泰迪熊磁带怎么获得

博德之门3二环法术马友夫强酸箭有什么特点

《原神》林尼角色攻略 4.0林尼培养指南

《原神》4.0版问题修复一览

《汉字找茬王》勇气大爆发 完成歌曲通关攻略

《原神》4.0版枫丹全旋曜玉帛收集攻略

《元气骑士》能量减半挑战因子介绍

《命运方舟》二觉任务流程

《曙光英雄》黑卡获得方法一览

《超级达人》848男团通关攻略

《原神》4.0新boss介绍一览

太空射击游戏《永恒空间2》主机版发售预告 现已加入XGP

传闻被否认 动视确认《使命召唤:现代战争3》售价70美元

《装甲核心6》PC配置公布 1650即可一战

隐秘的档案撞邪通关攻略

醉计三国兑换码最新分享 2023礼包激活码合集

杨超越丁禹兮《七时吉祥》大片 甜蜜对视默契十足

反腐风暴下的医药IPO:年内13家企业终止上市,ipo进度放缓,规则调整

中科磁业8月16日盘中跌幅达5%

《虚拟桌宠模拟器》Steam免费推出 支持互动投喂

悬疑解谜游戏《破月执行》上线steam 8月正式发售

《穿越火线》VR新作《穿越火线:塞拉小队》公布 将于8月29日发售

原神苍白的遗荣秘境解锁条件介绍

博德之门3阿尔菲拉怎么招募入队

原神旅行者水元素状态下有什么技能

边骂边玩,守望先锋2次日留存达80%;博德之门3热度消退

暗黑4开发者直播玩自家游戏,因为过菜惹怒玩家

泰拉瑞亚骷髏李介绍

博德之门3二环法术造风有什么特点

《别惹农夫》电音泰坦解锁方法一览

《曙光英雄》亲密关系提升攻略

变态传奇战士天赋怎么加_变态传奇战士天赋怎么加点

《刺客信条:幻景》将亮相科隆开幕之夜 期待吗?

变态传奇战士走位出刀教程_传奇战士走位出刀视频

《地平线》在线合作游戏已经开发了将近6年

原神虹彩蔷薇采集位置路线一览

原神柔灯铃采集位置分享

原神枫丹怎么进入 枫丹最快进入方法一览

桃李面包第二季度业绩双降经营现金流承压 产能利用率下滑至73.35%或存过剩隐忧

原创 内存大告急!更新32个G,米哈游到底往《原神》新版本里塞了啥?

就挺秃然的故事新编-就挺秃然的故事新编通关攻略

影之刃3械城之鲤心法介绍

博德之门3尖叫之剑怎么获取