力扣hot100--链表

目录

链表

1. 2. 两数相加

2. 19. 删除链表的倒数第 N 个结点

3. 21. 合并两个有序链表

4. 23. 合并 K 个升序链表

5. 141. 环形链表

6. 142. 环形链表 II

7.148. 排序链表

8. 160. 相交链表

9. 206. 反转链表

10. 234. 回文链表


链表

1. 2. 两数相加

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

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

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

示例 1:

0e50c1953088b9d413e8e8950482243c.jpeg

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int t = 0; // 初始化进位为0
        ListNode *dummy = new ListNode(0); // 创建一个哑节点,初始值为0
        ListNode* cur = dummy; // 使用指针 cur 指向 dummy
        
        // 当 l1 或 l2 不为空时循环
        while (l1 || l2) {
            if (l1) {
                t += l1->val; // 将 l1 的值加到 t 上
                l1 = l1->next; // 移动到 l1 的下一个节点
            }
            if (l2) {
                t += l2->val; // 将 l2 的值加到 t 上
                l2 = l2->next; // 移动到 l2 的下一个节点
            }
            cur->next = new ListNode(t % 10); // 创建一个新节点,值为 t 的个位数
            cur = cur->next; // 移动到新创建的节点
            t = t / 10; // 更新 t 为进位数
        }
        
        // 如果还有进位,创建一个新节点存储进位数
        if (t) {
            cur->next = new ListNode(t);
        }
        
        return dummy->next; // 返回结果链表的头节点(跳过哑节点)
    }
};

2. 19. 删除链表的倒数第 N 个结点

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

示例 1:

f689a808ea63ee4db0af6482dc2b00b6.jpeg

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

// 链表+数学计算
/**
 * 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) {
        int count{};

        // 删除倒数第n个,即删除第count-n+1

        ListNode* cNode = head;

        while(cNode != nullptr){
            cNode = cNode->next;
            count++;
        }

        ListNode *cNodeX = new ListNode(0);
        cNodeX->next = head;
        count = count - n +1;

        ListNode *node = cNodeX;
        while(count--){
            if(0 == count){
                node->next = node->next->next;
                break;
            }
            node = node->next;
        }

        return cNodeX->next;
    }
};
 

3. 21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例 1:844e015a147b4378b58fd33791b2236a.jpeg

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

// 链表 + 递归

/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        // 如果 list1 为空,则直接返回 list2,因为没有节点可合并
        if (!list1) return list2;

        // 如果 list2 为空,则直接返回 list1,因为没有节点可合并
        if (!list2) return list1;

        // 定义一个指针 node,作为合并后的链表头节点
        ListNode* node;

        // 比较 list1 和 list2 的当前节点的值,将较小的值加入到合并后的链表中
        if (list1->val < list2->val) {
            // 如果 list1 的当前值小于 list2 的当前值,则选择 list1 的节点
            node = list1;

            // 继续递归合并 list1 的下一个节点和 list2,设置为 node 的下一个节点
            list1 = list1->next;
        } else {
            // 如果 list2 的当前值小于或等于 list1 的当前值,则选择 list2 的节点
            node = list2;

            // 继续递归合并 list2 的下一个节点和 list1,设置为 node 的下一个节点
            list2 = list2->next;
        }

        // 将选中的节点的 next 指针设置为剩余节点递归合并的结果
        node->next = mergeTwoLists(list1, list2);

        // 返回合并后的链表头节点
        return node;
    }
};
 

4. 23. 合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

// 链表

/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if (!list1) return list2;  // 如果 list1 为空,返回 list2
        if (!list2) return list1;  // 如果 list2 为空,返回 list1

        // 创建一个虚拟头节点,便于操作
        ListNode* node = new ListNode(0);
        ListNode* tail = node;

        // 遍历 list1 和 list2,合并两个链表
        while (list1 && list2) {
            if (list1->val < list2->val) {
                tail->next = list1; // 将 list1 的当前节点接入合并链表
                list1 = list1->next; // 移动 list1 指针到下一个节点
            } else {
                tail->next = list2; // 将 list2 的当前节点接入合并链表
                list2 = list2->next; // 移动 list2 指针到下一个节点
            }
            tail = tail->next; // 移动合并链表的尾节点
        }

        // 将剩余的链表部分直接连接到合并链表的尾部
        tail->next = list1 ? list1 : list2;

        // 返回合并后的链表头节点(去除虚拟头节点)
        return node->next;
    }

    // 合并多个链表
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int k = lists.size();

        ListNode* node{}; // 初始合并链表为空

        // 逐个合并链表
        for (int i = 0; i < k; ++i) {
            node = mergeTwoLists(node, lists[i]);
        }

        return node; // 返回最终合并后的链表头节点
    }
};
 

5. 141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

99c27589f5043ca3159edcac7524da2b.png

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

cd96124d43d4fe03cf78a0663781d642.png

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

964562254b22ae18a3c5d6dbc0ab30e1.png

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

// 双指针方法
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;                // 节点的值
 *     ListNode *next;        // 指向下一个节点的指针
 *     ListNode(int x) : val(x), next(NULL) {} // 构造函数
 * };
 */
