赞
踩
注:会持续更新
首先我们先来看看链表的几个特点:离散的内存分布、快速插入/删除
最直观的就是将其和线性数据结构列表对比:
可见,链表的内存分布是离散的,想要知道其中一个目标结点的地址,首先要知道其前驱结点,根据前驱结点的指针来找到目标结点。
至于快速插入和删除的特点,先别急,我们先看下链表的分类。
常见种类:单向链表、双向链表、循环链表、双向循环链表
单向链表:
单链表即只能朝着一个方向去遍历,根据结点的next指针来找到目标结点,尾结点指向空指针NULL
双向链表:[java中的 LinkedHashMap就用的这个容器]
双向链表即只能朝着前后个方向去遍历,要想找到目标结点,需要先找到其前驱结点或后继结点根据next或pre指针找到目标结点,尾结点的next指向空指针,pre指针指向前一个结点。
循环链表:
循环链表就是尾结点的后继指针指向的是头节点,这样从链尾到链头就很方便。很适合解决“问题中带有循环提示的问题”
双向循环链表:
到这里,就算不给你双向链表的结构图你应该也能猜到了,是的,就是向双向链表一样每个结点加上前驱指针,头结点的前驱指针指向了最后的尾结点。
常见应用场景:
为什么要有缓存淘汰算法?
因为缓存的大小有限,当缓存被用满时,应该决定哪些数据被清理出去,哪些应该被保留。
常见的有三种:
- 先进先出策略FIFO
First in First out
- 最少使用策略LFU
Least Frequently Used
- 最近最少使用策略LRU
Least Recently Used
如何采用链表实现LRU:
链表Vs数组
数组使用的是连续的内存空间,可以借助CPU缓存机制,预读数组中的数据,访问效率高;链表不是连续存储的,不能够直接访问,必须进行遍历才能找到目标结点,因此访问效率低。
这里的CPU缓存机制指的是什么??
- CPU在从内存读取数据的时候,会先把读取到的数据加载到CPU的缓存中。而CPU每次从内存读取数据并不是只读取那个特定要访问的地址,而是读取一个数据块并保存到CPU缓存中,然后下次访问内存数据的时候就会先从CPU缓存开始查找,如果找到就不需要再从内存中取。这样就实现了比内存访问速度更快的机制。因为数组存储空间是连续的,所以在加载的时候可以把以后的几个下标元素同时加载到CPU缓存,这样执行速度会快于存储空间不连续的链表存储。这就是所谓的“局部性原理”
数组的缺点是大小固定,如果你想要申请的内存过大,可能系统没有那么大的连续内存空间给你,那么就会报错。同时,当你申请的数组不够用了,需要重新申请一个新的更大的数组,此时就需要将原本的数据全部拷贝过去,显然就很浪费时间和效率。
同时,你可能也注意到了,链表每个结点不仅需要保存值,还要有一个指针来指向后继结点,这样链表存储信息的利用率可能就没有数组那么高[因为需要分出一部分内存存储指针]。因此,如果在对内存要求很苛刻的情况下,最好选择数组。
链表操作的注意事项:
警惕指针丢失和内存泄露。
比如在插入结点时候一定要先
将待插入结点指针指向被插入结点的下一个结点,然后修改被插入结点next指向待插入结点,防止指针丢失
留意特殊情况。
链表为空,程序能否正常工作?
通常就是特殊判断
链表只包含一个结点或两个结点,程序有没有Bug?
处理头结点和尾结点时,程序能够正常工作?
通常
加入哨兵结点或者if条件判断解决
利用画图来辅助自己。
相关代码实现:
包含了单向链表、双向链表、循环链表的C语言实现代码:
link.h
#ifndef LINK_H_ #define LINK_H_ #include<stdio.h> #include<stdlib.h> #include<stdbool.h> #include<math.h> #include<string.h> typedef char elemtype; #define ERROR 0 #define OK 1 //单向链表// typedef struct Node{ elemtype data; struct Node * next; }SNode,*SLinklist; //单链表函数// bool Init_SLinkList(SLinklist *L); bool Creat_SLinkList_Tail(SLinklist *L); bool Creat_SLinkList_Head(SLinklist *L); SNode *Find_SLinkNode_Pos(SLinklist L,int i); SNode *Find_SLinkNode_Val(SLinklist L,elemtype key); int Get_SLink_Len(SLinklist L); void Print_SLink(SLinklist L); void Print_Node(SNode *node); int InsertSLinkList(SLinklist *L,int n,elemtype data); int Delete_DatainList(SLinklist *L,int n); //*********// //双向链表// typedef struct DNode{ elemtype data; struct DNode * pre; struct DNode * next; }DNode,*DLinklist; //双向链表函数// bool Init_DLinklist(DLinklist *L); bool Creat_DLinklist_Tail(DLinklist *L); bool Print_DLinkist(DLinklist L); //********// //单向循环链表// typedef struct SCNode{ elemtype data; struct SCNode * next; }SCNode,*SCLinklist; //单向循环链表函数// bool Init_SCLinklist(SCLinklist *L); bool Creat_SCLinklist_Tail(SCLinklist *L); bool Print_SCLinkist(SCLinklist L); //********// #endif
link.c
//单向链表// //初始化单链表 bool Init_SLinkList(SLinklist *L) { (*L) = (SLinklist)malloc(sizeof(SNode)); if(!(*L)) { printf("malloc error!"); return ERROR; } (*L)->next =NULL; return OK; } //尾插法建立单链表 bool Creat_SLinkList_Tail(SLinklist *L) { elemtype e; SLinklist Node_L,Node_new; Node_L = (*L); printf("Input data... :"); scanf("%c",&e); while ('#' != e) { Node_new = (SLinklist)malloc(sizeof(SNode)); if(!Node_new) { printf("malloc error!"); return ERROR; } Node_new->data = e; Node_new->next = Node_L->next; Node_L->next = Node_new; Node_L = Node_new; scanf("%c",&e); } getchar(); return OK; } //头插发建立单链表 bool Creat_SLinkList_Head(SLinklist *L) { elemtype e; SLinklist Node_L,Node_new; Node_L = (*L); printf("Input data... :"); scanf("%c",&e); while ('#' != e) { Node_new = (SLinklist)malloc(sizeof(SNode)); if(!Node_new) { printf("malloc error!"); return ERROR; } Node_new->data = e; Node_new->next = Node_L->next; Node_L->next = Node_new; scanf("%c",&e); } getchar(); return OK; } //查找单链表中第i个结点,按照位置查找 SNode *Find_SLinkNode_Pos(SLinklist L,int i) { int count=0; SLinklist Node_find; Node_find = L; while(Node_find != NULL) { Node_find = Node_find->next; count ++; if(count == i) { return Node_find; } } printf("What you put n is error!\r\n"); return NULL; } //按照值查找单链表 SNode *Find_SLinkNode_Val(SLinklist L,elemtype key) { SLinklist Node_find; Node_find = L; while (Node_find != NULL) { Node_find = Node_find->next; if (Node_find->data == key) { return Node_find; } } printf("No find what you want node!\r\n"); return NULL; } //计算单链表长度 int Get_SLink_Len(SLinklist L) { int len=0; SLinklist Node_L; Node_L = L->next; while (Node_L != NULL) { len++; Node_L=Node_L->next; } printf("The Link's len is %d\r\n",len); return len; } //打印单链表// void Print_SLink(SLinklist L) { SLinklist Node_L; Node_L = L->next; while (Node_L) { printf("%c ",Node_L->data); Node_L = Node_L->next; } printf("\r\n"); } void Print_Node(SNode *node) { printf("The Node data is %c\r\n",node->data); } //向单链表中第n个结点前(第n-1个结点后)插入一个结点// int InsertSLinkList(SLinklist *L,int n,elemtype data) { int i,j; SLinklist node_L,node_new; node_L=(*L)->next; //找到第n-1个结点 n=n-2; while(n) { if(node_L == NULL) { break; } node_L=node_L->next; n--; } if(n) { printf("Input 'n' is error!"); return ERROR; } node_new=(SLinklist)malloc(sizeof(SNode)); node_new->next=node_L->next; node_L->next=node_new; node_new->data=data; return OK; } //删除单链表中第n个元素// int Delete_DatainList(SLinklist *L,int n) { int i,j; SLinklist node_L,node_delete; node_L=(*L)->next; //找到第n-1个结点 n=n-2; while(n) { if(node_L == NULL) { break; } node_L=node_L->next; n--; } if(n) { printf("Input 'n' is error!"); return ERROR; } node_delete=node_L->next; node_L->next=node_delete->next; free(node_delete); return OK; } //双向链表// bool Init_DLinklist(DLinklist *L) { (*L) = (DLinklist)malloc(sizeof(DNode)); if(!(*L)) { printf("malloc error!\n"); return ERROR; } (*L)->next = NULL; (*L)->pre = NULL; return OK; } bool Creat_DLinklist_Tail(DLinklist *L) { elemtype e; DLinklist Node_L,Node_new; Node_L = (*L); printf("Input data... :"); scanf("%c",&e); while ('#' != e) { Node_new = (DLinklist)malloc(sizeof(SNode)); if(!Node_new) { printf("malloc error!"); return ERROR; } Node_new->data = e; Node_new->next = Node_L->next; Node_new->pre = Node_L; Node_L->next = Node_new; Node_L = Node_new; scanf("%c",&e); } getchar(); return OK; } //打印双向链表 bool Print_DLinkist(DLinklist L) { DLinklist Node_L; Node_L = L->next; while (Node_L) { printf("%c ",Node_L->data); Node_L = Node_L->next; } printf("\r\n"); } //单向循环链表// bool Init_SCLinklist(SCLinklist *L) { (*L) = (SCLinklist)malloc(sizeof(SCNode)); if(!(*L)) { printf("malloc error!"); return ERROR; } (*L)->next =(*L); return OK; } //尾插法创建循环链表 bool Creat_SCLinklist_Tail(SCLinklist *L) { elemtype e; SCLinklist Node_L,Node_new; Node_L = (*L); printf("Input data... :"); scanf("%c",&e); while ('#' != e) { Node_new = (SCLinklist)malloc(sizeof(SCNode)); if(!Node_new) { printf("malloc error!"); return ERROR; } Node_new->data = e; Node_new->next = Node_L->next; Node_L->next = Node_new; Node_L = Node_new; scanf("%c",&e); } getchar(); return OK; } //打印循环链表 bool Print_SCLinkist(SCLinklist L) { SCLinklist Node_L; Node_L = L->next; while (Node_L != L) { printf("%c ",Node_L->data); Node_L = Node_L->next; } printf("\r\n"); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。