学习笔记
链表结构
单链表
示意图如下
结构特征:
- 尾节点的指针域指向NULL
- 用头节点记录链表的基地址
行为特征: - 插入或删除只需要考虑相邻节点指针的改变,其时间复杂度是O(1)
- 通过指针将零散的内存块串联起来使用
- 随机访问第K个元素的时间复杂度是O(n), 每个节点只能找到它后面的节点,因而要找到第K个元素就需要从链表头开始一个一个往后询问直到遇到第K个,没有数组那么好的性能。
下图演示插入或删除一个节点的操作
循环链表
结构特征
- 尾节点指针域指向头节点,形成一个环,其他特征和单链表一样,示意图如下
行为特征 - 约瑟夫问题
- 访问能从链表尾端直接到链表头部
双向链表
结构特征
- 每个节点不只有一个后继指针next指向后面的节点,还有一个前驱指针pre指向前面的节点。
- 存储同样多的数据比单链表占用更多的存储空间
结构示意图如下
行为特征 - 要删除节点中“值等于某个给定值”的节点,遍历查找到符合条件的节点,需要从链表头开始检查直到找到符合条件的节点,查找的时间复杂度是O(n),找到之后可以直接删除它,删除的时间复杂度是O(n),根据时间复杂度的加法法则,这种情况下的总实际那复杂度就是O(n).
- 删除给定指针指向的节点,我们已经找到了要删除的结点,但是删除某个结点 q, 需要知道其前驱结点,但单链表并不支持直接获取前驱结点,我们还是要从头结点开始遍历链表,直到 p->next=q,说明p是q的前驱结点。这种情况下,单链表的时间复杂度仍然是O(n)。而双向链表能直接获取q的前驱结点,它的时间复杂度是O(1),更高效。
- 用空间换时间的设计思想
链表vs数组性能比拼
从行为特征上来看,插入删除和随机访问的时间复杂度性能刚好相反,如下
从结构特征上来看,数组使用连续内存空间,可借助CPU缓存机制,预读数据,访问效率高,但数组大小固定,可能系统没有足够大的连续内存空间可供分配。链表使用离散的内存空间,在系统运行时获得内存和运行结束时是否内存,动态的内存分配和释放能充分利用内存从而能节省内存使用,频繁的申请和释放肯能会产生内存碎片。
实现LRU缓存淘汰算法
所谓缓存策略, 就是当缓存满了之后, 又有新数据需要加入内存时, 我们怎么从缓存中为新数据腾出空间的策略。这个权衡的过程就是所谓的缓存策略。
LRU, Least Recently Used的简写, 即近期最少使用算法。 该算法依据与程序的局部性原理, 其淘汰旧数据的策略是, 距离当前最久没有被访问过的数据应该被淘汰。
基于链表实现
/*LRU algorithm*/
#include <stdio.h>
#include <stdlib.h>
#define LOG() printf("[%d]\n", __LINE__)
#define LRU_CAPACITY 10
struct nodeSoldier{
int number; /*the soldier's number*/
struct nodeSoldier *next; /*point to next soldier*/
}nodeSoldier;
typedef struct nodeSoldier *pNodeSoldier;
/*insert node in the header of the linked list*/
void insertLikedlist(pNodeSoldier *head, int *pdata)
{
pNodeSoldier pNewSoldier;
pNodeSoldier probe = *head;
pNewSoldier =(pNodeSoldier)malloc(sizeof(struct nodeSoldier));
if (NULL != pNewSoldier)
{
pNewSoldier->number = *pdata;
pNewSoldier->next = probe;
*head = pNewSoldier;
}
else
{
printf("malloc error!\n");
}
}
/*deleted matched node*/
pNodeSoldier deleteLinkedlist(pNodeSoldier head, int data)
{
pNodeSoldier probe = head;
pNodeSoldier preProbe = head;
while(data != probe->number)
{
probe = probe->next;
}
/*the node probe point to*/
if (NULL != probe)
{
/*delete it*/
if (probe == head) /*is the first node*/
{
head = probe->next;
}
else
{
/*find the node before it*/
preProbe = head;
while(preProbe->next != probe)
{
preProbe = preProbe->next;
}
preProbe->next = probe->next;
}
free(probe);
}
else
{
printf("no found\n");
}
return (head);
}
/*Create LRU catch with the single linked list*/
/*soldier is named as 1,2,3,...capacity in turn*/
pNodeSoldier LRUCreate(const int capacity)
{
pNodeSoldier head = NULL;
pNodeSoldier probe = NULL;
int counter = capacity;
/*if length of the linked list is bigger than 1, then adding one more node*/
if (capacity > 1)
{
/*initialize the first node firstly*/
head = (pNodeSoldier)malloc(sizeof(struct nodeSoldier));
if(NULL != head)
{
head->next = NULL;
head->number = capacity;
}
else
{
/*malloc error*/
printf("malloc error!\n");
}
/*insert the remaining nodes in the header of the linked list one by one*/
for (; counter > 1; counter--)
{
probe = (pNodeSoldier)malloc(sizeof(struct nodeSoldier));
if (NULL != probe)
{
probe->next = head;
probe->number = counter - 1;
head = probe;
}
else
{
/*malloc error*/
printf("malloc error!\n");
}
}
}
else if (capacity == 1)
{
/*only 1 node in the linked list*/
head = (pNodeSoldier)malloc(sizeof(struct nodeSoldier));
if(NULL != head)
{
head->next = NULL;
head->number = 1;
}
else
{
/*malloc error*/
printf("malloc error!\n");
}
}
else
{
printf("capacity error! it should be bigger than 0!\n");
/*capacity error*/
}
return (head);
}
/*calculate the length of the catch*/
int LRUCalcuLen(pNodeSoldier const head)
{
int counter = 0;
pNodeSoldier probe = head;
if (NULL != head)
{
while(NULL != probe)
{
++counter;
probe = probe->next;
}
}
else
{
printf("input parameters error!\n");
/*NULL pointer from the input parameters of the function*/
}
return (counter); /*cann't return the pointer variable or reference to the stack,but others'variable because the stack will be destroied when the function exit*/
}
/*output the LRU catch*/
void LRUOutput(pNodeSoldier const head)
{
pNodeSoldier probe = head;
printf("--------------output the linked list----------\n");
if (NULL != head)
{
for(; probe != NULL; )
{
printf("Node's number is %d\n", probe->number);
probe = probe->next;
}
}
else
{
printf("input parameters error!\n");
/*NULL pointer from the input parameters of the function*/
}
}
/*destroy the LRU catch*/
void LRUDestroy(pNodeSoldier head)
{
pNodeSoldier probe = head;
pNodeSoldier nextProbe = head;
if (NULL != head)
{
for(; probe != NULL; )
{
nextProbe = probe->next;
free(probe);
probe = nextProbe;
}
}
else
{
printf("input parameters error!\n");
/*NULL pointer from the input parameters of the function*/
}
}
#if 1
pNodeSoldier LRUAlogrithm(pNodeSoldier head, int soldierNumber)
{
pNodeSoldier preProbe = head;
pNodeSoldier probe = head;
pNodeSoldier pNewSoldier = NULL;
int length = LRUCalcuLen(head);
if (NULL != head)
{
/*find it in the link list*/
for(; NULL != probe; )
{
if (probe->number != soldierNumber)
{
/*I don't find it, continue to find it until the last node probe point to*/
probe = probe->next;
}
else
{
/*Yes, I find it, I can leave*/
break;
}
}
/*Link list doesn't have it, then add it in the header of the linked list*/
if (NULL == probe)
{
/*Buffer is not full, then put it in the header*/
if (LRU_CAPACITY > length)
{
insertLikedlist(&head, &soldierNumber);
}
else/*Buffer is full, then delete the last node, then put it in the header*/
{
/*find the last node*/
probe = head;
while(NULL != probe->next)
{
probe = probe->next;
}
/*probe point to the last node,find the node before it*/
preProbe = head;
while (preProbe->next != probe)
{
preProbe = preProbe->next;
}
/*delete the last node*/
preProbe->next = NULL;
free(probe);
insertLikedlist(&head, &soldierNumber);
}
}
else/*move it to the header of the linked list*/
{
/*find the node before it*/
while(preProbe->next != probe)
{
preProbe = preProbe->next;
}
/*connect to the next of the node which probe point to*/
preProbe->next = probe->next;
/*head connect to the node which probe point to */
probe->next = head;
head = probe;
}
}
else
{
/*NULL address access from input parameters of the function*/
}
return (head);
}
#endif
void main(void)
{
pNodeSoldier head = NULL;
pNodeSoldier pNewNode = NULL;
int soldierNumber = 14;
int length = 0;
printf("-----------------test case 0: node's number can't zero------------------\n");
head = LRUCreate(0);
printf("-----------------test case 1: only one node ------------------\n");
head = LRUCreate(1);
LRUDestroy(head);
printf("-----------------test case 2: big enough catch------------------\n");
head = LRUCreate(13);
printf("the length of the LRU is %d\n", LRUCalcuLen(head));
LRUOutput(head);
printf("-----------------test case 3: insert node------------------\n");
insertLikedlist(&head, &soldierNumber);
LRUOutput(head);
printf("-----------------test case 4: delete the first node------------------\n");
head = deleteLinkedlist(head, 14);
LRUOutput(head);
printf("-----------------test case 5: the node deleted is the middle node: ------------------\n");
head = deleteLinkedlist(head, 7);
LRUOutput(head);
printf("-----------------test case 6: the node deleted is the last node: ------------------\n");
head = deleteLinkedlist(head, 13);
LRUOutput(head);
printf("-----------------test case 7: put new data in the header after deleting the last node if catch is full ------------------\n");
head = LRUAlogrithm(head, 15);
LRUOutput(head);
printf("-----------------test case 8: put new data in the header if catch is not full ------------------\n");
head = deleteLinkedlist(head, 8);
head = deleteLinkedlist(head, 9);
head = deleteLinkedlist(head, 10);
head = deleteLinkedlist(head, 11);
LRUOutput(head);
head = LRUAlogrithm(head, 16);
LRUOutput(head);
printf("-----------------test case 9: move the existent data to the header ------------------\n");
head = LRUAlogrithm(head, 4);
LRUOutput(head);
LRUDestroy(head);
}