数据结构之链表C语言实现以及使用场景分析

牢骚:本篇博客两个星期前已经存为草稿,鉴于发生一些糟糕的事情,今天才基本完成。本人6月份应届毕业生一枚,毕业后当天来到帝都,之后也非常顺利,面试了俩家公司都成功了。一家做C++方面电商ERP,一家做wifi模块,觉得第二家公司小,薪资低,但是觉得好玩就去了。同时,在学校也喝了不少鸡汤,觉得公司小怎么了。然而去了不到20天,公司被深圳一家公司收购了,公司动员我去深圳,我尼玛我才来20多天啊,有木有?而且感觉公司做这么大的决定都是随时拍板的吗?

原本以为一个公司的生命力强到可以忽略的概率,然而当自己真实的遇到这一切,才知道这个社会有多残忍,公司的生存可能比一个员工的生存更要困难。

接下来就是找工作了,跑呀,找呀。。。。。。。。           一路被忽略,一路被放鸽子,一路被无视。。。。。。。   心里五味杂陈。。。。。。。。。

so,我还是去撸代码,看书去了,没有一刻可以用来stop。

感谢seven,严厉也好,鼓励也好,短短的一个月,您教会了我许多,感谢您的知遇之恩!愿你们去深圳一切顺利!待倔强的我在北京玩上一阵!

————————————————————————————————————————————————————————————————————————————————

链表是数据结构中比较基础也是比较重要的类型之一,那么有了数组,为什么我们还需要链表呢!或者说设计链表这种数据结构的初衷在哪里?

这是因为,在我们使用数组的时候,需要预先设定目标群体的个数,也即数组容量的大小,然而实时情况下我们目标的个数我们是不确定的,因此我们总是要把数组的容量设置的很大,这样以来就浪费了很多的空间。另外,数组在进行插入操作和删除操作的时候,在插入或者删除制定元素之后,我们往往需要进行循环移位,这增加了我们的线性开销。

正是由于以上的两种主要原因,链表被设计出来用于一般表的操作。为了避免上面描述数组的两种弊端,我们希望链表有一下的特点

1 可以灵活的扩展自己的长度。

2 存储地址不连续,删除或者插入操作的时候不需要循环移位。

要实现以上两个特点,我们需既要保证每个节点的独立性,又要保存相邻两个节点的联系。

为此,链表一般被设计为下面的形式。

Node--->Node---->Node

链表是由一个一个的节点组成的,可以方便和自由的插入未知个Node,前一个节点中用指针保存着下一个节点的位置,这样以来便顺利的完成了我们对链表的两点期望,但是唯一的缺点是增加了额外的空间消耗。

————————————————————————————————————————————————————————————————————————————

链表的定义:

链表的定义一般使用结构体,在看《数据结构与算法分析》这本书的时候发现,书中频繁的使用typedef的关键字,结果真的很棒不仅保持的代码的整洁程度,也让我们在下面的编码过程中少见了很多烦人的指针(当然指针还是一直存在的)。所以这里也借用了书中的定义方法。

struct Node;
typedef struct Node* PtrNode;
typedef PtrNode Position;
typedef PtrNode List;
struct Node{
        int Value;
        PtrNode Next;
};

下面接着书写一个建立链表的函数,输入每个节点的值,直到这个值是-1的时候函数结束。

在这个里面,我以前一直搞不明白为什么需要定义三个Node *,现在终于了解了,最终还是复习了指针的内容明白的,这里说一下指针实现链表对指针的操作很频繁,需要比较扎实的掌握了指针之后,在来看链表会轻松很多。在下面的一段程序里,我分别定义了head/p/tmp这三个指向节点结构体的指针,head的主要作用就像一个传销头目,他会主动联系上一个下线p,然后他就什么也不干了,p接着去发展一个又一个的下线tmp,结果一串以head为首的链表就出来了。

起先,我总觉得有了head,为什么还要p,这是因为如果直接使用head去指向下一个节点,head的位置也是不断在移动的,即它永远处于链表的尾端,这样当我们返回链表的时候,其实是空值。所以,我们需要p这个中转环节。(其实,这种做法在指针中非常普遍,大部分有返回指针类型的函数中,都会首先定义一个指针变量来保存函数的传入的参数,而不是对参数直接进行操作)。

