算法考核题解

1.两数相加(力扣2题)

1.1题目

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

1.2思路

  • 先统计两个链表的长度 len1len2,判断谁更长。
  • 按长度分三种情况处理
    • 长度相等:直接在 l1 上做加法,每一位相加,有进位就直接给下一位 val+1
    • l1 更长:前半部分和 l2 相加,剩余部分只处理 l1 的进位。
    • l2 更长:前半部分和 l1 相加,剩余部分只处理 l2 的进位。
  • 进位处理方式 :每一位相加 ≥10 时,当前位取 %10,下一位直接 +1;如果是最后一位,就新建一个值为 1 的节点。

1.3完整代码

cpp 复制代码
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        //先遍历一遍判断个数
        //从个数不同的地方开始相加
        //从长的一个链表的个数不同位置开始替换数字
        int len1=0,len2=0;
        auto cur1=l1;
        auto cur2=l2;
        while(cur1){
            len1++;
            cur1=cur1->next;
        }
        while(cur2){
            len2++;
            cur2=cur2->next;
        }
        if(len1==len2){
            cur1=l1;
            cur2=l2;
            while(cur1){
                int num=cur1->val+cur2->val;
                if(num>=10){
                    int newNum=num%10;
                    cur1->val=newNum;
                    if(cur1->next!=nullptr){
                        (cur1->next->val)++;
                    }
                    else if(cur1->next==nullptr){
                        auto newNode =new ListNode(1);
                        cur1->next=newNode;
                        return l1;
                    }
                }
                else{
                    cur1->val=num;
                }
                cur1=cur1->next;
                cur2=cur2->next;
            }
            return l1;
        }
        else if(len1>len2){
            // int dif=len1-len2;
            cur1=l1;
            cur2=l2;
            // while(dif--){
            //     cur1=cur1->next;
            // }
            while(cur1&&cur2){
                int num=cur1->val+cur2->val;
                if(num>=10){
                    int newNum=num%10;
                    cur1->val=newNum;
                    if(cur1->next!=nullptr){
                        (cur1->next->val)++;
                    }
                    else if(cur1->next==nullptr){
                        auto newNode =new ListNode(1);
                        cur1->next=newNode;
                        return l1;
                    }
                }
                else{
                    cur1->val=num;
                }
                cur1=cur1->next;
                cur2=cur2->next;
            }
             while(cur1){
                if(cur1->val>=10){
                    int newNum=(cur1->val)%10;
                    cur1->val=newNum;
                    if(cur1->next!=nullptr){
                        (cur1->next->val)++;
                    }
                    else if(cur1->next==nullptr){
                        auto newNode =new ListNode(1);
                        cur1->next=newNode;
                        return l1;
                    }
                }
                cur1=cur1->next;
            }
            return l1;
        }
        else if(len2>len1){
            // int dif=len2-len1;
            cur1=l1;
            cur2=l2;
            while(cur2&&cur1){
                int num=cur1->val+cur2->val;
                if(num>=10){
                    int newNum=num%10;
                    cur2->val=newNum;
                    if(cur2->next!=nullptr){
                        (cur2->next->val)++;
                    }
                    else if(cur2->next==nullptr){
                        auto newNode =new ListNode(1);
                        cur2->next=newNode;
                        return l2;
                    }
                }
                else{
                    cur2->val=num;
                }
                cur1=cur1->next;
                cur2=cur2->next;
            }
            while(cur2){
                if(cur2->val>=10){
                    int newNum=(cur2->val)%10;
                    cur2->val=newNum;
                    if(cur2->next!=nullptr){
                        (cur2->next->val)++;
                    }
                    else if(cur2->next==nullptr){
                        auto newNode =new ListNode(1);
                        cur2->next=newNode;
                        return l2;
                    }
                }
                cur2=cur2->next;
            }
            return l2;
        }
        return NULL;
    }
};

2. 买卖股票的最佳时机(121 题)

2.1 题目

给定一个数组 prices,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例:输入 [7,1,5,3,6,4],输出 5(第 2 天买入,第 5 天卖出,利润 6-1=5)。

