数据结构面试题之一----链表



  1. 从尾到头输出链表
    Question:输入一个链表的头结点,从尾到头反过来打印出每个节点的值。
    典型的后进先出,可以使用栈实现。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    void Revers_Pring(ListNode* pHead)
    {
          std::stack<ListNode*> nodes;
          ListNode* pNode = pHead;
          while (pNode != NULL)
          {
              modes.push(pNode);
              pNode = pNode->pNext;
          }
          while (!nodes.empty())
          {
              pNode = nodes.top();
              printf ( "%d\t" ,pNode->data);
              nodes.pop();
          }
    }

    递归本质上就是栈,所以也可以用递归实现

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void ReversPrint(ListNode* pHead)
    {
          if (pHead!=NULL)
          {
               if (pHead->pNext != NULL)
               {
                    ReversPrint(pHead->pNext);
               }
               printf ( "%d\t" ,pHead->data);
          }    
    }
  2. 在O(1)时间删除链表结点
    题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。
    分析:O(1)时间内把下一个节点的内存复制覆盖掉要删除的节点,并删除下一个节点。对于尾结点,仍需要顺序查找,复杂度O(n)。
    以下代码前提假设为要删除的结点确实在链表中。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    void DeleteNode(ListNode** pHead,ListNode* pDelete)
    {
         if (!pHead || !pDelete)
             return ;
         //要删除的节点不是尾节点
         if (pDelete->pNext!=NULL)
         {
             ListNode* Next = pDelete->pNext;
             pDelete->data = Next->data;
             pDelete->pNext = Next->pNext;
             delete Next;
             Next = NULL;
         }
         //链表只有一个节点
         else if (*pHead == pDelete)
         {
             delete pDelete;
             pDelete = NULL;
             *pHead = NULL;
         }
         //链表中有多个节点,删除尾节点,需要顺序查找
         else
         {
             ListNode *Node = *pHead;
             while (Node->pNext!=pDelete)
                 Node = Node->pNext;
     
             Node->pNext = NULL;
             delete pDelete;
             pDelete = NULL;
         }
    }
  3. 链表中的倒数第k个结点
    题目:输入一个链表,输出该链表中倒数第k个结点。假设链表的尾节点为倒数第一个结点。
    分析:两个指针差k-1步,但需要注意特殊情况:Head为空指针,结点数少于k个,参数k=0情况。
    延伸:求链表中间结点。定义两个指针,一个指针一次一步,一个指针一次两步,走得快的到尾部时,走的慢的正好在中间。同样,判断单向链表是否形成了环形,也是这样,最后如果走得快的指针追上了走得慢的指针,那么链表就是环形链表,如果走得快的走到了链表尾部都没有追上慢的,那么就不是环形链表。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ListNode* FindKthToTail(ListNode* pHead, int k)
    {
         if (pHead == NULL || k < 0)
             return NULL;
         ListNode* pA = pHead; //先移动k-1步
         ListNode* pB = pHead;
         for ( int i = 0;i < k-1;++i)
         {
             if (pA == NULL)
                 return NULL;
             else
                 pA = pA->pNext;
         }
     
         while (pA->pNext!=NULL)
         {
             pA = pA->pNext;
             pB = pB->pNext;
         }
         return pB;
    }
  4. 反转链表
    题目:定义一个函数,输入一个链表的头指针,反转该链表并输出反转后链表的头结点。
    分析:为防止链表断开,需定义三个指针,分别指向当前遍历到的结点、它的前一个结点及后一个结点。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ListNode* ReverseList(ListNode* pHead)
    {
         ListNode* ReversedHead = NULL;
         ListNode* Node = pHead;
         ListNode* Prev = NULL; //头结点反转后指向NULL
         while (Node != NULL)
         {
             ListNode* Next = Node->pNext; //指向后一个结点
             if (Next == NULL)
                 ReversedHead = Node;
             Node->pNext = Prev;
             Prev = Node;
             Node = Next;
         }
         return ReversedHead;
    }
  5. 合并两个排序的链表
    题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
    {
         if (pHead1 == NULL) return pHead2;
         if (pHead2 == NULL) return pHead1;
     
         ListNode* pMerge = NULL;
         if (pHead1->data < pHead2->data)
         {
             pMerge = pHead1;
             pMerge->pNext=Merge(pHead1->pNext,pHead2);
         }
         else
         {
             pMerge = pHead2;
             pMerge->pNext=Merge(pHead1,pHead2->pNext);
         }
         return pMerge;
    }
  6. 两个链表的第一个公共结点(还有树的公共结点问题)
    题目:输入两个链表,找出它们的第一个公共结点。
    分析:如果两个链表有公共结点,那么公共结点出现在两个链表的尾部。从两个尾部开始向前比较,最后一个相同的借点就是我们要找的结点。分别将两个链表的结点放入两个栈,每次比较栈顶结点,相同就弹出比较下一个。(需要复制空间)
    或者
    我们先遍历一遍得到两个链表的长度,然后让长的链表先向前走几步,之后再同时遍历。
  7. 环形链表-圆圈中最后剩下的数字
    题目:0,1,2,…n-1这n个数排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字,求出这个圆圈中最后剩下的那个数字(约瑟夫问题)。例如,{0,1,2,3,4}从0开始删除第3个数字,那么删除的前四个数依次是2,0,4,1,最后剩下3.
    解法一:环形链表模拟
    用std::list模仿环形链表,扫描到链表尾部时记得吧迭代器移到头部。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    int LastRemain(unsigned int n,unsigned int m)
    {
         if (n<1||m<1)
             return -1;
         list< int > numbers;
         for ( int i=0;i<n;++i)
             numbers.push_back(i);
     
         list< int >::iterator cur = numbers.begin();
         while (numbers.size()>1)
         {
             for ( int i=1;i<m;++i)
             {
                 cur++;
                 if (cur == numbers.end())
                     cur = numbers.begin();
             }
             list< int >::iterator next = ++cur;
             if (next == numbers.end())
                 next = numbers.begin();
             --cur;
             numbers.erase(cur);
             cur=next;
         }
         return *(cur);
    }

    解法二:分析每次被删除的数字的规律直接计算出最后剩下的数字

    1) if we shift the ids by k, namely, start from k instead of 0, we should add the result by k%n
    2) after the first round, we start from k+1 ( possibly % n) with n-1 elements, that is equal to an (n-1) problem
    while start from (k+1)th element instead of 0, so the answer is (f(n-1, m)+k+1)%n
    3) k = m-1, so f(n,m)=(f(n-1,m)+m)%n.
    finally, f(1, m) = 0;Now this is a O(n) solution.

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int LastRemain(unsigned int n,unsigned int m)
    {
         if (n<1||m<1)
             return -1;
         int last=0;
         for ( int i=2;i<=n;++i)
             last = (last+m)%i;
         return last;
    }
  8. 二叉搜索树与双向链表
    题目:输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建新的结点,只能调整树中节点指针的指向。

    分析:中序遍历,当遍历到根节点时,连接左子链表的最右结点和右子链表的最左结点。再递归调整左右子链表。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    struct BinaryTreeNode
    {
         int nValue;
         BinaryTreeNode* pLeft;
         BinaryTreeNode* pRight;
    };
     
    void helper(BinaryTreeNode *&head,BinaryTreeNode *&tail,BinaryTreeNode *root)
    {
         BinaryTreeNode *lt,*rh; //left tail,right head
         if (root == NULL)
         {
             head = NULL;
             tail = NULL;
             return ;
         }
         helper(head,lt,root->pLeft);
         helper(rh,tail,root->pRight);
         if (lt != NULL)
         {
             lt->pRight = root;
             root->pLeft = lt;
         }
         else
             head = root;
         if (rh != NULL)
         {
             root->pRight = rh;
             rh->pLeft = root;
         }
         else
             tail = root;
    }
     
    BinaryTreeNode* treeToLinkedList(BinaryTreeNode *root)
    {
         BinaryTreeNode *head,*tail; //生成双向链表的头指针和尾指针
         helper(head,tail,root);
         return head;
    }
  9. 复杂链表的复制
    题目:实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。复杂链表中,每个结点除了有一个指针指向下一个结点,还有一个指针指向链表中任意结点或者NULL。
    分析:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    struct ComplexNode
    {
         int m_nValue;
         ComplexNode* m_pNext;
         ComplexNode* m_pSibling;
    };
    ComplexNode* Clone(ComplexNode* pHead)
    {
         if (pHead == NULL)
             return NULL;
         preClone(pHead);
         inClone(pHead);
         return postClone(pHead);
    }
    void preClone(ComplexNode* pHead)
    {
         //复制原始链表的任意结点N并创建新结点N’,链接到N的后面
         ComplexNode* pNode = pHead;
         while (pNode != NULL)
         {
             ComplexNode* pClone = new ComplexNode();
             pClone->m_nValue = pNode->m_nValue;
             pClone->m_pNext = pNode->m_pNext;
             pClone->m_pSibling = NULL;
     
             pNode->m_pNext = pClone;
             pNode = pClone->m_pNext;
         }
    }
     
    void inClone(ComplexNode* pHead)
    {
         //原始链表上的节点N的m_pSibling指向S,则N'指向S的下一个节点S'
         ComplexNode * pNode = pHead;
         while (pNode != NULL)
         {
             ComplexNode* pClone = pNode->m_pNext;
             if (pNode->m_pSibling != NULL)
                 pClone->m_pSibling = pNode->m_pSibling->m_pNext;
             pNode = pClone->m_pNext;
         }
    }
     
    ComplexNode * postClone(ComplexNode* pHead)
    {
         //拆分链表,奇偶
         ComplexNode* pNode = pHead;
         ComplexNode* pCloneHead = NULL;
         ComplexNode* pCloneNode = NULL;
     
         if (pNode != NULL)
         {
             pCloneHead = pCloneNode = pNode->m_pNext;
             pNode->m_pNext = pCloneNode->m_pNext;
             pNode = pNode->m_pNext;
         }
         while (pNode != NULL)
         {
             pCloneNode->m_pNext = pNode->m_pNext;
             pCloneNode = pCloneNode->m_pNext;
             pNode->m_pNext = pCloneNode->m_pNext;
             pNode = pNode->m_pNext;
         }
         return pCloneHead;
    }
  10. 从一个未排序的单链表中删除重复元素
    题目:Write code to remove duplicates from an unsorted linked list.
    解法一:如果对空间复杂度没有要求,则使用哈希表,如果存在即删除,如果不存在将值写入hashtable。
    解法二:使用两个pointer进行遍历–current进行正常遍历,当runner每次从头开始查找(到current为止)是否有重复元素,如果重复,删除current。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    void deleteDups(ListNode* pHead)
    {
         if (pHead == NULL) return ;
         ListNode* pre = pHead;
         ListNode* current = pre->pNext;
         while (current != NULL)
         {
             ListNode* runner = pHead;
             while (runner != current)
             {
                 if (runner->data == current->data)
                 {
                     //remove current
                     ListNode* tmp = current->pNext;
                     pre->pNext = tmp;
                     current = tmp;
                     break ; //跳出的while循环
                 }
                 runner = runner->pNext;
             }
             if (runner == current)
             {
                 //runner在0-current间遍历
                 //无重复
                 pre = current;
                 current = current->pNext;
             }
         }
    }
  11. You have two numbers represented by a linked list,where each node contains a single digit.The digits are stored in reverse order,such that the 1′s digit is at the head of the list.Write a function that adds the two numbers and returns the sum as a linked list.e.g. Input:(3->1->5),(5->9->2),Output:8->0->8
    分析:每一位的值相当于(链表1的值+链表2的值+前一位的进位)%10,如果该位所得的值超过10,带进位。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ListNode* addLists(ListNode* p1,ListNode* p2, int flag)
    {
         if (p1==NULL && p2==NULL) return NULL;
         ListNode* result;
         int value = flag;
         if (p1!=NULL)
             value += p1->data;
         if (p2!=NULL)
             value += p2->data;
         result->data = value%10;
         ListNode* more = addLists(p1 == NULL?NULL:p1->pNext,
                 p2==NULL?NULL:p2->pNext,
                 value>10?1:0);
         result->pNext = more;
         return result;
    }
  12. Given a circular linked list,implement an algorithm which returns node at the beginning of the loop.e.g. Input:A->B->C->D->E->C,output:C.
    分析:两个指针,一个每次移动1步,一个每次移动2步,如果最后能相遇,则表明链表有环。
    1)表头距离环开始处k个结点;2)p1p2相遇处距离环开始处也为k个结点;3)寻找两个点的中点即可。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ListNode* FindBeginning(ListNode* pHead)
    {
         ListNode* p1 = pHead;
         ListNode* p2 = pHead;
     
         while (p2->pNext!=NULL)
         {
             p1=p1->pNext;
             p2=p2->pNext->pNext;
             if (p1==p2)
                 break ;
         }
         if (p2->pNext==NULL)
             return NULL; //no loop
         p1 = pHead;
         while (p1!=p2)
         {
             p1=p1->pNext;
             p2=p2->pNext;
         }
         return p2;
    }


标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值