/*
        函数功能:创建一个链表
        函数描述:每次输入一个新的整数,即把新增加一个节点存放该整数,
        当输入的整数为-1时,函数结束。
*/
List create()
{
        int n=0;
        Position p,head,tmp;
        head=NULL;
        tmp=malloc(sizeof(struct Node));
        if(tmp==NULL)
        {
                printf("tmp malloc failed!\n");
                return NULL;
        }
else
        {
                p=tmp;
                printf("please input the first node‘s message!\n");
                scanf("%d",&(tmp->Value));
        }
        while(tmp->Value!=-1)
        {
                n+=1;
                if(n==1)
                {
                        head=p;
                        tmp->Next=NULL;
                }
                else
                {
                        p->Next=tmp;
                }
                p=tmp;
                tmp=malloc(sizeof(struct Node));
                printf("please input the %d node!\n",n+1);
                scanf("%d",&(tmp->Value));
        }
        p->Next=NULL;
        free(tmp);  //free函数free掉的只是申请的空间,但是指针还是依然存在的。
        tmp=NULL;
        return head;

}

接下来,在写一个删除链表节点的函数,输入一个整数然后遍历链表节点,当链表节点的值与该整数相等的时候,即把该节点删除。

在完成这个函数首先一定要把这个过程思考清楚,不可否认我之前是一个上来就敲代码的人,看了《剑指offer》感觉这种习惯是程序员的大忌,甚至还想写一篇博客,名字都想好了《程序员的自我修养之思考在前,代码在后》。其实想想也是,我们写程序的目的是为了解决问题,而不是为了简单的写程序,纯粹的让程序跑起来大概只会在上学那会存在吧!真实的程序开发中需要考虑几乎所有 能想到的实际问题,所以无论程序再下,一要学会先思考清楚,再下笔写程序。

关于这个函数,我们要想到的是:

1 如果链表为空,我们该怎么做,当然是直接返回。

2 如果要删除的元素为头节点该怎么办?

3 如果要删除的元素为尾节点该怎么办?

当注意到以上三个部分,我们的程序就可能避免掉了输入链表为空,程序直接崩溃的现象,也可以避免删除元素值为头节点时删不掉的尴尬。我们的程序就有了一定的鲁棒性。

下面着重考虑链表的删除的实现:

list:      Node_a->Node_b->Node_c->Node_d;

list        tmp         p

------->              tmp->Next=p->Next;

list:       Node_a->Node_b----------->Node_d

free(p)

假设我们要删除的节点为上图的Node_c;假设我们能够找到Node_c的前一个位置tmp和被删除节点位置p的话;这个时候我们只需要执行tmp->Next=p->Next即可。

只要完成上面的分析以及考虑到各种情况,我们完成下面的代码就水到渠成了。

/*
函数功能:删除链表中指定值的节点(如果存在多个,只删除第一个)
本例中输入一个整数,删除链表节点值为这个整数的节点。
*/
List DeleteNode(List list)
{
        Position p,tmp;
        int value;
        if(list==NULL)
        {
                printf("The list is null,function return!\n");
                return NULL;
        }
        else
        {
                printf("please input the delete Node‘s value:\n");
                scanf("%d",&value);
        }
        p=list;
       if(p->Value==value)
        {
                list=p->Next;
                free(p);
                p=NULL;
                return list;
        }
        while(p!=NULL&&p->Value!=value)
        {
                tmp=p;
                p=p->Next;
        }
        if(p->Value==value)
        {
                if(p->Next!=NULL){
                        tmp->Next=p->Next;
                }
                else
                {
                        tmp->Next=NULL;
                }
                free(p);
                p=NULL;
        }
        return list;

}
        

关于链表的使用场景分析:

链表在程序开发中用到的频率还是非常高的,所以在高级语言中往往会对链表进行一些实现,比如STL中list以及Java中也有类似的东西。在目前的服务器端开发,主要运用链表来接收一些从数据中取出来的数据进行处理。

即使你不知道链表的底层实现,仍然可以成功的运用STL里面的现成的东西。但是作为一个学习者,我觉得会使用和从底层掌握仍然是两个不同的概念,linux之父说:“talk is less,show you code”。

以下的程序,用链表模拟了一个电话通讯录的功能,包括添加联系人,查找联系人,以及删除联系人。

PS:关于鲁棒性,程序中最大的危险是使用了gets这个函数,目前先保留使用gets,等待找到工作之后在做进一步的程序完善。(尼玛,读书去。。。应届生,找个工作他妈咋这么难呢!   工作经验,工作经验,艹,那个大牛一出校门就什么都会。)