2.2 思路

从后往前遍历数组,用 ** 大顶堆(优先队列)** 保存 "未来的卖出价格",动态计算当前天买入的最大利润:

  1. 从数组末尾向前遍历,遇到的价格都加入大顶堆,堆顶永远是 "未来能卖出的最高价"。
  2. 每次遍历到第 i 天,用堆顶价格减去 prices[i],得到 "第 i 天买入的最大利润",更新全局最大利润 res
  3. 遍历结束后返回 res(初始为 0,无正利润时自动返回 0)。

2.3 完整代码

cpp 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        priority_queue<int> que;
        int res=0;
        vector<int> profits(prices.size(),0);
        for(int i=prices.size()-1;i>=0;i--){
            if(!que.empty()){
                profits[i]=que.top()-prices[i];
                res=max(profits[i],res);
            }
            que.push(prices[i]);
        }
        return res;
    }
};

3. 删除链表的倒数第 N 个结点(19 题)

3.1 题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例:输入 head = [1,2,3,4,5], n = 2,输出 [1,2,3,5](删除倒数第 2 个节点4)。

3.2 思路

虚拟头节点 + 快慢指针解决,避免处理 "删除头节点" 的特殊情况:

  1. 创建虚拟头节点 dummyHead 指向原链表头,让 fastslow 都指向 dummyHead
  2. 先让 fast 先走 n 步,使 fastslow 之间相差 n 个节点。
  3. fastslow 同步向前走,直到 fast 走到链表末尾(fast->next == nullptr),此时 slow->next 就是要删除的倒数第 n 个节点。
  4. 直接修改 slow->next = slow->next->next,跳过待删除节点,返回 dummyHead->next

3.3 完整代码

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        auto dummyHead=new ListNode(0);
        dummyHead->next=head;
        auto fast=dummyHead;
        auto slow=dummyHead;
        while(n--){
            fast=fast->next;
        }
        while(fast->next){
            fast=fast->next;
            slow=slow->next;
        }
        slow->next=slow->next->next;
        return dummyHead->next;
    }
};

4. 最长递增子序列(300 题)

4.1 题目

给你一个整数数组 nums,找到其中最长严格递增子序列的长度。子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。

示例:输入 nums = [10,9,2,5,3,7,101,18],输出 4(最长递增子序列为 [2,3,7,101])。

4.2 思路

动态规划解法,核心是定义 dp 数组记录状态:

  1. 定义 dp[i]:以 nums[i] 结尾的最长严格递增子序列的长度。
  2. 初始化 dp 数组,每个元素设为 1(每个元素本身就是长度为 1 的子序列)。
  3. 双重循环遍历:外层 i 遍历每个元素,内层 j 遍历 0~i-1 的所有元素。
  4. nums[j] < nums[i],说明 nums[i] 可以接在 nums[j] 后形成更长的子序列,更新 dp[i] = max(dp[i], dp[j]+1)
  5. 遍历结束后,dp 数组的最大值即为答案。

4.3 完整代码

cpp 复制代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(),1);
        for(int i=0;i<nums.size();i++){
            for(int j=i;j>=0;j--){
                if(nums[j]<nums[i]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
        }
        int res=0;
        for(int i=0;i<dp.size();i++){
            res=max(dp[i],res);
        }
        return res;
    }
};

5. 最大子数组和(53 题)

5.1 题目

给你一个整数数组 nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:输入 nums = [-2,1,-3,4,-1,2,1,-5,4],输出 6(连续子数组 [4,-1,2,1] 的和最大)。

5.2 思路

贪心算法(Kadane 算法),用两个变量动态维护状态:

  1. sum:记录当前连续子数组的和;res:记录全局最大子数组和(初始为 INT_MIN,避免全负数数组出错)。
  2. 遍历数组,每次将当前元素加到 sum 中,更新 ressum 和当前 res 的较大值。
  3. sum < 0,说明当前子数组和为负,继续累加只会拉低后续和,因此重置 sum = 0,重新开始计算新的子数组。
  4. 遍历结束后返回 res