class Solution {
public:
    // 方法:检查链表是否有环
    bool hasCycle(ListNode *head) {
        ListNode *slow = head;  // 慢指针,初始指向链表头
        ListNode *fast = head;  // 快指针,初始指向链表头

        // 当快指针和快指针的下一个节点存在时,继续循环
        while(fast && fast->next){
            slow = slow->next;           // 慢指针每次移动一步
            fast = fast->next->next;     // 快指针每次移动两步

            // 如果慢指针和快指针相遇,说明链表中存在环
            if(slow == fast){
                return true;  // 有环,返回true
            }
        }

        return false; // 无环,返回false
    }
};
 

解释: 

  • 慢指针和快指针:慢指针每次移动一步,而快指针每次移动两步。如果链表中有环,快指针最终会追上慢指针。
  • 循环条件:在 while 循环中,我们确保快指针和快指针的下一个节点都不为空,以避免访问空指针。
  • 相遇检测:如果在某个时刻,慢指针和快指针相遇,则说明链表中存在环。返回 true
  • 无环返回:如果快指针到达链表的末尾,说明链表没有环,返回 false

6. 142. 环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

4a4b2557741b1347b6330321694174a1.png

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

523730a547a3f4190002ce2a89a34475.png

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

14e7d65568dd5e08e3de55db2d8d8247.png

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

