数据结构01----C语言单向链表

下面的叙述如果有什么错误,还请批评指正,谢谢。

一、数据结构与算法简介

1.什么是数据结构?
简单的说法,就是数据以及数据之间存在的关系
数据之间的关系分为:逻辑关系和物理关系(逻辑结构和物理结构)

逻辑关系:即人为赋予的关系:
一对一:线性表  栈  队列
一对多:树
多对多:图

物理关系:数据在计算机中的实际存储。
这就告诉我们,在选择数据的存储结构的时候,需要我们理清楚数据之间结构,从而选择一个合适的物理结构。

2.数据结构与算法
有这么一个式子,相信大家不会陌生:

							程序=结构+算法

数据结构上面已经介绍过,那么什么是算法呢?
所谓的算法就是要完成一个任务具体的实现步骤,那么这个步骤肯定是有优劣之分的,一个好的算法,步骤少,效率高,这也是作为程序员一直追求的东西。这里就引出了两个概念:时间复杂度和空间复杂度,一个算法的好坏就是由这两个复杂度来衡量的。

时间复杂度:大O估算法 O(频度)
i+=1; O(1)
for(i=0;i<n;i++) {} O(n)
for(i=0;i<n;i++){for(j=0;j<n;j++){}} O(n^2)
这个是不是一看就明白了如何计算时间复杂度?

空间复杂度
是对一个算法在运行过程中临时占用存储空间大小的量度。一般情况下,时间复杂度与空间复杂度不可兼得:减少时间复杂度,就势必会导致空间复杂度的增大,反过来,想要减少空间复杂度,就肯定会导致时间复杂度的增大。这就需要我们根据实际需要来做合理取舍了。

好,有关数据结构和算法的概念,就说这么多 ,下面我们的重点,放在数据的存储结构上,注意,需要学习的是他的思想,而非代码,所以重在理解!!!

二、四大逻辑结构

1.集合结构:所有元素在同一个集合上,他们之间没啥关系,这里不做过多的介绍 ;

2.线性结构:一对一的关系:如链表、栈、队列,这里使我们掌握的重点;

3.树形结构:一对多的关系,我们主要学习二叉树;

4.图形结构:多对多的关系,这个在这里也不做过多的叙述。

三、链表

链表属于线性表,线性表的存储分为顺序存储和链式存储,我们接下来要讲的链表,属于链式存储,具体的关系,如下图所示:
在这里插入图片描述
这里需要特别注意的是,我们学习单链表和双向不循环链表都是为了学习双向循环链表,在今后的工作学习中,大多数用的都是双向循环链表。下面的介绍,我们就按照单向不循环->单向循环->双向不循环->双向循环,这样循序渐进的方式来讲述。注意,对于数据结构,我们更重要的是理解,而不只是盯着代码不放,切记!

1.单向不循环链表
他的结构示意图如下:
(忽略其中的错别字,嘿嘿)
在这里插入图片描述
链表,分为指针域和数据域,二者构成一个节点(对应上面一个方框),为了方便理解,如上图,我们把方框的上面部分叫做“数据域”,下面部分看成“指针域”(当然,这个上面下面的顺序无所谓),对于单向链表来说,每个节点的指针(next)都指向下一个节点的地址,最后一个节点的next,指向的是NULL(不循环链表)或者头节点(循环链表)。其中,头结点只是为了方便数据的索引而存在,他的数据域不存放有效数据,有时候为了方便,我们会在头结点的数据域里面存放链表节点的个数(链表的大小)。从首元节点开始,存放有效数据的第一个元素。在C语言中,我们可以这样来表示一个节点:

struct Node
{
	int m_Data;
	struct Node *next;
};
typedef struct Node node_t;

当然,数据域不仅仅可以是单一的数据类型,也可以是多个数据类型,还可以是一个结构体,所以,还可以这样:

struct Data
{
	char 	m_Name[20];
	int 	m_Age;
	double 	m_score;
};
typedef struct Data data_t;

struct Node 
{
	data_t 		m_Data;
	struct Node *next;
};
typedef struct Node node_t;

这个不管怎么样,只要符合上面的规则,都是可以的,这里只是举了两个例子。

学了数据结构的朋友都知道,对链表的操作,无非就是增删改查。