/**************************************************************************
Programe:
    This is a phone list write by list
    The programe is just prictise for list
Author: heat nan
Mail:[email protected]
Data:2015/07/27
**************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 25
#define M 15
struct node;
typedef struct node* p_node;
typedef p_node List;
typedef p_node Position;
typedef struct node** PList;
struct node{
    char name[N];
    char number[M];
    Position next;
};
int JudgeNameExist(List list,char* name);
void AddPerson(PList list);
void PrintList(List list);
List FindPerson(List list);
List FindPersonByName(List list,char* name);
int AddPersonByName(PList list,List node);
int DeletePersonByName(PList list,char* name);
void DeletePerson(PList list);
int main()
{
    List list=NULL;
    Position p;
    char cmd[100];
        while(1)
    {
        printf("                    MAIN                 \n");
        printf("       ******* 1 add a person        *******\n");
        printf("       ******* 2 show the phone list *******\n");
        printf("       ******* 3 find  from phone list *******\n");
        printf("       ******* 4 delete from phone list *******\n\n\n");
        printf("Please input the cmd number:\n");
        gets(cmd);
        switch(cmd[0])
        {
            case ‘1‘:
                Add_person(&list);
                break;
            case ‘2‘:
                    Print_list(list);
                break;
            case ‘3‘:
                FindPerson(list);
                break;
            case ‘4‘:
                DeletePerson(&list);
                break;
            default:
                printf("wrong cmd!\n");
                break;

        }

    }
    return 0;
}
/*
    Function:判断要添加的联系人名称是否已经存在于电话簿中.
    Input:   List 电话列表,name 要添加的联系人的姓名.
    Return:  已经存在返回1,不存在返回0.
*/
int JudgeNameExist(List list,char* name)
{
    if(FindPersonByName(list,name)!=NULL)
        return 1;
    else
        return 0;
}
/*
    Function:根据输入的姓名查找联系人的信息节点
    Input:   要输入的电话列表list,姓名name
    Return:  返回查找到的节点
*/
List FindPersonByName(List list,char* name)
{
    while(list!=NULL)
    {
        if(strcmp(list->name,name)==0)
            break;
        list=list->next;
    }
    return list;
}
/*
    Function:根据姓名添加新的联系人到联系人列表
    Input:   指向联系人列表地址的指针, 新用户节点
    Return:  添加成功返回1,添加失败返回0
*/
int AddPersonByName(PList list,List node)
{
    if(node==NULL)
    {
        printf("the node is NULL!\n");
        return 0;
    }
    if(*list==NULL)
    {
        *list=node;
        return 1;
    }
    List pHead=*list;
    while(pHead->next!=NULL)
        pHead=pHead->next;
    pHead->next=node;
    return 1;
}
void Add_person(PList list)
{
    Position tmp;
    Position p_head;
    tmp=(struct node*)malloc(sizeof(struct node));
    char name[N];
    char number[M];
    if(tmp==NULL)
    {
        printf("malloc the tmp node failed in function add person!\n");
    }
    else
    {

        printf("please input the name:\n");
        gets(name);
        printf("please input the number:\n");
        gets(number);
        strcpy(tmp->name,name);
        strcpy(tmp->number,number);
        tmp->next=NULL;
    }
    if(JudgeNameExist(*list,name)==1)
    {
        free(tmp);
        printf("the name have already exist!\n");
        return;
    }
    AddPersonByName(list,tmp);
}
/*
    Function: 打印联系人列表
    Input:   联系人列表

*/
void Print_list(List list)
{
    Position show;
    show=list;
    if(show==NULL)
    {
        return ;
    }
    printf("Now,we print the phone list:\n");
    while(show!=NULL)
    {
        printf("Name:%s  Number:%s\n",show->name,show->number);
        show=show->next;
    }

}
List FindPerson(List list)
{
    char name[N];
    Position pHead=list;
    printf("please input the name you will find:\n");
    gets(name);
        Position node=FindPersonByName(list,name);

    if(node!=NULL)
    printf("find success! name-> %s number-> %s\n",node->name,node->number);
    else
    printf("find failed!\n");
    return node;
}
/*
    Function:根据姓名删除联系人
    Input:  指向联系人地址的指针,联系人姓名
    Output: 删除成功返回1,失败返回0
*/
int DeletePersonByName(PList list,char* name)
{
    if(*list==NULL||name==NULL)
        return 0;
    List pHead=*list;
    if(strcmp(pHead->name,name)==0)
    {
        *list=pHead->next;
        free(pHead);
        pHead->next==NULL;
        return 0;
    }
    List tmp=pHead->next;
    while(tmp!=NULL)
    {
        if(strcmp(tmp->name,name)==0)
        {
            pHead->next=tmp->next;
            free(tmp);
            tmp->next=NULL;
            return 1;
        }
        pHead=tmp;
        tmp=tmp->next;
    }
return 0;

}
void DeletePerson(PList list)
{
    List pHead=*list;
    if(pHead==NULL)
    {
        printf("there is no person you can delet\n");
        return ;
    }
    char name[N];
    printf("please input the name:\n");
    gets(name);
    DeletePersonByName(list,name);
}
时间: 2024-11-08 21:50:50

