通过C语言构造线性表(数据结构学习笔记)

前言

C语言构造线性表的代码量就有点大了,所以在这一篇文章就只用C语言来做。其中面向对象语言编写起来的思想都差不多所以后面每章就选择一个语言来写。
这一章中我们来试着构造一下线性表,在上一篇文章中我们说了数据在计算机中有两种不同的存储方式一种是顺序存储结构链式存储结构,所以这节课中我们要通过着两种不同结构和上节课构造的基结构来构造我们的线性表。关于线性表的定义我也不废话了,具体可以可能看看百度百科线性表
代码量有点多所以我也就不废话了。

顺序存储

先把完整的代码给放出来

#include<stdio.h>
#include<stdlib.h>
#define BaseStruct LinkList//标注BaseStruct现在是什么类型 比如说现在是列表 对列表进行操作
#define LinkSize 10
void initData();
short add();
void removeh();
typedef int BaseType;//定义baseStruct的基本类型
typedef struct node{
    BaseType data;
    struct node *next;
} Node;//链式存储需要用到这样的一个结构
typedef struct basestruct{
    BaseType *header;//不管是什么存储结构都会有一个头,通过开头地址就能找到下一个元素
    int p;//p
    void (*initData)();//初始化数据
    short (*add)();//插入数据
    void (*removeh)();//删除值 remove有函数用了这个名字所以换成removeh
    void (*printbs)();//打印bs
    void (*insert)();
}*BaseStruct;
//开始构建存储
void initData(BaseStruct bs){
    bs->p=0;
    bs->header=(BaseType*)malloc(LinkSize*sizeof(BaseType));
    

    //分配了10个空间 你可以用a[11]进行访问,产生了越界,不会报错,但是这个数据在内存中是不安全的,数据随时可能会丢失,内存被拿走
}
short add(BaseStruct bs,BaseType val){
    int i=0;
    if(bs->p<LinkSize){
        bs->header[bs->p]=val;
        //printf("%d",bs->header[bs->p]);
        bs->p++;
        
        return 1;
    }else{
        return 0;
    }

}
//在指定位置插入一个值
void insert(BaseStruct bs,int addr,BaseType val){
    int i=addr-1;
    if(i<bs->p){
        if(bs->p!=10){
            bs->p++;
        }

        for(i=(bs->p-1);i>(addr-1);i--){
            bs->header[i]=bs->header[i-1];
        }
        bs->header[addr-1]=val;
    }

    
}
void removeh(BaseStruct bs,int addr){
    //addr为删除元素的位置 void是类型不能返回
    int i=addr-1;
    if(i<bs->p){
        if(addr==LinkSize){
                bs->header[LinkSize-1]=0;
        }else{
            for(i;i<bs->p;i++){
                    bs->header[i]=bs->header[i+1];
            }
                        bs->header[bs->p]=0;
        }
    
        bs->p--;
    }
}
void printbs(BaseStruct bs){
    int i=0;
    while(i<bs->p){
        printf("%d ",bs->header[i]);
        i++;
    }
}
void main(){
    BaseStruct a=(BaseStruct)malloc(sizeof(BaseStruct));
    a->initData=initData;
    a->add=add;
    a->removeh=removeh;
    a->printbs=printbs;
    a->insert=insert;
    a->initData(a);
    a->add(a,2);
    a->add(a,3);
    a->add(a,2);
    a->add(a,3);
    a->printbs(a);
    printf("\n");
    a->insert(a,4,4);
    a->printbs(a);
    getchar();
}


有几个点说一下

  1. 在BaseStruct结构体中,新增加了一个变量p。p是用来记录当前存放了几个元素的。
  2. 在initData当中,我们采用了malloc的方法分配区域(也可以通过定义数组,去分配内存)
  3. 重点说的还有两个函数removeh和insert
    removeh
    addr为我们需要删除的元素的位置,如我们想要删除第1个元素,就等同于删除bs->header[0]
    所以我们先进行一个判断 ,判断我们要删除的元素是否大于我们目前线性表的大小,如果大于就什么都不进行操作,如果小于进行下一步判断,即判断要删除的元素是否等于最后一个元素,如果等于就直接将此元素设置为0(模拟删除),如果不等于就按照从前往后的顺序进行循环处理(与插入数据的顺序相反,防止数据的丢失),为什么要进行这一步判断呢?是因为我们malloc只分配了10个大小的连续空间给程序,如果不进行判断就可能将后面的分配给其他内存中的一小段数据给读出来,为了内存安全着想,还是做一下判断为好(尽管在这段程序中不会有什么影响)
    insert
    开始跟removeh一样的判断,我就不多说了。接着,我们要判断当前的线性表有没有达到最大的大小,因为线性表达到最大元素之后,我们插入一个元素线性表的大小是不会变化的。接着说一说这个循环吧,按照从后往前的顺序进行循环处理(与删除数据的顺序相反,这个顺序也是为了防止数据的丢失)