下面我们来看看操作步骤:

  • 1.确定单向链表节点;
  • 2.初始化链表;
  • 3.插入某个数据到某个位置;
  • 4.打印链表;
  • 5.删除某个位置的数据;
  • 6.查询某个数据在节点中的位置
  • 7.更改某个数据为新的数据

在这里插入图片描述

对照上面的视图,理解一下下面的C语言代码:

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

#define SYSERR(x,option,y,a,k) if((x)option(y))\
{\
    printf("%s:%s:%d:%s\n",__FILE__,__func__,__LINE__,a);\
    return (k);\
}

struct Node
{
    int data;
    int list_length;
    struct Node *next;
};

typedef struct Node node_t;

/*
 *链表初始化
 *参数:void
 *返回值:初始化后的链表头结点
 */
node_t* listInit(void)
{
    node_t* head = (node_t *)malloc(sizeof(node_t));
    SYSERR(head,==,NULL,"[malloc err]",(node_t *)-1); /* 判断是否空间申请失败 */
    head->next        = NULL;
    head->list_length = 0;
    return head;
}

/*
 *头插
 *参数:head:要插入的链表   dat:要插入的数据
 *返回值:返回头插后的链表头结点
 */
node_t* insertHead(node_t* head,int dat)
{
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp,==,NULL,"[malloc err]",NULL);
    temp->data = dat;
    temp->next = head->next;
    head->next = temp;
    head->list_length++;
    return head;
}

/*
 *尾插
 *参数:head:要插入的链表   dat:要插入的数据
 *返回值:返回尾插后的链表头结点
 */
node_t* insertTail(node_t* head,int dat)
{
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp,==,NULL,"[malloc err]",NULL);
    node_t* temp1 = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp1,==,NULL,"[malloc err]",NULL);
    temp = head;
    for(;temp->next != NULL; temp = temp->next);
    temp1->data = dat;
    temp1->next = NULL;
    temp->next = temp1;
    head->list_length++;
    return head;
}

/*
 *打印链表
 *参数:head:要打印的链表 
 *返回值:void
 */
int displayList(node_t* head)
{
    SYSERR(head,==,NULL,"[list is empty]",-1);
    node_t* temp = head->next;
    for(;temp != NULL; temp = temp->next)
    {
        printf("-> %d ",temp->data);
    }
    putchar(10);
    return 0;
}

/*
 *删除节点
 *参数:head:要删除节点的链表  dat:要删除的数据 
 *返回值:删除节点后的链表首节点
 */
node_t* deleteNode(node_t* head,int dat)
{
    int num = 0,i = 0,flag = 0;
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = head->next;
    for(; temp != NULL; temp = temp->next)
    {
        num++;
        if(temp->data == dat)
        {
            flag++;
            break;
        }
        if(flag != 1 && num == head->list_length) {printf("list has not %d\n",dat);return head;}
    }
    node_t* node = head->next;
    node_t* q = node;
    if(num == 1) /* 第一个 */
    {
        head->next = node->next;
        head->list_length--;
        free(q);
    }
    else
    {
        for(i = 0; i < num-2; i++)
        {
            node = node->next;
        }
        node_t* p = node->next;
        node->next = node->next->next;
        head->list_length--;
        free(p);
    }
    return head;
}