5.3 完整代码

cpp 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sum=0;
        int res=INT_MIN;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
            res=max(sum,res);
            if(sum<0){
                sum=0;
                continue;
            }
        }
        return res;
    }
};

6. 搜索旋转排序数组(33 题)

6.1 题目

整数数组 nums 按升序排列,数组中的值互不相同。在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了向左旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]

给你旋转后的数组 nums 和一个整数 target,如果 nums 中存在 target,则返回它的下标,否则返回 -1。题目要求设计时间复杂度为 O(log n) 的算法(注:你的代码为 O(n) 解法,以下按你的思路整理)。

示例:输入 nums = [4,5,6,7,0,1,2], target = 0,输出 4

6.2 思路

先找到旋转点,再将数组还原为有序数组后遍历查找:

  1. 遍历数组找到旋转点 index :即第一个满足 nums[i] < nums[i-1] 的位置(无旋转时 index=0)。
  2. 将数组拆分为两部分:前半部分 nums[0..index-1] 和后半部分 nums[index..end],再将后半部分拼接到前半部分后,还原为升序数组。
  3. 遍历还原后的数组,找到 target 则返回下标,未找到则返回 -1

6.3 完整代码

cpp 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int index=0;
        for(int i=1;i<nums.size();i++){
            if(nums[i]<nums[i-1]){
                index=i;
                break;
            }
        }
        vector<int> nums1(nums.begin(),nums.begin()+index);
        vector<int> nums2(nums.begin()+index,nums.end());
        for(int i=0;i<nums2.size();i++){
            nums1.push_back(nums2[i]);
        }
        for(int i=0;i<nums1.size();i++){
            if(target==nums1[i]){
                return i;
            }
        }
        return -1;
    }
};

7. 环形链表 II(142 题)

7.1 题目

给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null。不允许修改链表结构。

示例:输入 head = [3,2,0,-4], pos = 1(链表尾部连接到索引 1 的节点),输出索引为 1 的节点。

7.2 思路

Floyd 判圈算法(快慢指针法),分两步解决:

  1. 判断是否有环fast 指针每次走 2 步,slow 指针每次走 1 步,若两者相遇则说明链表有环;若 fast 走到链表末尾则无环,返回 null
  2. 找到环的入口:相遇后,让其中一个指针回到链表头,两个指针每次同步走 1 步,再次相遇的节点即为环的入口节点。

7.3 完整代码

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head||!head->next){
            return NULL;
        }
        auto left=head;
        auto right=head;
        while(left&&right&&right->next){
            right=right->next->next;
            left=left->next;
            if(right==left){
                auto cur=head;
                while(left!=cur){
                    left=left->next;
                    cur=cur->next;
                }
                return left;
            }
        }
        return NULL;
    }
};
相关推荐
MediaTea1 小时前
AI 术语通俗词典:ID3 算法
人工智能·算法
Morwit1 小时前
【力扣hot100】 221. 最大正方形
前端·算法·leetcode
呃呃本2 小时前
算法题(矩阵)
线性代数·算法·矩阵
呃呃本2 小时前
算法题(普通数组、矩阵)
线性代数·算法·矩阵
计算机安禾2 小时前
【计算机网络】第11篇:链路状态路由协议——Dijkstra算法与OSPF的分区架构
计算机网络·算法·架构
珂朵莉MM2 小时前
第七届全球校园人工智能算法精英大赛-算法巅峰赛产业命题赛第二赛季优化题--遗传算法
人工智能·算法
gihigo19982 小时前
严格耦合波分析计算光栅衍射效率算法
算法
可编程芯片开发2 小时前
基于双Qlearning强化学习的温差发电系统电压动态补偿算法matlab仿真
算法·matlab·双qlearning强化学习·电压动态补偿·温差发电系统
Java成神之路-2 小时前
【LeetCode 刷题笔记】69.x 的平方根 | 二分查找经典刷题题解
算法·leetcode