链式存储


#include<stdio.h>
#include<stdlib.h>
#define BaseStruct LinkList//标注BaseStruct现在是什么类型 比如说现在是列表 对列表进行操作
void initData();
void insert();
void removeh();
void add();
typedef int BaseType;//定义baseStruct的基本类型
typedef struct node{
    BaseType data;
    struct node *next;
} Node;//链式存储需要用到这样的一个结构
typedef struct basestruct{
    Node *header;//不管是什么存储结构都会有一个头,通过开头地址就能找到下一个元素
    void (*initData)();//初始化数据
    void (*insert)();//插入数据
    void (*removeh)();//删除值 remove有函数用了这个名字所以换成removeh
    void (*printbs)();//打印bs
    void (*add)();
}*BaseStruct;
void initData(BaseStruct bs){
    bs->header=(Node*)malloc(sizeof(Node));
    bs->header->next=NULL;
}
void add(BaseStruct bs,BaseType val){
    Node *n=NULL;
    Node *p=NULL;
    n=(Node*)malloc(sizeof(Node));
    n->data=val;
    n->next=NULL;
    p=bs->header;
    while(p->next!=NULL){
        p=p->next;
    }
    p->next=n;
    
}
void insert(BaseStruct bs,int addr,BaseType val){
    //addr为要删除元素的位置
    int i=0;
    Node *pre,*n,*p;
    p=bs->header;
    while(p->next!=NULL){
        pre=p;
        //next=p->next;
        p=p->next;
        i++;
        if(i==addr){
            n=(Node*)malloc(sizeof(Node));
            n->data=val;
            pre->next=n;
            n->next=p;
            break;
        }
    }
    
}
void removeh(BaseStruct bs,int addr){
    int i=0;
    Node *p=NULL,*pre=NULL,*next=NULL;
    p=bs->header;
    while(p->next!=NULL){
        pre=p;
        p=p->next;
        i++;
        if(i==addr){
            pre->next=p->next;
            free(p);
            break;
        }
    }
    
}
void printbs(BaseStruct bs){
    Node *p=NULL;
    p=bs->header->next;
    while(p!=NULL){
        printf("%d ",p->data);
        p=p->next;
    }

}
void main(){
    BaseStruct a=(BaseStruct)malloc(sizeof(struct basestruct));
    a->initData=initData;
    a->insert=insert;
    a->add=add; 
    a->removeh=removeh;
    a->printbs=printbs;
    a->insert=insert;
    a->initData(a);
    a->add(a,1);
    a->add(a,2);
    a->insert(a,1,3);
    a->insert(a,2,5);
    a->removeh(a,1);
    a->printbs(a);
    
    
}

add函数
首先我们需要创建两个指针变量n和p,n指向新创建的节点的地址,而p是用来遍历整个链表用的。通过循环找到整个链表的最后一个节点,将新创建的节点接入。
insert函数
首先创建三个指针变量,pre指向当前操作节点的上一个节点,n指向我们新创建的节点的地址,同理p是用来对遍历整个链表的。接下来进入循环判断当前的位置是否为要操作的位置。如果是,则将链子断开,将pre指向的变量的next成员指向新创建的节点,再将新创建的节点的next成员指向下一个节点。
removeh函数
removeh和insert其实有一些相似,变量的含义跟之前一样,不同的是removeh在找到需要操作的位置后,将pre的指针指向当前操作节点的后一个节点,再利用free函数将当前操作节点清空。

对比

顺序存储
顺序存储要求内存中可用存储单元的地址必须是连续的。
优点:存储密度大(=1),易于查找和修改。
缺点:插入或删除元素时不方便;存储空间利用率低,预先分配内存可能造成存储空间浪费。
顺序表适宜于做查找这样的静态操作;
若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;
链式存储
链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。
优点:插入或删除元素时很方便,使用灵活,存储空间利用率高。
缺点:存储密度小(<1),查找和修改需要遍历整个链表。
链表宜于做插入、删除这样的动态操作。
若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。

两种方式的比较

参考

https://blog.csdn.net/fengxiao8/article/details/78690125

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339