int main(void)
{
    node_t* head = listInit();
#if 1
    head = insertHead(head,2);
    head = insertHead(head,5);
    head = insertHead(head,6);
    head = insertHead(head,7);
    head = insertHead(head,53);
    head = insertHead(head,34);
    printf("头插后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif

#if 1
    head = insertTail(head,78);
    head = insertTail(head,23);
    head = insertTail(head,100);
    head = insertTail(head,75);
    head = insertTail(head,37);
    head = insertTail(head,29);
    printf("尾插后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif

#if 1
    head = deleteNode(head,7);
    printf("删除一个节点后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif
    return 0;
}

代码运行结果如下:
在这里插入图片描述
上面例程中,只有初始化、头插、尾插、删除某个节点,只要弄懂这些,剩下的修改查询都是很简单的,这里就不写了。接下来的例程中同样只有这几个部分。这里就一并说明了。

2.单向循环链表
和上面的不循环链表类似,他的模型如下:

在这里插入图片描述
区别就在于最后一个节点的next指向的是头结点而已。那么对于他的操作也是一样的:

  • 1.确定单向链表节点;
  • 2.初始化链表;
  • 3.插入某个数据到某个位置;
  • 4.打印链表;
  • 5.删除某个位置的数据;
  • 6.查询某个数据在节点中的位置
  • 7.更改某个数据为新的数据

注意下面程序上的细微差别:

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

#define SYSERR(x,option,y,b,k) if((x)option(y))\
{\
    printf("%s:%s:%d:%s\n",__FILE__,__func__,__LINE__,b);\
    return (k);\
}

struct Node
{
    int data;
    int list_length;
    struct Node *next;
};

typedef struct Node node_t;

/*
 *链表初始化
 *参数:void
 *返回值:初始化后的链表头结点
 */
node_t* listInit(void)
{
    node_t* head = (node_t *)malloc(sizeof(node_t));
    SYSERR(head,==,NULL,"[malloc err]",(node_t *)-1); /* 判断是否空间申请失败 */
    head->next        = head;  /* 自己指向自己 */
    head->list_length = 0;
    return head;
}

/*
 *头插
 *参数:head:要插入的链表   dat:要插入的数据
 *返回值:返回头插后的链表头结点
 */
node_t* insertHead(node_t* head,int dat)
{
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp,==,NULL,"[malloc err]",NULL);
    temp->data = dat;
    temp->next = head->next;
    head->next = temp;
    head->list_length++;
    return head;
}

/*
 *尾插
 *参数:head:要插入的链表   dat:要插入的数据
 *返回值:返回尾插后的链表头结点
 */
node_t* insertTail(node_t* head,int dat)
{
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp,==,NULL,"[malloc err]",NULL);
    node_t* temp1 = (node_t *)malloc(sizeof(node_t));
    SYSERR(temp1,==,NULL,"[malloc err]",NULL);
    temp = head->next;
    for(;temp->next != head; temp = temp->next);
    temp1->data = dat;
    temp1->next = head;
    temp->next = temp1;
    head->list_length++;
    return head;
}

/*
 *打印链表
 *参数:head:要打印的链表 
 *返回值:void
 */
int displayList(node_t* head)
{
    SYSERR(head,==,NULL,"[list is empty]",-1);
    node_t* temp = head->next;
    for(;temp != head; temp = temp->next)
    {
        printf("-> %d ",temp->data);
    }
    putchar(10);
    return 0;
}

/*
 *删除节点
 *参数:head:要删除节点的链表  dat:要删除的数据 
 *返回值:删除节点后的链表首节点
 */
node_t* deleteNode(node_t* head,int dat)
{
    int num = 0,i = 0,flag = 0;
    SYSERR(head,==,NULL,"[list is empty]",NULL);
    node_t* temp = head->next;
    for(; temp != head; temp = temp->next)
    {
        num++;
        if(temp->data == dat)
        {
            flag = 1;
            break;
        }
        if(flag != 1 && num == head->list_length){printf("list has not %d\n",dat);return head;}
    }
    node_t* node = head->next;
    node_t* q = node;
    if(num == 1)
    {
        head->next = node->next;
        head->list_length--;
        free(q);
    }
    else
    {
        for(i = 0; i < num-2; i++)
        {
            node = node->next;
        }
        node_t* p = node->next;
        node->next = node->next->next;
        head->list_length--;
        free(p);
    }
    return head;
}

int main(void)
{ 
    node_t* head = listInit();
#if 1
    head = insertHead(head,2);
    head = insertHead(head,5);
    head = insertHead(head,6);
    head = insertHead(head,7);
    head = insertHead(head,53);
    head = insertHead(head,34);
    printf("头插后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif

#if 1
    head = insertTail(head,78);
    head = insertTail(head,23);
    head = insertTail(head,100);
    head = insertTail(head,75);
    head = insertTail(head,37);
    head = insertTail(head,29);
    printf("尾插后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif

#if 1
    head = deleteNode(head,53);
    printf("删除一个节点后:");
    (void)displayList(head);
    printf("链表长度为:%d\n\n",head->list_length);
#endif
    return 0;
}

在这里插入图片描述

运行结从上面两个链表来看,无论是循环还是不循环的,都存在着以下的问题:
**只能是一个方向遍历,也就是说,只能找到后继,无法找到前驱。这给数据的操作带来一定的复杂度。**有关前驱和后继,如下图:
在这里插入图片描述
双链表请看:
数据结构02----C语言双向链表
栈:
数据结构03----C语言栈
队列:
数据结构04----队列

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

all of the time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值