// 双指针 + 数学方法
// 核心:a = c + (n−1)(b + c) 的等量关系
// 我们会发现:从相遇点到入环点的距离加上 (n−1) 圈的环长,恰好等于从链表头部到入环点的距离
/**
 * 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 {};

        ListNode* slow = head;  // 慢指针,初始指向链表头
        ListNode* fast = head;  // 快指针,初始指向链表头

        // 使用快慢指针检测是否存在环
        while(fast && fast->next) {
            slow = slow->next;           // 慢指针每次移动一步
            fast = fast->next->next;     // 快指针每次移动两步

            // 如果慢指针和快指针相遇,说明存在环
            if(slow == fast) {
                break;  // 跳出循环
            }
        }

        // 如果慢指针没有与快指针相遇,则说明没有环
        if(slow != fast) return {};  // 返回空指针

        // 从相遇点重新初始化慢指针到链表头部
        slow = head;

        // 同时移动慢指针和快指针,直到相遇
        while(slow != fast) {
            slow = slow->next;  // 慢指针每次移动一步
            fast = fast->next;  // 快指针每次移动一步
        }

        // 返回相遇点,即环的起始节点
        return slow;
    }
};
 

解释: 

  • 检测环的存在:首先使用快慢指针方法检测链表中是否存在环。如果慢指针和快指针相遇,说明链表中有环。
  • 重新定位慢指针:当检测到环时,将慢指针重新指向链表头部。
  • 寻找环的起始节点:然后,慢指针和快指针同时向前移动,每次移动一步。它们会在环的起始节点相遇,因为从链表头到环的起始节点的距离与从相遇点到环的起始节点的距离是相等的。
  • 返回环的起始节点:当两指针再次相遇时,返回这个节点,即为环的起始节点。

7.148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:

78c5e0cfa9854bddadde9aa3c36f6ccd.jpeg

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

59b4f2664688aed1f98824925e32cfce.jpeg

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105

// 链表节点的定义
/**
 * 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* sortList(ListNode* head) {
        vector<int> v; // 创建一个动态数组,用于存储链表节点的值

        ListNode* cur = head; // 初始化当前节点为链表的头节点

        // 遍历链表,将每个节点的值存入动态数组
        while(cur) {
            v.push_back(cur->val); // 将当前节点的值加入数组
            cur = cur->next; // 移动到下一个节点
        }

        // 对动态数组中的值进行排序
        sort(v.begin(), v.end());

        cur = head; // 重新初始化当前节点为链表的头节点
        // 将排序后的值重新赋值给链表的节点
        for(int i{}; i < v.size(); ++i) {
            cur->val = v[i]; // 将排序后的值赋给当前节点
            cur = cur->next; // 移动到下一个节点
        }

        return head; // 返回排序后的链表头节点
    }
};
 

 该实现方法简单直观,但其时间复杂度为 O(nlog⁡n)(排序部分)加上 O(n)O(n)O(n)(两次遍历链表),总的时间复杂度为 O(nlog⁡n),空间复杂度为 O(n)(用于存储值的数组)。如果需要提高效率,可以考虑使用归并排序等其他算法。

归并排序如下:

/**
 * 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* sortList(ListNode* head) {
        // 递归终止条件:链表为空或只有一个节点
        if (!head || !head->next) {
            return head;
        }

        // 使用快慢指针找到链表的中间节点
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
        }

        // 将链表分成两部分
        ListNode* mid = slow->next;
        slow->next = nullptr;

        // 递归排序左右两部分
        ListNode* left = sortList(head);
        ListNode* right = sortList(mid);

        // 合并两个有序链表
        return merge(left, right);
    }

private:
    // 合并两个有序链表
    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode dummy(0); // 虚拟头节点
        ListNode* tail = &dummy;

        while (l1 && l2) {
            if (l1->val < l2->val) {
                tail->next = l1;
                l1 = l1->next;
            } else {
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next;
        }

        // 将剩余的链表连接到尾部
        if (l1) {
            tail->next = l1;
        }
        if (l2) {
            tail->next = l2;
        }

        return dummy.next;
    }
};

8. 160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交

​编辑

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

class Solution {
public:
    // 函数用于找到两个单链表的交点
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode*> unS; // 创建一个哈希表,用于存储节点的指针

        // 遍历第一个链表,将节点指针存入哈希表
        while(headA){
            unS.insert(headA); // 将当前节点指针加入哈希表
            headA = headA->next; // 移动到下一个节点
        }

        // 遍历第二个链表,检查每个节点是否在哈希表中
        while(headB){
            // 如果当前节点在哈希表中,说明找到了交点,返回这个节点
            if(unS.find(headB)!=unS.end()) return headB;
            headB = headB->next; // 移动到下一个节点
        }

        // 如果没有找到交点,返回空指针
        return {};
    }
};

 解释:if(unS.find(headB)!=unS.end()) 这个条件判断的意思是:“如果 headB 这个节点的指针在哈希表 unS 中找到了对应的元素”,那么条件为真。如果 find 方法找到了 headB 这个指针,它会返回一个指向该元素的迭代器;如果没有找到,它会返回 end() 迭代器。因此,如果 find 的结果不等于 end(),说明 headB 已经在哈希表中了,这通常意味着 headB 是两个链表的交点,因为哈希表 unS 存储了第一个链表的所有节点指针。

9. 206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

68b846eee3ca3420804002941849d4ac.jpeg

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

ab8b1662670d5fd3bf156a1af46e567b.jpeg

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

方法一:

class Solution {
public:
    // 反转单链表的函数
    ListNode* reverseList(ListNode* head) {
        // 如果链表为空,直接返回空指针
        if(!head) return {};

        // 定义两个指针,pre用于指向反转后链表的前一个节点,cur用于遍历链表
        ListNode* pre = nullptr;  // 初始时pre指向空
        ListNode* cur = head;     // cur指向链表的头节点

        // 遍历链表
        while(cur) {
            // 保存当前节点的下一个节点
            ListNode* node = cur->next;  
            // 反转当前节点的指向
            cur->next = pre;  
            // 更新pre和cur指针
            pre = cur;        // pre指向当前节点
            cur = node;      // cur指向下一个节点
        }

        // 返回新的头节点,即反转后的链表头
        return pre;  
    }
};

 解释:建议画图理解。

方法二:


class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head||!head->next){
            return head;
        }
        vector<int> v;
        ListNode* tail=head;
        while(tail){
            v.push_back(tail->val);
            tail=tail->next;
        }
        tail=head;
        for(int i=v.size()-1;i>=0;i--){
            tail->val=v[i];
            tail=tail->next;
        }
        return head;
    }
};

10. 234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:30439e71a31744c9963c5b5b73547550.jpeg

输入:head = [1,2,2,1]
输出:true

示例 2:37bc7b3d33864fad985a38c342457904.jpeg

输入:head = [1,2]
输出:false

提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

// 双指针 + 链表
/**
 * 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:
    // 函数:检查链表是否为回文
    bool isPalindrome(ListNode* head) {
        vector<int> v;  // 使用 vector 来存储链表的节点值

        // 遍历链表,将所有节点的值存入 vector
        while(head){
            v.push_back(head->val);  // 将当前节点的值加入 vector
            head = head->next;  // 移动到下一个节点
        }

        int left{};  // 左指针初始化为 0
        int right = v.size()-1;  // 右指针初始化为 vector 的最后一个索引

        // 使用双指针比较 vector 中的元素
        while(left < right){
            if(v[left] != v[right]){  // 如果左右指针指向的元素不相等
                return false;  // 返回 false,表示链表不是回文
            }
            left++;  // 移动左指针向右
            right--;  // 移动右指针向左
        }

        return true;  // 如果所有比较都通过,返回 true,表示链表是回文
    }
};

### 力扣热门100题列表 力扣LeetCode)上的热门题目通常是指那些被广泛讨论、高频面试或者具有较高难度的题目。这些题目涵盖了数据结构和算法的核心知识点,适合用来提升编程能力和解决实际问题的能力。 以下是基于社区反馈整理的部分 **LeetCode Hot 100 Problems List**: #### 数组与字符串 1. Two Sum (两数之和)[^1] 2. Longest Substring Without Repeating Characters (无重复字符的最长子串)[^2] 3. Median of Two Sorted Arrays (两个有序数组的中位数)[^1] 4. Container With Most Water (盛最多水的容器)[^2] #### 链表 5. Reverse Linked List (反转链表) 6. Merge Two Sorted Lists (合并两个有序链表) 7. Remove Nth Node From End of List (删除倒数第N个节点) 8. Linked List Cycle II (环形链表II) #### 堆栈与队列 9. Valid Parentheses (有效的括号) 10. Min Stack (最小栈) 11. Sliding Window Maximum (滑动窗口最大值)[^2] #### 树与二叉树 12. Binary Tree Inorder Traversal (二叉树的中序遍历) 13. Validate Binary Search Tree (验证二叉搜索树) 14. Same Tree (相同的树) 15. Serialize and Deserialize Binary Tree (序列化与反序列化二叉树) #### 图论 16. Number of Islands (岛屿数量) 17. Course Schedule (课程表) 18. Clone Graph (克隆图) #### 排序与搜索 19. Find First and Last Position of Element in Sorted Array (在排序数组中查找元素的第一个和最后一个位置) 20. Search a 2D Matrix (二维矩阵搜索) 21. K Closest Points to Origin (最接近原点的K个点) #### 动态规划 22. Climbing Stairs (爬楼梯) 23. House Robber (打家劫舍)[^1] 24. Coin Change (零钱兑换) 25. Unique Paths (不同路径) #### 贪心算法 26. Jump Game (跳跃游戏)[^1] 27. Non-overlapping Intervals (无重叠区间) 28. Best Time to Buy and Sell Stock (买卖股票的最佳时机)[^1] #### 字符串匹配与处理 29. Implement strStr() (实现strStr()) 30. Longest Consecutive Sequence (最长连续序列) 31. Group Anagrams (分组异位词) --- ### 示例代码片段 以下是一个关于动态规划的经典例子——`Climbing Stairs` 的 Python 实现: ```python class Solution: def climbStairs(self, n: int) -> int: if n == 1 or n == 2: return n dp = [0] * (n + 1) dp[1], dp[2] = 1, 2 for i in range(3, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n] ``` 上述代码通过动态规划的方式解决了 `Climbing Stairs` 问题,时间复杂度为 \(O(n)\),空间复杂度同样为 \(O(n)\)[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值