链表

链表

链表通过“指针”将一组零散的内存块串联起来,并不需要一块连续的内存空间。

链表的插入删除操作时间复杂度是O(1),随机访问则需要O(n)的时间复杂度。

循环链表优点:适用于处理的数据具有环形结构特点。约瑟夫问题

删除操作

  • 删除结点中“值等于某个给定值”的结点

    总时间复杂度O(n):删除操作O(1),遍历查找O(n)。

  • 删除给定指针指向的结点

    单链表O(n)时间复杂度,双向链表O(1)

    双向链表费内存,但是其插入、删除、查找操作都比单链表有优势。

对于执行较慢的程序,可以通过消耗更多的内存(空间换时间)来进行优化。

对于消耗内存过多的程序,可以通过消耗更多的时间(时间换空间)来降低内存的消耗。

数组与链表

借助CPU的缓存机制,预读数组中的数据,所以访问效率更高。

链表对CPU缓存不友好,没办法有效预读。

数组容易出现“内存不足(out of memory)”,链表天然支持动态扩容。

CPU在从内存读取数据的时候,会先把读取到的数据加载到CPU的缓存中,而CPU每次从内存读取数据并不是制度去那个特定要访问的地址,而是读取一个数据块,并保存到CPU缓存中。然后下次访问内存数据的时候就会先从CPU缓存开始查找。

基于链表实现LRU缓存淘汰算法

缓存淘汰策略有三种:

先进先出策略FIFO

最少使用策略LFU

最近最少使用策略LRU

思路:

维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的,当有一个新的数据被访问时,从链表头开始顺序遍历链表。

1.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。

2.如果此数据没有在缓存链表中,又可以分为两种情况:

如果此时缓存未满,则将此结点直接插入到链表头部;

如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表头部;

如果字符串通过单链表存储,如何判断一个回文串

1.快慢指针定位中间节点

2.从中间节点对后半部分逆序

3.前后半部分比较,判断是否为回文

4.后半部分逆序复原

  • 检查链表代码是否正确的边界条件
  • 如果链表为空时,代码是否能正常工作
  • 如果链表只包含一个结点时,代码是否能正常工作
  • 如果链表只包含两个结点时,代码是否能正常工作
  • 代码逻辑在处理头结点和尾结点时,是否能正常工作
链表练习

单链表反转