数据结构之链表C语言实现以及使用场景分析的相关文章

《数据结构与算法分析—C语言描述》pdf

下载地址:网盘下载 内容简介 编辑 <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行时间分析的基础上考查了一些高级数据结构,从历史的角度和近年的进展对数据结构的活跃领域进行了简要的概括.由于<数据结构与算法分析:C语言描述(原书第2版)>选材新颖,方法实用,题例丰富,取舍得当.<数据结构与算法分析:C语言描述(原书第2版)>的目的是培养学生良好的程序设计技巧和熟练的算

数据结构——动态链表

说明:严蔚敏的<数据结构>(C语言版)学习笔记,记录一下,以备后面查看. #include <stdio.h> #include <malloc.h> const int OK = 1; //定义正确返回 const int ERROR = -1; //定义错误的返回 const int OVERFLOW = -2; //定义溢出 //定义元素类型 typedef int ElemType; //定义返回类型 typedef int Status; typedef st

前端学数据结构之链表

前面的话 本文将介绍如何实现和使用链表这种动态的数据结构 数据结构 要存储多个元素,数组(或列表)可能是最常用的数据结构.每种语言都实现了数组.这种数据结构非常方便,提供了一个便利的[]语法来访问它的元素.然而,这种数据结构有一个缺点:(在大多数语言中)数组的大小是固定的,从数组的起点或中间插入或移除项的成本很高,因为需要移动元素 链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成.下图展示

数据结构--单向链表

C语言中,我们在使用数组时,会需要对数组进行插入和删除的操作,这时就需要移动大量的数组元素,但在C语言中,数组属于静态内存分配,数组在定义时就必须指定数组的长度或者初始化.这样程序一旦运行,数组的长度就不能再改变,若想改变,就只能修改源代码.实际使用中数组元素的个数也不能超过数组元素的最大长度,否则就会发生下标越界的错误(这是新手在初学C语言时肯定会遇到的问题,相信老师也会反复强调!!!但这种问题肯定会遇到,找半天找不到错误在哪,怪我咯???).另外如果数组元素的使用低于最大长度,又会造成系统资

数据结构:单向链表系列6--交换相邻两个节点1(交换数据域)

给定一个单向链表,编写函数交换相邻 两个元素 输入: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 输出: 2 -> 1 -> 4 -> 3 -> 6 -> 5 -> 7 输入: 1 -> 2 -> 3 -> 4 -> 5 -> 6 输出: 2 -> 1 -> 4 -> 3 -> 6 -> 5 通过观察发现:当输入的与元素个数是单数的时候,最后一位不参与交换

数据结构:链表的原理和实现

完整代码向下拉 链表是一种常用的数据结构,在插入和移除操作中有着优秀的表现,同为数据结构的数组哭晕,其实数组的访问效率比链表高多了有木有. 我们先看一下链表的样子 有同学可能要说了,这不就是我们生活中的交通工具——火车,没错链表的结构和下图简直就是一个模子刻出来的.(咳咳,忽略这灵魂的画法) 通过火车示意图可以观察到,火车由火车头和n节车厢组成,每节车厢都与下一节车厢相连,能理解这句话,链表你就掌握一半了. 以小学掌握的物品分类知识来对上图进行面向对象抽象,火车整体可以认为是链表,火车又由车厢组

数据结构-单链表-类定义2-C++

上一次的C++链表实现两个单链表的连接不太理想,此次听了一些视频课,自己补了个尾插法,很好的实现了两个链表的连接,当然了,我也是刚接触,可能是C++的一些语法还不太清楚,不过硬是花了一些时间尽量在数据结构中将c++的语言特点表现出来.一开始也是不愿意读c++的数据结构,只是一种挑战心里,不想读着读着感觉自己太low了,c++的内容更加丰富,所以还得多多练习...... 头文件 1 #ifndef LIST_H 2 #define LIST_H 3 #include <iostream> 4 5

基本数据结构:链表(list)

copy from:http://www.cppblog.com/cxiaojia/archive/2012/07/31/185760.html 基本数据结构:链表(list) 谈到链表之前,先说一下线性表.线性表是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的.线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构. 顺序存储结构就是两个相邻的元素在内存中也是相邻的.这种存储方式的优点是

(续)顺序表之单循环链表(C语言实现)

单循环链表和单链表的唯一区别在于单循环链表的最后一个节点的指针域指向第一个节点, 使得整个链表形成一个环. C实现代码如下: #include<stdio.h> typedef struct node { int data; struct node *next; }Node; //链表的初始化 Node* InitList(int number) { int i; Node *pHead=(Node *)malloc(sizeof(Node)); Node *TempHead=pHead; N