链表

链表

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

链表的插入删除操作时间复杂度是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.youkuaiyun.com/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 
来源:优快云 
原文: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 
来源:优快云 
原文: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 
来源:优快云 
原文: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 
来源:优快云 
原文:https://blog.csdn.net/ljx_5489464/article/details/50934793 
版权声明:本文为博主原创文章,转载请附上博文链接!
本项目构建于RASA开源架构之上,旨在实现一个具备多模态交互能力的智能对话系统。该系统的核心模块涵盖自然语言理解、语音转文本处理以及动态对话流程控制三个主要方面。 在自然语言理解层面,研究重点集中于增强连续对话中的用户目标判定效能,并运用深度神经网络技术提升关键信息提取的精确度。目标判定旨在解析用户话语背后的真实需求,从而生成恰当的反馈;信息提取则专注于从语音输入中析出具有特定意义的要素,例如个体名称、空间位置或时间节点等具体参数。深度神经网络的应用显著优化了这些功能的实现效果,相比经典算法,其能够解析更为复杂的语言结构,展现出更优的识别精度与更强的适应性。通过分层特征学习机制,这类模型可深入捕捉语言数据中隐含的语义关联。 语音转文本处理模块承担将音频信号转化为结构化文本的关键任务。该技术的持续演进大幅提高了人机语音交互的自然度与流畅性,使语音界面日益成为高效便捷的沟通渠道。 动态对话流程控制系统负责维持交互过程的连贯性与逻辑性,包括话轮转换、上下文关联维护以及基于情境的决策生成。该系统需具备处理各类非常规输入的能力,例如用户使用非规范表达或对系统指引产生歧义的情况。 本系统适用于多种实际应用场景,如客户服务支持、个性化事务协助及智能教学辅导等。通过准确识别用户需求并提供对应信息或操作响应,系统能够创造连贯顺畅的交互体验。借助深度学习的自适应特性,系统还可持续优化语言模式理解能力,逐步完善对新兴表达方式与用户偏好的适应机制。 在技术实施方面,RASA框架为系统开发提供了基础支撑。该框架专为构建对话式人工智能应用而设计,支持多语言环境并拥有活跃的技术社区。利用其内置工具集,开发者可高效实现复杂的对话逻辑设计与部署流程。 配套资料可能包含补充学习文档、实例分析报告或实践指导手册,有助于使用者深入掌握系统原理与应用方法。技术文档则详细说明了系统的安装步骤、参数配置及操作流程,确保用户能够顺利完成系统集成工作。项目主体代码及说明文件均存放于指定目录中,构成完整的解决方案体系。 总体而言,本项目整合了自然语言理解、语音信号处理与深度学习技术,致力于打造能够进行复杂对话管理、精准需求解析与高效信息提取的智能语音交互平台。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值