#递归方式实现
按照我的理解,简单来说就是从要反转的链表最后两个开始,最后一个指向前一个,断开前一个指向最后一个的关系,再递归进行。
网上的解答,用C++实现,我搬过来了
#include <iostream>
using namespace std;
struct ListNode
{
        int value;
        ListNode *next;
};
void CreateList(ListNode* &head,int data)
{
        ListNode *p =(ListNode*)malloc(sizeof(ListNode));
        p->value=data;
        p->next =NULL;
        if (head == NULL)
        {   
                head=p;
                return;
        }    
        p->next=head;
        head=p;
}    
void printList(ListNode* head)
{
        ListNode* p = head;
        while (p != NULL)
        {   
                cout << p->value<<" ";
                p=p->next;
        }   
        cout << endl;
}
ListNode* ReverseList(ListNode* head)
{
        if (head == NULL || head->next == NULL)
                return head;
        else
        {
                ListNode* newhead=ReverseList(head->next);
                head->next->next=head;
                head->next=NULL;
                return newhead;
        }

}
int main()
{
        ListNode* head=NULL;
        for(int i=0;i<9;i++)
                CreateList(head,i);
        printList(head);
        head=ReverseList(head);
        printList(head);
//system("pause");
        return 0;
我反正用gcc,g++都没运行出来。
附参考原文:https://blog.csdn.net/geekmanong/article/details/51097196

链表中环的检测

PSListNode HasCycle(PSListNode pHead)
{
    if ((NULL == pHead) || (NULL == pHead->pNextNode))
    {
        return NULL;
    }
    else
    {
        PSListNode pFast = pHead->pNextNode->pNextNode;
        PSListNode pSlow = pHead->pNextNode;
        //利用快慢指针,让快指针每次走两步,慢指针每次走一步,要是快指针没有走到NULL,且快指针与慢指针指向相同就说明是有环
        while (pFast != pSlow)
        {
            //快指针要是没有指向为空,那么慢指针就不可能指向空(快指针走得快)
            if (NULL == pFast)
            {
                return;
            }
            else
            {
                pFast = pFast->pNextNode;
                pSlow = pSlow->pNextNode;   
                if (NULL == pFast)
                {
                    return;
                }
                else
                {
                    pFast = pFast->pNextNode;
                }
            }
        }
        return pFast;
    }
}

int GetCyleLen(PSListNode pMeetNode)
{
    //默认传的参数是HasCycle函数返回的环中的一个结点
    if (NULL == pMeetNode)
    {
        return 0;
    }
    else
    {
        int nCount = 1;
        PSListNode pNode = pMeetNode;
        while (pMeetNode != pNode->pNextNode)
        {
            pNode = pNode->pNextNode;
            nCount++;
        }
        return nCount;
    }
}
//pMeetNode参数是用HasCycle函数求链表是否有环时pFast指针与pSlow指针的碰撞点
//定律:在链表头,pFast指针与pSlow指针的碰撞点分别设定一个指针,每次各走一步,两个指针必定相遇,则相遇第一点为环入口点
PSListNode FindEnterNode(PSListNode pHead, PSListNode pMeetNode)
{
    PSListNode pNode = pHead;
    if ((NULL == pHead) || (NULL == pMeetNode))
    {
        return NULL;
    }
    while (pNode != pMeetNode)
    {
        pNode = pNode->pNextNode;
        pMeetNode = pMeetNode->pNextNode;
    }
    return pNode;
}
--------------------- 
作者:ljx_csdn 
来源:CSDN 
原文:https://blog.csdn.net/ljx_5489464/article/details/50934793 
版权声明:本文为博主原创文章,转载请附上博文链接!

两个有序的链表合并

PSListNode MergeList(PSListNode pL1, PSListNode pL2)
{
    PSListNode pNewNode = NULL;
    PSListNode pListNode1 = pL1;
    PSListNode pListNode2 = pL2;
    PSListNode pNode = NULL;
    if (NULL == pListNode1)
    {
        return pListNode2;
    }
    else if (NULL == pListNode2)
    {
        return pListNode1;
    }
    else
    {
        //先把新链表的头结点的指针找到,每次取两个链表中保存的数据较小的结点后插到新链表中
        if (pListNode1->data > pListNode2->data)
        {
            pNode = pListNode2;
            pListNode2 = pListNode2->pNextNode;
            pNewNode = pNode;
        }
        else
        {
            pNode = pListNode1;
            pListNode1 = pListNode1->pNextNode;
            pNewNode = pNode;
        }
        while ((NULL != pListNode1) && (NULL != pListNode2))
        {
            if (pListNode1->data > pListNode2->data)
            {
                pNode->pNextNode = pListNode2;
                pListNode2 = pListNode2->pNextNode;
                pNode = pNode->pNextNode;
            }
            else
            {
                pNode->pNextNode = pListNode1;
                pListNode1 = pListNode1->pNextNode;
                pNode = pNode->pNextNode;
            }
        }
        if (NULL == pListNode1)
        {
            pNode->pNextNode = pListNode2;
            return pNewNode;
        }
        else
        {
            pNode->pNextNode = pListNode1;
            return pNewNode;
        }
    }
}
--------------------- 
作者:ljx_csdn 
来源:CSDN 
原文:https://blog.csdn.net/ljx_5489464/article/details/50934793 
版权声明:本文为博主原创文章,转载请附上博文链接!

删除链表倒数第n个结点

PSListNode FindLastKNode(PSListNode pHead, int K)
{
    if ((NULL == pHead) || (K <= 0))
    {
        return NULL;
    }
    else
    {
        PSListNode pFast = pHead;
        PSListNode pSlow = pHead;
        //利用快慢指针,让快指针先走K-1步,然后两指针同时走,直到快指针指向的下一个结点为空为止
        while (--K)
        {
            pFast = pFast->pNextNode;
            if (NULL == pFast)
            {
                return NULL;
            }
        }
        while (NULL != pFast->pNextNode)
        {
            pFast = pFast->pNextNode;
            pSlow = pSlow->pNextNode;
        }
        return pSlow;
    }
}
--------------------- 
作者:ljx_csdn 
来源:CSDN 
原文:https://blog.csdn.net/ljx_5489464/article/details/50934793 
版权声明:本文为博主原创文章,转载请附上博文链接!

求链表的中间结点

PSListNode FindMidNode(PSListNode pHead)
{
    if (NULL == pHead)
    {
        return NULL;
    }
    else
    {
        //快慢指针
        PSListNode pSlow = pHead;
        PSListNode pFast = pHead;
        //注意结束条件得加上NULL != pFast->pNextNode,否则当NULL == pFast->pNextNode时,进入循环,
        //执行pFast = pFast->pNextNode->pNextNode时会崩溃
        while ((NULL != pFast) && (NULL != pFast->pNextNode))
        {
            pSlow = pSlow->pNextNode;
            pFast = pFast->pNextNode->pNextNode;
        }
        return pSlow;
    }
}
--------------------- 
作者:ljx_csdn 
来源:CSDN 
原文:https://blog.csdn.net/ljx_5489464/article/details/50934793 
版权声明:本文为博主原创文章,转载请附上博文链接!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值