链表
定义链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表
特征链表主要有以下几大特性:
-
1、解决数组无法存储多种数据类型的问题。
-
2、解决数组中,元素个数无法改变的限制(C99的变长数组,C++也有变长数组可以实现)。
-
3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。
先来感性的认识一下链表,我们先来认识下简单的链表
从这幅图我们得出以下信息:
这个简单链表的构成:
头指针(Header),若干个节点(节点包括了数据域和指针域),最后一个节点要指向空。
实现原理: 头指针指向链表的第一个节点,然后第一个节点中的指针指向下一个节点,然后依次指到最后一个节点,这样就构成了一条链表。
接下来看看链表的数据结构:
//定义一个结构体 struct list_node { int data ; //数据域,用于存储数据 struct list_node *next ; //指针,可以用来访问节点数据,也可以遍历,指向下一个节点 };
那么如何来创建一个链表的一个节点呢?
我们写个程序演示一下:
#include <stdio.h> #include <stdlib.h> #include <string.h> struct list_node { int data ; struct list_node *next ; }; typedef struct list_node list_single ; //用list_single代替list_node类型,下面见到list_single就包含了上面自定义的结构体的数据类型 int main(void) { list_single *node = NULL ; //1、首先,当然是定义一个头指针 node = (list_single *)malloc(sizeof(list_single)); //2、然后分配内存空间,malloc函数功能是动态内存分配,用于申请一块连续的指定大小的内存块区域以 void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。具体可以自己查阅相关书籍和网页。 if(node == NULL) { printf("malloc fair!\n"); } memset(node,0,sizeof(list_single)); //3、清一下 memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思 node->data = 100 ; //4、给链表节点的数据赋值 node->next = NULL ; //5、将链表的指针域指向空 printf("%d\n",node->data); free(node); return 0 ; }
那么,这仅仅只是创建一个链表中的一个节点,为了好看,我们把创建节点封装成函数,以后想创建多少个节点,我们就可以反复调用一个函数来创建,会很方便:
list_single *create_list_node(int data) { list_single *node = NULL ; node = (list_single *)malloc(sizeof(list_single)); if(node == NULL){ printf("malloc fair!\n"); } memset(node,0,sizeof(list_single)); node->data = 100 ; node->next = NULL ; return node ; }
接下来在程序上完成的程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> struct list_node { int data ; struct list_node *next ; }; typedef struct list_node list_single ; list_single *create_list_node(int data) { list_single *node = NULL ; node = (list_single *)malloc(sizeof(list_single)); if(node == NULL){ printf("malloc fair!\n"); } memset(node,0,sizeof(list_single)); node->data = 100 ; node->next = NULL ; return node ; } int main(void) { int data = 100 ; list_single *node_ptr = create_list_node(data); //创建一个节点 printf("node_ptr->data=%d\n",node_ptr->data); //打印节点里的数据 printf("node_ptr->next=%d\n",node_ptr->next); free(node_ptr); return 0 ; }
运行结果:
这样我们就完成一个链表节点的创建了,那么它现在的样子如下图:
链表的结构里,数据存储了100,因为这个链表只有一个节点,所以它的指针域指向了NULL。
包括了链表生成、删除、计算等等。