力扣(LeetCode)刷题

最后更新于:2026年3月15日 上午

力扣(LeetCode)刷题

第一部分:基础数据结构

1. 数组(Array)

1.1 基本概念

数组是最基本的数据结构,在内存中连续存储相同类型的元素。

1
2
3
4
内存布局:
地址: 0x100 0x104 0x108 0x10C 0x110
值: [ 10 ][ 20 ][ 30 ][ 40 ][ 50 ]
索引: 0 1 2 3 4

1.2 动态数组实现

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
int *data;
int size;
int capacity;
} DynamicArray;

// 创建动态数组
DynamicArray* createArray(int capacity) {
DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
arr->data = (int*)malloc(sizeof(int) * capacity);
arr->size = 0;
arr->capacity = capacity;
return arr;
}

// 扩容
void resize(DynamicArray *arr) {
arr->capacity *= 2;
arr->data = (int*)realloc(arr->data, sizeof(int) * arr->capacity);
}

// 尾部添加 O(1) 均摊
void pushBack(DynamicArray *arr, int val) {
if (arr->size == arr->capacity) {
resize(arr);
}
arr->data[arr->size++] = val;
}

// 指定位置插入 O(n)
void insertAt(DynamicArray *arr, int index, int val) {
if (index < 0 || index > arr->size) return;
if (arr->size == arr->capacity) {
resize(arr);
}
// 后移元素
memmove(&arr->data[index + 1], &arr->data[index],
sizeof(int) * (arr->size - index));
arr->data[index] = val;
arr->size++;
}

// 删除指定位置 O(n)
int removeAt(DynamicArray *arr, int index) {
if (index < 0 || index >= arr->size) return -1;
int val = arr->data[index];
memmove(&arr->data[index], &arr->data[index + 1],
sizeof(int) * (arr->size - index - 1));
arr->size--;
return val;
}

// 获取元素 O(1)
int getAt(DynamicArray *arr, int index) {
if (index < 0 || index >= arr->size) return -1;
return arr->data[index];
}

// 释放
void freeArray(DynamicArray *arr) {
free(arr->data);
free(arr);
}

// 打印
void printArray(DynamicArray *arr) {
printf("[");
for (int i = 0; i < arr->size; i++) {
printf("%d", arr->data[i]);
if (i < arr->size - 1) printf(", ");
}
printf("] (size=%d, cap=%d)\n", arr->size, arr->capacity);
}

int main() {
DynamicArray *arr = createArray(4);

pushBack(arr, 10);
pushBack(arr, 20);
pushBack(arr, 30);
pushBack(arr, 40);
printArray(arr); // [10, 20, 30, 40]

pushBack(arr, 50); // 触发扩容
printArray(arr); // [10, 20, 30, 40, 50] cap=8

insertAt(arr, 2, 25);
printArray(arr); // [10, 20, 25, 30, 40, 50]

removeAt(arr, 1);
printArray(arr); // [10, 25, 30, 40, 50]

freeArray(arr);
return 0;
}

1.3 LeetCode 实战

LeetCode 1. 两数之和 (Two Sum)

1
2
3
4
5
给定数组 nums 和目标值 target
找出和为目标值的两个整数的索引。

示例:nums = [2,7,11,15], target = 9
输出:[0,1] (因为 2+7=9)

image-20260312094024766

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
* 方法一:暴力法 O(n²)
*/
int* twoSum_brute(int* nums, int numsSize, int target, int* returnSize) {
int *result = (int*)malloc(sizeof(int) * 2);
*returnSize = 2;

for (int i = 0; i < numsSize - 1; i++) {
for (int j = i + 1; j < numsSize; j++) {
if (nums[i] + nums[j] == target) {
result[0] = i;
result[1] = j;
return result;
}
}
}
*returnSize = 0;
return result;
}

/*
* 方法二:哈希表法 O(n) - C语言手动实现哈希表
*/
#define HASH_SIZE 10007

typedef struct HashNode {
int key; // 值
int val; // 索引
struct HashNode *next;
} HashNode;

typedef struct {
HashNode *buckets[HASH_SIZE];
} HashMap;

HashMap* createMap() {
HashMap *map = (HashMap*)calloc(1, sizeof(HashMap));
return map;
}

int hashFunc(int key) {
return ((key % HASH_SIZE) + HASH_SIZE) % HASH_SIZE;
}

void mapPut(HashMap *map, int key, int val) {
int h = hashFunc(key);
HashNode *node = (HashNode*)malloc(sizeof(HashNode));
node->key = key;
node->val = val;
node->next = map->buckets[h];
map->buckets[h] = node;
}

int mapGet(HashMap *map, int key, int *found) {
int h = hashFunc(key);
HashNode *cur = map->buckets[h];
while (cur) {
if (cur->key == key) {
*found = 1;
return cur->val;
}
cur = cur->next;
}
*found = 0;
return -1;
}

void freeMap(HashMap *map) {
for (int i = 0; i < HASH_SIZE; i++) {
HashNode *cur = map->buckets[i];
while (cur) {
HashNode *tmp = cur;
cur = cur->next;
free(tmp);
}
}
free(map);
}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
int *result = (int*)malloc(sizeof(int) * 2);
*returnSize = 2;

HashMap *map = createMap();

for (int i = 0; i < numsSize; i++) {
int complement = target - nums[i];
int found = 0;
int idx = mapGet(map, complement, &found);

if (found) {
result[0] = idx;
result[1] = i;
freeMap(map);
return result;
}
mapPut(map, nums[i], i);
}

freeMap(map);
*returnSize = 0;
return result;
}

LeetCode 26. 删除有序数组中的重复项

1
2
3
4
给定升序数组,原地删除重复元素,返回新长度。

示例:nums = [1,1,2] → 返回2,nums变为[1,2,...]
nums = [0,0,1,1,1,2,2,3,3,4] → 返回5
image-20260312100718153
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 双指针法
* slow 指向已处理区域的最后一个元素
* fast 扫描所有元素
*
* 示例过程:[0,0,1,1,1,2,2,3,3,4]
* s
* f → 0==0 跳过
* f → 1!=0, s++, nums[s]=1 → [0,1,1,1,1,2,2,3,3,4]
* s
*/
int removeDuplicates(int* nums, int numsSize) {
if (numsSize == 0) return 0;

int slow = 0;
for (int fast = 1; fast < numsSize; fast++) {
if (nums[fast] != nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}

LeetCode 27. 移除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 原地移除所有值为 val 的元素
* 示例:nums = [3,2,2,3], val = 3 → 返回2, nums=[2,2,...]
*/
int removeElement(int* nums, int numsSize, int val) {
int slow = 0;
for (int fast = 0; fast < numsSize; fast++) {
if (nums[fast] != val) {
nums[slow++] = nums[fast];
}
}
return slow;
}

image-20260312102227826

LeetCode 88. 合并两个有序数组

image-20260312103905882

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
/*
* nums1 有足够空间,从后往前合并避免覆盖
*
* nums1 = [1,2,3], m=3
* nums2 = [2,5,6], n=3
* 结果: [1,2,2,3,5,6]
*/
void merge(int* nums1, int nums1Size, int m,
int* nums2, int nums2Size, int n) {
int i = m - 1; // nums1 的尾指针
int j = n - 1; // nums2 的尾指针
int k = m + n - 1; // 合并后的尾指针

while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
// nums2 剩余元素
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}

LeetCode 53. 最大子数组和 (Kadane算法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* 动态规划 / Kadane算法
* dp[i] = max(nums[i], dp[i-1] + nums[i])
*
* 示例:[-2,1,-3,4,-1,2,1,-5,4]
* dp: [-2,1,-2,4, 3,5,6, 1,5]
* 最大值 = 6 (子数组 [4,-1,2,1])
*/
int maxSubArray(int* nums, int numsSize) {
int maxSum = nums[0];
int currentSum = nums[0];

for (int i = 1; i < numsSize; i++) {
// 要么延续前面的子数组,要么从当前重新开始
currentSum = currentSum + nums[i] > nums[i] ?
currentSum + nums[i] : nums[i];
if (currentSum > maxSum) {
maxSum = currentSum;
}
}
return maxSum;
}

LeetCode 136. 只出现一次的数字

1
2
3
4
5
6
7
8
9
10
11
/*
* 利用异或性质:a ^ a = 0, a ^ 0 = a
* 所有成对的数异或后为0,剩下的就是单独的数
*/
int singleNumber(int* nums, int numsSize) {
int result = 0;
for (int i = 0; i < numsSize; i++) {
result ^= nums[i];
}
return result;
}

LeetCode 169. 多数元素 (摩尔投票法)

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
/*
* Boyer-Moore 投票算法
* 思路:候选人和计数器,不同元素互相"抵消"
数组:[1, 1, 2, 1, 3]目标:找超过一半的数 → 1
开始遍历:
数字 1 → count=0 → 候选人 = 1,count=1
数字 1 → 相同 → count=2
数字 2 → 不同 → count=1
数字 1 → 相同 → count=2
数字 3 → 不同 → count=1
最后 候选人 = 1 ✅
*/
int majorityElement(int* nums, int numsSize) {
// 摩尔投票算法
//初始化候选人和计数器
int candidate = nums[0];
int count = 1;

for (int i = 1; i < numsSize; i++) {
//如果计数器为0,更新候选人和计数器
if (count == 0) {
candidate = nums[i];
count = 1;
//如果当前元素等于候选人,增加计数器
} else if (nums[i] == candidate) {
count++;
//如果当前元素不等于候选人,减少计数器
} else {
count--;
}
}
return candidate;
}

image-20260313165811022

LeetCode 238. 除自身以外数组的乘积

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
/*
给你一个数组 nums,要求返回一个新数组,每个位置的值 = 数组中除了自己以外所有数的乘积。
并且有两个限制:
不能用除法
时间 O (n)(只能遍历一遍或两遍)
空间 O (1)(除结果数组外,不能开额外大数组)

超级简单的总结
前缀积:算每个数左边的乘积
后缀积:算每个数右边的乘积
结果:左右相乘 → 就是除自己以外的总乘积
满足:不用除法、O (n) 时间、O (1) 空间
*/
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
*returnSize = numsSize;
int *result = (int*)malloc(sizeof(int) * numsSize);

// 先计算前缀积存入result
result[0] = 1;
for (int i = 1; i < numsSize; i++) {
result[i] = result[i-1] * nums[i-1];
}

// 从右往左乘以后缀积
int suffix = 1;
for (int i = numsSize - 1; i >= 0; i--) {
result[i] *= suffix;
suffix *= nums[i];
}

return result;
}

2. 链表(Linked List)

2.1 基本概念

1
2
3
4
5
6
单链表:
[data|next] → [data|next] → [data|next] → NULL
头节点

双向链表:
NULL ← [prev|data|next] ⇄ [prev|data|next] ⇄ [prev|data|next] → NULL

2.2 完整实现

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <stdio.h>
#include <stdlib.h>

// =================== 单链表 ===================

typedef struct ListNode {
int val;
struct ListNode *next;
} ListNode;

// 创建节点
ListNode* createNode(int val) {
ListNode *node = (ListNode*)malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
return node;
}

// 头插法 O(1)
ListNode* insertHead(ListNode *head, int val) {
ListNode *node = createNode(val);
node->next = head;
return node;
}

// 尾插法 O(n)
ListNode* insertTail(ListNode *head, int val) {
ListNode *node = createNode(val);
if (!head) return node;

ListNode *cur = head;
while (cur->next) {
cur = cur->next;
}
cur->next = node;
return head;
}

// 删除指定值的第一个节点
ListNode* deleteNode(ListNode *head, int val) {
// 使用哑节点简化边界处理
ListNode dummy = {0, head};
ListNode *prev = &dummy;
ListNode *cur = head;

while (cur) {
if (cur->val == val) {
prev->next = cur->next;
free(cur);
break;
}
prev = cur;
cur = cur->next;
}
return dummy.next;
}

// 反转链表(迭代)
ListNode* reverseList(ListNode *head) {
ListNode *prev = NULL;
ListNode *cur = head;

while (cur) {
ListNode *next = cur->next; // 暂存
cur->next = prev; // 反转
prev = cur; // 前进
cur = next;
}
return prev;
}

// 获取长度
int getLength(ListNode *head) {
int len = 0;
while (head) {
len++;
head = head->next;
}
return len;
}

// 打印
void printList(ListNode *head) {
while (head) {
printf("%d", head->val);
if (head->next) printf(" → ");
head = head->next;
}
printf(" → NULL\n");
}

// 释放链表
void freeList(ListNode *head) {
while (head) {
ListNode *tmp = head;
head = head->next;
free(tmp);
}
}

// 从数组创建链表(方便测试)
ListNode* createListFromArray(int *arr, int n) {
if (n == 0) return NULL;
ListNode *head = createNode(arr[0]);
ListNode *cur = head;
for (int i = 1; i < n; i++) {
cur->next = createNode(arr[i]);
cur = cur->next;
}
return head;
}

int main() {
int arr[] = {1, 2, 3, 4, 5};
ListNode *head = createListFromArray(arr, 5);

printf("原链表: ");
printList(head); // 1 → 2 → 3 → 4 → 5 → NULL

head = reverseList(head);
printf("反转后: ");
printList(head); // 5 → 4 → 3 → 2 → 1 → NULL

head = deleteNode(head, 3);
printf("删除3: ");
printList(head); // 5 → 4 → 2 → 1 → NULL

freeList(head);
return 0;
}

2.3 LeetCode 实战

LeetCode 206. 反转链表

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
/* 
* 迭代法 - 三指针
*
* 过程演示:1 → 2 → 3 → NULL
*
* Step 0: prev=NULL, cur=1, next=2
* NULL ← 1 2 → 3 → NULL
* Step 1: prev=1, cur=2, next=3
* NULL ← 1 ← 2 3 → NULL
* Step 2: prev=2, cur=3, next=NULL
* NULL ← 1 ← 2 ← 3
* Step 3: cur=NULL, 返回 prev=3
*/
ListNode* reverseList(ListNode* head) {
ListNode *prev = NULL, *cur = head;
while (cur) {
ListNode *next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
return prev;
}

/* 递归法 */
ListNode* reverseListRecursive(ListNode* head) {
// 基线条件
if (!head || !head->next) return head;

// 递归反转后面的部分
ListNode *newHead = reverseListRecursive(head->next);

// head->next 是反转后链表的尾节点
head->next->next = head; // 让尾节点指向 head
head->next = NULL; // head 变成新的尾节点

return newHead;
}

LeetCode 21. 合并两个有序链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* 示例:1→2→4 和 1→3→4 → 1→1→2→3→4→4
* 使用哑节点简化代码
*/
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode dummy = {0, NULL};
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;
}
tail->next = l1 ? l1 : l2;

return dummy.next;
}

LeetCode 141. 环形链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* Floyd 快慢指针算法
* 快指针每次走2步,慢指针每次走1步
* 如果有环,它们一定会相遇
*
* 1 → 2 → 3 → 4
* ↑ ↓
* 6 ← 5
*/
#include <stdbool.h>

bool hasCycle(ListNode *head) {
ListNode *slow = head, *fast = head;

while (fast && fast->next) {
slow = slow->next; // 走1步
fast = fast->next->next; // 走2步
if (slow == fast) return true;
}
return false;
}

LeetCode 142. 环形链表 II(找入环点)

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
/*
* 数学推导:
* 设 head 到入环点距离为 a,入环点到相遇点为 b,
* 相遇点到入环点为 c(环的剩余部分)
*
* slow 走了: a + b
* fast 走了: a + b + n(b+c) (绕了n圈)
* fast = 2 * slow → a + b + n(b+c) = 2(a+b)
* → a = (n-1)(b+c) + c
* → 从 head 和相遇点同时出发,相遇处即入环点
*/
ListNode* detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;

// 第一步:找到相遇点
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
// 第二步:找入环点
ListNode *ptr = head;
while (ptr != slow) {
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return NULL;
}

LeetCode 19. 删除链表的倒数第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
/*
* 快慢指针,快指针先走n步
*
* 示例:1→2→3→4→5, n=2 → 删除4 → 1→2→3→5
*
* 用哑节点处理删除头节点的情况
*/
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode dummy = {0, head};
ListNode *fast = &dummy, *slow = &dummy;

// fast 先走 n+1 步
for (int i = 0; i <= n; i++) {
fast = fast->next;
}

// 同时走,fast到末尾时slow到达要删除节点的前一个
while (fast) {
fast = fast->next;
slow = slow->next;
}

ListNode *toDelete = slow->next;
slow->next = toDelete->next;
free(toDelete);

return dummy.next;
}

LeetCode 160. 相交链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* 双指针法:A走完走B,B走完走A
* 如果相交,一定在交点相遇(路程相同)
*
* A: a1 → a2 ↘
* c1 → c2 → c3
* B: b1 → b2 → b3 ↗
*
* 指针A路程: a1→a2→c1→c2→c3→b1→b2→b3→c1 ✓
* 指针B路程: b1→b2→b3→c1→c2→c3→a1→a2→c1 ✓
*/
ListNode* getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pA = headA, *pB = headB;

while (pA != pB) {
pA = pA ? pA->next : headB;
pB = pB ? pB->next : headA;
}
return pA;
}

LeetCode 234. 回文链表

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
/*
* O(n)时间 O(1)空间:快慢指针找中点 + 反转后半部分 + 比较
*/
bool isPalindrome(ListNode* head) {
if (!head || !head->next) return true;

// 1. 找中点
ListNode *slow = head, *fast = head;
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}

// 2. 反转后半部分
ListNode *prev = NULL, *cur = slow->next;
while (cur) {
ListNode *next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}

// 3. 比较
ListNode *p1 = head, *p2 = prev;
bool result = true;
while (p2) {
if (p1->val != p2->val) {
result = false;
break;
}
p1 = p1->next;
p2 = p2->next;
}

// 4. (可选)恢复链表

return result;
}

LeetCode 25. K个一组翻转链表(困难)

C

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
/*
* 示例:1→2→3→4→5, k=2 → 2→1→4→3→5
* 1→2→3→4→5, k=3 → 3→2→1→4→5
*/

// 反转从 head 开始的 k 个节点,返回新头
// 如果不足 k 个,不反转
ListNode* reverseKGroup(ListNode* head, int k) {
// 检查是否有 k 个节点
ListNode *check = head;
for (int i = 0; i < k; i++) {
if (!check) return head; // 不足k个,直接返回
check = check->next;
}

// 反转 k 个节点
ListNode *prev = NULL, *cur = head;
for (int i = 0; i < k; i++) {
ListNode *next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}

// head 现在是这组的尾,连接下一组的结果
head->next = reverseKGroup(cur, k);

return prev; // prev 是新的头
}

3. 栈(Stack)

3.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
后进先出 (LIFO - Last In First Out)

┌─────┐
push → │ 3 │ ← top(栈顶)
├─────┤
2
├─────┤
1 │ ← bottom(栈底)
└─────┘

push(4): pop() → 返回4:
┌─────┐ ┌─────┐
4 │ ← top │ 3 │ ← top
├─────┤ ├─────┤
3 │ │ 2
├─────┤ ├─────┤
2 │ │ 1
├─────┤ └─────┘
1
└─────┘

3.2 实现

C

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
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// ============ 数组实现的栈 ============
typedef struct {
int *data;
int top;
int capacity;
} Stack;

Stack* createStack(int capacity) {
Stack *s = (Stack*)malloc(sizeof(Stack));
s->data = (int*)malloc(sizeof(int) * capacity);
s->top = -1;
s->capacity = capacity;
return s;
}

bool isEmpty(Stack *s) { return s->top == -1; }
bool isFull(Stack *s) { return s->top == s->capacity - 1; }

void push(Stack *s, int val) {
if (isFull(s)) {
s->capacity *= 2;
s->data = (int*)realloc(s->data, sizeof(int) * s->capacity);
}
s->data[++s->top] = val;
}

int pop(Stack *s) {
if (isEmpty(s)) { printf("Stack underflow!\n"); return -1; }
return s->data[s->top--];
}

int peek(Stack *s) {
if (isEmpty(s)) { printf("Stack empty!\n"); return -1; }
return s->data[s->top];
}

void freeStack(Stack *s) {
free(s->data);
free(s);
}

3.3 LeetCode 实战

LeetCode 20. 有效的括号

C

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
/*
* 示例:"()[]{}" → true
* "(]" → false
* "([)]" → false
* "{[]}" → true
*
* 遇到左括号入栈,遇到右括号检查栈顶是否匹配
*/
bool isValid(char* s) {
int len = strlen(s);
char *stack = (char*)malloc(len);
int top = -1;

for (int i = 0; s[i]; i++) {
char c = s[i];
if (c == '(' || c == '[' || c == '{') {
stack[++top] = c;
} else {
if (top == -1) { free(stack); return false; }
char t = stack[top--];
if ((c == ')' && t != '(') ||
(c == ']' && t != '[') ||
(c == '}' && t != '{')) {
free(stack);
return false;
}
}
}

bool result = (top == -1);
free(stack);
return result;
}

LeetCode 155. 最小栈

C

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
/*
* 要求 push, pop, top, getMin 都是 O(1)
* 思路:用辅助栈同步记录当前最小值
*/
typedef struct {
int *data;
int *minData; // 辅助栈,记录每层的最小值
int top;
int capacity;
} MinStack;

MinStack* minStackCreate() {
MinStack *s = (MinStack*)malloc(sizeof(MinStack));
s->capacity = 1024;
s->data = (int*)malloc(sizeof(int) * s->capacity);
s->minData = (int*)malloc(sizeof(int) * s->capacity);
s->top = -1;
return s;
}

void minStackPush(MinStack* obj, int val) {
obj->top++;
obj->data[obj->top] = val;
if (obj->top == 0) {
obj->minData[obj->top] = val;
} else {
int prevMin = obj->minData[obj->top - 1];
obj->minData[obj->top] = val < prevMin ? val : prevMin;
}
}

void minStackPop(MinStack* obj) {
obj->top--;
}

int minStackTop(MinStack* obj) {
return obj->data[obj->top];
}

int minStackGetMin(MinStack* obj) {
return obj->minData[obj->top];
}

void minStackFree(MinStack* obj) {
free(obj->data);
free(obj->minData);
free(obj);
}

LeetCode 150. 逆波兰表达式求值

C

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
/*
* 示例:["2","1","+","3","*"] → ((2+1)*3) = 9
* ["4","13","5","/","+"] → (4+(13/5)) = 6
*
* 遇到数字入栈,遇到运算符弹出两个数计算后入栈
*/
#include <string.h>

int evalRPN(char** tokens, int tokensSize) {
int *stack = (int*)malloc(sizeof(int) * tokensSize);
int top = -1;

for (int i = 0; i < tokensSize; i++) {
char *t = tokens[i];

// 判断是否是运算符(长度为1且是+-*/)
if (strlen(t) == 1 && (t[0] == '+' || t[0] == '-' ||
t[0] == '*' || t[0] == '/')) {
int b = stack[top--];
int a = stack[top--];
switch (t[0]) {
case '+': stack[++top] = a + b; break;
case '-': stack[++top] = a - b; break;
case '*': stack[++top] = a * b; break;
case '/': stack[++top] = a / b; break;
}
} else {
stack[++top] = atoi(t);
}
}

int result = stack[top];
free(stack);
return result;
}

LeetCode 739. 每日温度(单调栈)

C

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
/*
* 给定温度数组,求每天还要等几天才能等到更高温度
*
* 示例:[73,74,75,71,69,72,76,73]
* 输出:[ 1, 1, 4, 2, 1, 1, 0, 0]
*
* 单调递减栈:栈中存索引
*/
int* dailyTemperatures(int* temperatures, int temperaturesSize,
int* returnSize) {
*returnSize = temperaturesSize;
int *result = (int*)calloc(temperaturesSize, sizeof(int));
int *stack = (int*)malloc(sizeof(int) * temperaturesSize);
int top = -1;

for (int i = 0; i < temperaturesSize; i++) {
// 当前温度大于栈顶温度,弹出并计算天数
while (top >= 0 && temperatures[i] > temperatures[stack[top]]) {
int idx = stack[top--];
result[idx] = i - idx;
}
stack[++top] = i;
}

free(stack);
return result;
}

LeetCode 84. 柱状图中最大的矩形(困难)

C

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
/*
* 单调递增栈
*
* heights = [2,1,5,6,2,3]
* 输出 10 (高5宽2的矩形)
*
* 对每个柱子,找左边第一个比它矮的和右边第一个比它矮的
*/
int largestRectangleArea(int* heights, int heightsSize) {
int *stack = (int*)malloc(sizeof(int) * (heightsSize + 1));
int top = -1;
int maxArea = 0;

for (int i = 0; i <= heightsSize; i++) {
// i == heightsSize 时高度为 0,确保栈清空
int curHeight = (i == heightsSize) ? 0 : heights[i];

while (top >= 0 && curHeight < heights[stack[top]]) {
int h = heights[stack[top--]];
int w = (top >= 0) ? (i - stack[top] - 1) : i;
int area = h * w;
if (area > maxArea) maxArea = area;
}
stack[++top] = i;
}

free(stack);
return maxArea;
}

4. 队列(Queue)

4.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
先进先出 (FIFO - First In First Out)

入队(enqueue) 出队(dequeue)
[4] [3] [2] [1]
rear front

循环队列(数组实现):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ 2345 │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
↑ front ↑ rear

4.2 实现

C

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
// ============ 循环队列 ============
typedef struct {
int *data;
int front;
int rear;
int size;
int capacity;
} CircularQueue;

CircularQueue* createQueue(int capacity) {
CircularQueue *q = (CircularQueue*)malloc(sizeof(CircularQueue));
q->data = (int*)malloc(sizeof(int) * (capacity + 1)); // 多一个空位
q->front = 0;
q->rear = 0;
q->size = 0;
q->capacity = capacity + 1;
return q;
}

bool queueIsEmpty(CircularQueue *q) {
return q->front == q->rear;
}

bool queueIsFull(CircularQueue *q) {
return (q->rear + 1) % q->capacity == q->front;
}

void enqueue(CircularQueue *q, int val) {
if (queueIsFull(q)) { printf("Queue full!\n"); return; }
q->data[q->rear] = val;
q->rear = (q->rear + 1) % q->capacity;
q->size++;
}

int dequeue(CircularQueue *q) {
if (queueIsEmpty(q)) { printf("Queue empty!\n"); return -1; }
int val = q->data[q->front];
q->front = (q->front + 1) % q->capacity;
q->size--;
return val;
}

int queueFront(CircularQueue *q) {
if (queueIsEmpty(q)) return -1;
return q->data[q->front];
}

void freeQueue(CircularQueue *q) {
free(q->data);
free(q);
}

4.3 LeetCode 实战

LeetCode 232. 用栈实现队列

C

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
/*
* 双栈模拟队列
* stackIn 负责入队
* stackOut 负责出队
*
* 当 stackOut 为空时,将 stackIn 全部倒入 stackOut
*
* push(1), push(2), push(3):
* stackIn: [1,2,3] stackOut: []
*
* pop():
* 倒入: stackIn: [] stackOut: [3,2,1]
* 弹出1,stackOut: [3,2]
*/
typedef struct {
int stackIn[100];
int stackOut[100];
int topIn;
int topOut;
} MyQueue;

MyQueue* myQueueCreate() {
MyQueue *q = (MyQueue*)malloc(sizeof(MyQueue));
q->topIn = -1;
q->topOut = -1;
return q;
}

void transfer(MyQueue *q) {
if (q->topOut == -1) {
while (q->topIn >= 0) {
q->stackOut[++q->topOut] = q->stackIn[q->topIn--];
}
}
}

void myQueuePush(MyQueue* obj, int x) {
obj->stackIn[++obj->topIn] = x;
}

int myQueuePop(MyQueue* obj) {
transfer(obj);
return obj->stackOut[obj->topOut--];
}

int myQueuePeek(MyQueue* obj) {
transfer(obj);
return obj->stackOut[obj->topOut];
}

bool myQueueEmpty(MyQueue* obj) {
return obj->topIn == -1 && obj->topOut == -1;
}

void myQueueFree(MyQueue* obj) {
free(obj);
}

LeetCode 225. 用队列实现栈

C

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
/*
* 用一个队列实现:
* push时将新元素加入后,把前面的元素依次出队再入队
*
* 示例:push(1) → [1]
* push(2) → [2] → 出1入1 → [2,1] → 重排 → [2,1]
* 实际:加入2后 [1,2],然后出1入1 → [2,1]
*/
typedef struct {
int data[200];
int front, rear, size;
} MyStack;

MyStack* myStackCreate() {
MyStack *s = (MyStack*)calloc(1, sizeof(MyStack));
return s;
}

void myStackPush(MyStack* obj, int x) {
obj->data[obj->rear++] = x;
obj->size++;

// 将前面的 size-1 个元素移到后面
for (int i = 0; i < obj->size - 1; i++) {
obj->data[obj->rear++] = obj->data[obj->front++];
}
}

int myStackPop(MyStack* obj) {
obj->size--;
return obj->data[obj->front++];
}

int myStackTop(MyStack* obj) {
return obj->data[obj->front];
}

bool myStackEmpty(MyStack* obj) {
return obj->size == 0;
}

void myStackFree(MyStack* obj) {
free(obj);
}

LeetCode 239. 滑动窗口最大值(困难 - 单调队列)

C

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
/*
* 给定数组和窗口大小k,返回每个窗口的最大值
*
* nums = [1,3,-1,-3,5,3,6,7], k = 3
* 窗口 [1,3,-1] max=3
* [3,-1,-3] max=3
* [-1,-3,5] max=5
* [-3,5,3] max=5
* [5,3,6] max=6
* [3,6,7] max=7
* 输出:[3,3,5,5,6,7]
*
* 单调递减双端队列:队头始终是窗口最大值的索引
*/
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
*returnSize = numsSize - k + 1;
int *result = (int*)malloc(sizeof(int) * (*returnSize));
int *deque = (int*)malloc(sizeof(int) * numsSize); // 存索引
int front = 0, rear = -1;

for (int i = 0; i < numsSize; i++) {
// 移除超出窗口范围的队头元素
while (front <= rear && deque[front] < i - k + 1) {
front++;
}

// 移除队尾所有比当前值小的元素(维护递减)
while (front <= rear && nums[deque[rear]] <= nums[i]) {
rear--;
}

// 加入当前索引
deque[++rear] = i;

// 窗口形成后,记录最大值
if (i >= k - 1) {
result[i - k + 1] = nums[deque[front]];
}
}

free(deque);
return result;
}

5. 哈希表(Hash Table)

5.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
哈希函数将 key 映射到数组索引:

key "apple"hash("apple") → 3
key "banana"hash("banana") → 7

数组:
[0] → NULL
[1] → NULL
[2] → NULL
[3]["apple", 5]
[4] → NULL
[5] → NULL
[6] → NULL
[7]["banana", 3]

冲突解决(链地址法):
[3]["apple", 5]["cherry", 2] → NULL

5.2 完整实现

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define TABLE_SIZE 1024

typedef struct Entry {
char *key;
int value;
struct Entry *next;
} Entry;

typedef struct {
Entry *buckets[TABLE_SIZE];
int size;
} HashTable;

// 哈希函数 (djb2)
unsigned int hash(const char *key) {
unsigned int h = 5381;
while (*key) {
h = ((h << 5) + h) + *key; // h * 33 + c
key++;
}
return h % TABLE_SIZE;
}

HashTable* createHashTable() {
HashTable *ht = (HashTable*)calloc(1, sizeof(HashTable));
return ht;
}

// 插入/更新
void htPut(HashTable *ht, const char *key, int value) {
unsigned int idx = hash(key);
Entry *cur = ht->buckets[idx];

// 查找是否已存在
while (cur) {
if (strcmp(cur->key, key) == 0) {
cur->value = value; // 更新
return;
}
cur = cur->next;
}

// 新增(头插法)
Entry *entry = (Entry*)malloc(sizeof(Entry));
entry->key = strdup(key);
entry->value = value;
entry->next = ht->buckets[idx];
ht->buckets[idx] = entry;
ht->size++;
}

// 获取
int htGet(HashTable *ht, const char *key, bool *found) {
unsigned int idx = hash(key);
Entry *cur = ht->buckets[idx];

while (cur) {
if (strcmp(cur->key, key) == 0) {
*found = true;
return cur->value;
}
cur = cur->next;
}
*found = false;
return -1;
}

// 删除
bool htRemove(HashTable *ht, const char *key) {
unsigned int idx = hash(key);
Entry *cur = ht->buckets[idx];
Entry *prev = NULL;

while (cur) {
if (strcmp(cur->key, key) == 0) {
if (prev) prev->next = cur->next;
else ht->buckets[idx] = cur->next;
free(cur->key);
free(cur);
ht->size--;
return true;
}
prev = cur;
cur = cur->next;
}
return false;
}

// 释放
void freeHashTable(HashTable *ht) {
for (int i = 0; i < TABLE_SIZE; i++) {
Entry *cur = ht->buckets[i];
while (cur) {
Entry *tmp = cur;
cur = cur->next;
free(tmp->key);
free(tmp);
}
}
free(ht);
}

int main() {
HashTable *ht = createHashTable();

htPut(ht, "apple", 5);
htPut(ht, "banana", 3);
htPut(ht, "cherry", 8);

bool found;
printf("apple = %d\n", htGet(ht, "apple", &found)); // 5
printf("banana = %d\n", htGet(ht, "banana", &found)); // 3

htRemove(ht, "banana");
htGet(ht, "banana", &found);
printf("banana found: %d\n", found); // 0

freeHashTable(ht);
return 0;
}

5.3 LeetCode 实战

LeetCode 242. 有效的字母异位词

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 判断两个字符串是否是异位词(字母种类和数量相同)
* 示例:"anagram""nagaram"true
* "rat""car"false
*
* 利用26大小的数组作为哈希表
*/
bool isAnagram(char* s, char* t) {
int count[26] = {0};

while (*s) count[*s++ - 'a']++;
while (*t) count[*t++ - 'a']--;

for (int i = 0; i < 26; i++) {
if (count[i] != 0) return false;
}
return true;
}

LeetCode 49. 字母异位词分组

C

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
/*
* 示例:["eat","tea","tan","ate","nat","bat"]
* 输出:[["bat"],["nat","tan"],["ate","eat","tea"]]
*
* 思路:将每个单词排序后作为key,相同key的分为一组
*/
#include <string.h>

int charCompare(const void *a, const void *b) {
return *(char*)a - *(char*)b;
}

// 简化版:返回分组结果
char*** groupAnagrams(char** strs, int strsSize, int* returnSize,
int** returnColumnSizes) {
// 为每个字符串生成排序后的key
char **keys = (char**)malloc(sizeof(char*) * strsSize);
for (int i = 0; i < strsSize; i++) {
keys[i] = strdup(strs[i]);
qsort(keys[i], strlen(keys[i]), sizeof(char), charCompare);
}

// 分组
bool *used = (bool*)calloc(strsSize, sizeof(bool));
char ***result = (char***)malloc(sizeof(char**) * strsSize);
*returnColumnSizes = (int*)malloc(sizeof(int) * strsSize);
*returnSize = 0;

for (int i = 0; i < strsSize; i++) {
if (used[i]) continue;

// 找所有和 keys[i] 相同的
char **group = (char**)malloc(sizeof(char*) * strsSize);
int groupSize = 0;

for (int j = i; j < strsSize; j++) {
if (!used[j] && strcmp(keys[i], keys[j]) == 0) {
group[groupSize++] = strs[j];
used[j] = true;
}
}

result[*returnSize] = group;
(*returnColumnSizes)[*returnSize] = groupSize;
(*returnSize)++;
}

for (int i = 0; i < strsSize; i++) free(keys[i]);
free(keys);
free(used);

return result;
}

LeetCode 128. 最长连续序列

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
* 示例:[100,4,200,1,3,2] → 4 (序列[1,2,3,4])
* 要求 O(n) 时间
*
* 思路:用哈希表,对每个数检查是否是序列起点(num-1不存在)
*/

// 简单的整数哈希集合
#define HS_SIZE 100003

typedef struct HSNode {
int val;
struct HSNode *next;
} HSNode;

typedef struct {
HSNode *buckets[HS_SIZE];
} HashSet;

HashSet* createHashSet() {
return (HashSet*)calloc(1, sizeof(HashSet));
}

int hsHash(int val) {
return ((val % HS_SIZE) + HS_SIZE) % HS_SIZE;
}

void hsAdd(HashSet *hs, int val) {
int h = hsHash(val);
HSNode *cur = hs->buckets[h];
while (cur) {
if (cur->val == val) return; // 已存在
cur = cur->next;
}
HSNode *node = (HSNode*)malloc(sizeof(HSNode));
node->val = val;
node->next = hs->buckets[h];
hs->buckets[h] = node;
}

bool hsContains(HashSet *hs, int val) {
int h = hsHash(val);
HSNode *cur = hs->buckets[h];
while (cur) {
if (cur->val == val) return true;
cur = cur->next;
}
return false;
}

void freeHashSet(HashSet *hs) {
for (int i = 0; i < HS_SIZE; i++) {
HSNode *cur = hs->buckets[i];
while (cur) {
HSNode *tmp = cur;
cur = cur->next;
free(tmp);
}
}
free(hs);
}

int longestConsecutive(int* nums, int numsSize) {
if (numsSize == 0) return 0;

HashSet *hs = createHashSet();
for (int i = 0; i < numsSize; i++) {
hsAdd(hs, nums[i]);
}

int maxLen = 0;
for (int i = 0; i < numsSize; i++) {
// 只从序列起点开始计数
if (!hsContains(hs, nums[i] - 1)) {
int cur = nums[i];
int len = 1;
while (hsContains(hs, cur + 1)) {
cur++;
len++;
}
if (len > maxLen) maxLen = len;
}
}

freeHashSet(hs);
return maxLen;
}

LeetCode 383. 赎金信

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
* ransomNote能否由magazine中的字母构成(每个字母只能用一次)
* 示例:"aa", "aab"true
* "aa", "ab"false
*/
bool canConstruct(char* ransomNote, char* magazine) {
int count[26] = {0};

while (*magazine) count[*magazine++ - 'a']++;
while (*ransomNote) {
if (--count[*ransomNote++ - 'a'] < 0) return false;
}
return true;
}

6. 二叉树(Binary Tree)

6.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
         1          ← 根节点 (root)
/ \
2 3 ← 深度1
/ \ \
4 5 6 ← 深度2(叶节点: 4, 5, 6)

术语:
- 深度 (depth): 从根到节点的边数
- 高度 (height): 从节点到最远叶子的边数
- 完全二叉树: 除最后一层外全满,最后一层靠左
- 满二叉树: 每层都满
- 二叉搜索树(BST): 左子树 < 根 < 右子树

遍历顺序:
- 前序 (Pre-order): 根 → 左 → 右 → 1,2,4,5,3,6
- 中序 (In-order): 左 → 根 → 右 → 4,2,5,1,3,6
- 后序 (Post-order): 左 → 右 → 根 → 4,5,2,6,3,1
- 层序 (Level-order): 逐层 → 1,2,3,4,5,6

6.2 完整实现

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;

TreeNode* createTreeNode(int val) {
TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = node->right = NULL;
return node;
}

// ============ 三种遍历(递归) ============

void preorder(TreeNode *root) {
if (!root) return;
printf("%d ", root->val);
preorder(root->left);
preorder(root->right);
}

void inorder(TreeNode *root) {
if (!root) return;
inorder(root->left);
printf("%d ", root->val);
inorder(root->right);
}

void postorder(TreeNode *root) {
if (!root) return;
postorder(root->left);
postorder(root->right);
printf("%d ", root->val);
}

// ============ 前序遍历(迭代,用栈) ============

void preorderIterative(TreeNode *root) {
if (!root) return;

TreeNode *stack[100];
int top = -1;
stack[++top] = root;

while (top >= 0) {
TreeNode *node = stack[top--];
printf("%d ", node->val);

// 先压右,再压左(这样左先出栈)
if (node->right) stack[++top] = node->right;
if (node->left) stack[++top] = node->left;
}
}

// ============ 层序遍历(用队列) ============

void levelOrder(TreeNode *root) {
if (!root) return;

TreeNode *queue[1000];
int front = 0, rear = 0;
queue[rear++] = root;

while (front < rear) {
int levelSize = rear - front;
printf("[ ");
for (int i = 0; i < levelSize; i++) {
TreeNode *node = queue[front++];
printf("%d ", node->val);
if (node->left) queue[rear++] = node->left;
if (node->right) queue[rear++] = node->right;
}
printf("]\n");
}
}

// ============ BST 操作 ============

TreeNode* bstInsert(TreeNode *root, int val) {
if (!root) return createTreeNode(val);
if (val < root->val) root->left = bstInsert(root->left, val);
else if (val > root->val) root->right = bstInsert(root->right, val);
return root;
}

TreeNode* bstSearch(TreeNode *root, int val) {
if (!root || root->val == val) return root;
if (val < root->val) return bstSearch(root->left, val);
return bstSearch(root->right, val);
}

// 找最小值(最左节点)
TreeNode* findMin(TreeNode *root) {
while (root->left) root = root->left;
return root;
}

TreeNode* bstDelete(TreeNode *root, int val) {
if (!root) return NULL;

if (val < root->val) {
root->left = bstDelete(root->left, val);
} else if (val > root->val) {
root->right = bstDelete(root->right, val);
} else {
// 找到要删除的节点
if (!root->left) {
TreeNode *right = root->right;
free(root);
return right;
}
if (!root->right) {
TreeNode *left = root->left;
free(root);
return left;
}
// 有两个子节点:用右子树的最小值替代
TreeNode *minNode = findMin(root->right);
root->val = minNode->val;
root->right = bstDelete(root->right, minNode->val);
}
return root;
}

void freeTree(TreeNode *root) {
if (!root) return;
freeTree(root->left);
freeTree(root->right);
free(root);
}

int main() {
/*
* 构建BST:
* 5
* / \
* 3 7
* / \ / \
* 2 4 6 8
*/
TreeNode *root = NULL;
int vals[] = {5, 3, 7, 2, 4, 6, 8};
for (int i = 0; i < 7; i++) {
root = bstInsert(root, vals[i]);
}

printf("前序: "); preorder(root); printf("\n"); // 5 3 2 4 7 6 8
printf("中序: "); inorder(root); printf("\n"); // 2 3 4 5 6 7 8
printf("后序: "); postorder(root); printf("\n"); // 2 4 3 6 8 7 5
printf("层序:\n"); levelOrder(root);

freeTree(root);
return 0;
}

6.3 LeetCode 实战

LeetCode 104. 二叉树的最大深度

C

1
2
3
4
5
6
int maxDepth(TreeNode* root) {
if (!root) return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return (left > right ? left : right) + 1;
}

LeetCode 226. 翻转二叉树

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* 4 4
* / \ / \
* 2 77 2
* / \ / \ / \ / \
* 1 3 6 9 9 6 3 1
*/
TreeNode* invertTree(TreeNode* root) {
if (!root) return NULL;

TreeNode *temp = root->left;
root->left = root->right;
root->right = temp;

invertTree(root->left);
invertTree(root->right);

return root;
}

LeetCode 101. 对称二叉树

C

1
2
3
4
5
6
7
8
9
10
11
12
bool isMirror(TreeNode *left, TreeNode *right) {
if (!left && !right) return true;
if (!left || !right) return false;
return left->val == right->val &&
isMirror(left->left, right->right) &&
isMirror(left->right, right->left);
}

bool isSymmetric(TreeNode* root) {
if (!root) return true;
return isMirror(root->left, root->right);
}

LeetCode 102. 二叉树的层序遍历

C

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
/*
* 返回二维数组
* 3
* / \
* 9 20
* / \
* 15 7
* 输出:[[3],[9,20],[15,7]]
*/
int** levelOrder(TreeNode* root, int* returnSize, int** returnColumnSizes) {
*returnSize = 0;
if (!root) {
*returnColumnSizes = NULL;
return NULL;
}

int **result = (int**)malloc(sizeof(int*) * 2000);
*returnColumnSizes = (int*)malloc(sizeof(int) * 2000);

TreeNode *queue[2000];
int front = 0, rear = 0;
queue[rear++] = root;

while (front < rear) {
int levelSize = rear - front;
result[*returnSize] = (int*)malloc(sizeof(int) * levelSize);
(*returnColumnSizes)[*returnSize] = levelSize;

for (int i = 0; i < levelSize; i++) {
TreeNode *node = queue[front++];
result[*returnSize][i] = node->val;
if (node->left) queue[rear++] = node->left;
if (node->right) queue[rear++] = node->right;
}
(*returnSize)++;
}

return result;
}

LeetCode 110. 平衡二叉树

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 判断是否每个节点的左右子树高度差不超过1
* 返回-1表示不平衡
*/
int checkHeight(TreeNode *root) {
if (!root) return 0;

int left = checkHeight(root->left);
if (left == -1) return -1;

int right = checkHeight(root->right);
if (right == -1) return -1;

if (abs(left - right) > 1) return -1;

return (left > right ? left : right) + 1;
}

bool isBalanced(TreeNode* root) {
return checkHeight(root) != -1;
}

LeetCode 543. 二叉树的直径

C

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 diameter;

int depth(TreeNode *root) {
if (!root) return 0;
int left = depth(root->left);
int right = depth(root->right);

// 更新直径
if (left + right > diameter) {
diameter = left + right;
}

return (left > right ? left : right) + 1;
}

int diameterOfBinaryTree(TreeNode* root) {
diameter = 0;
depth(root);
return diameter;
}

LeetCode 236. 二叉树的最近公共祖先 (LCA)

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 递归思路:
* - 如果当前节点是p或q,返回当前节点
* - 递归查找左右子树
* - 如果左右都找到了,当前节点就是LCA
* - 否则返回非空的那个
*/
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || root == p || root == q) return root;

TreeNode *left = lowestCommonAncestor(root->left, p, q);
TreeNode *right = lowestCommonAncestor(root->right, p, q);

if (left && right) return root; // p和q分别在左右子树
return left ? left : right;
}

LeetCode 98. 验证二叉搜索树

C

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
/*
* BST: 左子树所有值 < 根 < 右子树所有值
* 注意是"所有",不只是直接子节点
*
* 用上下界限制
*/
bool isValidBSTHelper(TreeNode *root, long minVal, long maxVal) {
if (!root) return true;
if (root->val <= minVal || root->val >= maxVal) return false;
return isValidBSTHelper(root->left, minVal, root->val) &&
isValidBSTHelper(root->right, root->val, maxVal);
}

bool isValidBST(TreeNode* root) {
return isValidBSTHelper(root, LONG_MIN, LONG_MAX);
}

/* 方法二:中序遍历应该是严格递增序列 */
bool isValidBST_Inorder(TreeNode* root) {
long prev = LONG_MIN;
TreeNode *stack[10000];
int top = -1;
TreeNode *cur = root;

while (cur || top >= 0) {
while (cur) {
stack[++top] = cur;
cur = cur->left;
}
cur = stack[top--];
if (cur->val <= prev) return false;
prev = cur->val;
cur = cur->right;
}
return true;
}

LeetCode 105. 从前序与中序遍历序列构造二叉树

C

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
/*
* preorder = [3,9,20,15,7] 前序:根左右
* inorder = [9,3,15,20,7] 中序:左根右
*
* 前序第一个元素(3)是根
* 在中序中找到3,左边是左子树(9),右边是右子树(15,20,7)
*/
TreeNode* buildTreeHelper(int *preorder, int preStart, int preEnd,
int *inorder, int inStart, int inEnd,
int *inMap) {
if (preStart > preEnd || inStart > inEnd) return NULL;

TreeNode *root = createTreeNode(preorder[preStart]);
int inRoot = inMap[preorder[preStart] + 3001]; // 偏移处理负数
int numsLeft = inRoot - inStart;

root->left = buildTreeHelper(preorder, preStart + 1, preStart + numsLeft,
inorder, inStart, inRoot - 1, inMap);
root->right = buildTreeHelper(preorder, preStart + numsLeft + 1, preEnd,
inorder, inRoot + 1, inEnd, inMap);
return root;
}

TreeNode* buildTree(int* preorder, int preorderSize,
int* inorder, int inorderSize) {
// 建立中序值到索引的映射
int *inMap = (int*)calloc(6002, sizeof(int));
for (int i = 0; i < inorderSize; i++) {
inMap[inorder[i] + 3001] = i;
}

TreeNode *root = buildTreeHelper(preorder, 0, preorderSize - 1,
inorder, 0, inorderSize - 1, inMap);
free(inMap);
return root;
}

LeetCode 114. 二叉树展开为链表

C

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
/*
* 原地展开为右倾斜的链表(前序遍历顺序)
* 1 1
* / \ \
* 2 5 → 2
* / \ \ \
* 3 4 6 3
* \
* 4
* \
* 5
* \
* 6
*/
void flatten(TreeNode* root) {
TreeNode *cur = root;
while (cur) {
if (cur->left) {
// 找左子树的最右节点
TreeNode *rightmost = cur->left;
while (rightmost->right) {
rightmost = rightmost->right;
}
// 将右子树接到左子树的最右节点
rightmost->right = cur->right;
// 左子树变右子树
cur->right = cur->left;
cur->left = NULL;
}
cur = cur->right;
}
}

LeetCode 124. 二叉树中的最大路径和(困难)

C

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
/*
* 路径可以从任意节点开始到任意节点结束
*
* -10
* / \
* 9 20
* / \
* 15 7
* 最大路径: 15→20→7 = 42
*/
int maxPathSumResult;

int maxGain(TreeNode *root) {
if (!root) return 0;

// 只取正贡献
int leftGain = maxGain(root->left);
if (leftGain < 0) leftGain = 0;

int rightGain = maxGain(root->right);
if (rightGain < 0) rightGain = 0;

// 经过当前节点的最大路径和
int pathSum = root->val + leftGain + rightGain;
if (pathSum > maxPathSumResult) {
maxPathSumResult = pathSum;
}

// 返回给父节点的最大贡献(只能选一边)
return root->val + (leftGain > rightGain ? leftGain : rightGain);
}

int maxPathSum(TreeNode* root) {
maxPathSumResult = INT_MIN;
maxGain(root);
return maxPathSumResult;
}

7. 堆 / 优先队列(Heap / Priority Queue)

7.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
最大堆(父节点 ≥ 子节点):

90
/ \
80 70
/ \ / \
50 60 65 30

数组表示: [90, 80, 70, 50, 60, 65, 30]
索引: 0 1 2 3 4 5 6

关系:
- 父节点: (i-1)/2
- 左子: 2*i+1
- 右子: 2*i+2

7.2 完整实现

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <stdio.h>
#include <stdlib.h>

typedef struct {
int *data;
int size;
int capacity;
int isMinHeap; // 1=最小堆, 0=最大堆
} Heap;

Heap* createHeap(int capacity, int isMinHeap) {
Heap *h = (Heap*)malloc(sizeof(Heap));
h->data = (int*)malloc(sizeof(int) * capacity);
h->size = 0;
h->capacity = capacity;
h->isMinHeap = isMinHeap;
return h;
}

void swap(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}

// 比较函数:最小堆用 <,最大堆用 >
int shouldSwap(Heap *h, int parent, int child) {
if (h->isMinHeap)
return h->data[child] < h->data[parent];
else
return h->data[child] > h->data[parent];
}

// 上浮 O(log n)
void siftUp(Heap *h, int i) {
while (i > 0) {
int parent = (i - 1) / 2;
if (shouldSwap(h, parent, i)) {
swap(&h->data[parent], &h->data[i]);
i = parent;
} else {
break;
}
}
}

// 下沉 O(log n)
void siftDown(Heap *h, int i) {
while (2 * i + 1 < h->size) {
int child = 2 * i + 1; // 左子
// 选更优的子节点
if (child + 1 < h->size && shouldSwap(h, child, child + 1)) {
child++;
}
if (shouldSwap(h, i, child)) {
swap(&h->data[i], &h->data[child]);
i = child;
} else {
break;
}
}
}

// 插入 O(log n)
void heapPush(Heap *h, int val) {
if (h->size == h->capacity) {
h->capacity *= 2;
h->data = (int*)realloc(h->data, sizeof(int) * h->capacity);
}
h->data[h->size] = val;
siftUp(h, h->size);
h->size++;
}

// 弹出堆顶 O(log n)
int heapPop(Heap *h) {
int top = h->data[0];
h->data[0] = h->data[--h->size];
siftDown(h, 0);
return top;
}

// 查看堆顶 O(1)
int heapPeek(Heap *h) {
return h->data[0];
}

void freeHeap(Heap *h) {
free(h->data);
free(h);
}

int main() {
// 最小堆
Heap *minHeap = createHeap(16, 1);

int nums[] = {5, 3, 8, 1, 2, 7, 4, 6};
for (int i = 0; i < 8; i++) {
heapPush(minHeap, nums[i]);
}

printf("最小堆弹出顺序: ");
while (minHeap->size > 0) {
printf("%d ", heapPop(minHeap)); // 1 2 3 4 5 6 7 8
}
printf("\n");

freeHeap(minHeap);
return 0;
}

7.3 LeetCode 实战

LeetCode 215. 数组中的第K个最大元素

C

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
/*
* 示例:[3,2,1,5,6,4], k=2 → 5
*
* 方法一:最小堆,大小为k,堆顶即为第k大
* 方法二:快速选择算法
*/

/* 方法一:最小堆 O(n log k) */
int findKthLargest_heap(int* nums, int numsSize, int k) {
Heap *minHeap = createHeap(k + 1, 1);

for (int i = 0; i < numsSize; i++) {
heapPush(minHeap, nums[i]);
if (minHeap->size > k) {
heapPop(minHeap); // 弹出最小的
}
}

int result = heapPeek(minHeap);
freeHeap(minHeap);
return result;
}

/* 方法二:快速选择 O(n) 平均 */
int partition(int *nums, int left, int right) {
int pivot = nums[right];
int i = left;
for (int j = left; j < right; j++) {
if (nums[j] <= pivot) {
swap(&nums[i], &nums[j]);
i++;
}
}
swap(&nums[i], &nums[right]);
return i;
}

int findKthLargest(int* nums, int numsSize, int k) {
int target = numsSize - k; // 第k大 = 第(n-k)小
int left = 0, right = numsSize - 1;

while (left <= right) {
int pivotIndex = partition(nums, left, right);
if (pivotIndex == target) {
return nums[pivotIndex];
} else if (pivotIndex < target) {
left = pivotIndex + 1;
} else {
right = pivotIndex - 1;
}
}
return -1;
}

LeetCode 347. 前K个高频元素

C

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
/*
* 示例:nums=[1,1,1,2,2,3], k=2 → [1,2]
*
* 思路:统计频率 + 最小堆
*/
typedef struct {
int num;
int freq;
} NumFreq;

// 按频率的最小堆
typedef struct {
NumFreq *data;
int size;
int capacity;
} FreqHeap;

FreqHeap* createFreqHeap(int cap) {
FreqHeap *h = (FreqHeap*)malloc(sizeof(FreqHeap));
h->data = (NumFreq*)malloc(sizeof(NumFreq) * cap);
h->size = 0;
h->capacity = cap;
return h;
}

void freqSiftUp(FreqHeap *h, int i) {
while (i > 0) {
int p = (i-1)/2;
if (h->data[i].freq < h->data[p].freq) {
NumFreq t = h->data[i]; h->data[i] = h->data[p]; h->data[p] = t;
i = p;
} else break;
}
}

void freqSiftDown(FreqHeap *h, int i) {
while (2*i+1 < h->size) {
int c = 2*i+1;
if (c+1 < h->size && h->data[c+1].freq < h->data[c].freq) c++;
if (h->data[c].freq < h->data[i].freq) {
NumFreq t = h->data[i]; h->data[i] = h->data[c]; h->data[c] = t;
i = c;
} else break;
}
}

int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
*returnSize = k;

// 统计频率(用简单排序+遍历或哈希表)
// 这里用排序法简化
qsort(nums, numsSize, sizeof(int),
(int(*)(const void*,const void*))strcmp);
// 实际要用正确的int比较函数

// 简化:用哈希表统计频率
// 然后用最小堆维护前k个

// 完整实现省略,核心思想如上
int *result = (int*)malloc(sizeof(int) * k);
// ...
return result;
}

LeetCode 295. 数据流的中位数(困难)

C

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
/*
* 用两个堆:
* maxHeap 存较小的一半(堆顶是较小一半的最大值)
* minHeap 存较大的一半(堆顶是较大一半的最小值)
*
* 维护:maxHeap.size >= minHeap.size
* maxHeap.size - minHeap.size <= 1
*
* 中位数:
* 奇数个:maxHeap堆顶
* 偶数个:两个堆顶的平均
*/
typedef struct {
Heap *maxHeap; // 左半部分(较小的数)
Heap *minHeap; // 右半部分(较大的数)
} MedianFinder;

MedianFinder* medianFinderCreate() {
MedianFinder *mf = (MedianFinder*)malloc(sizeof(MedianFinder));
mf->maxHeap = createHeap(100, 0); // 最大堆
mf->minHeap = createHeap(100, 1); // 最小堆
return mf;
}

void medianFinderAddNum(MedianFinder* obj, int num) {
// 先加入maxHeap
heapPush(obj->maxHeap, num);
// 将maxHeap的最大值转给minHeap
heapPush(obj->minHeap, heapPop(obj->maxHeap));
// 平衡:保证maxHeap.size >= minHeap.size
if (obj->minHeap->size > obj->maxHeap->size) {
heapPush(obj->maxHeap, heapPop(obj->minHeap));
}
}

double medianFinderFindMedian(MedianFinder* obj) {
if (obj->maxHeap->size > obj->minHeap->size) {
return heapPeek(obj->maxHeap);
}
return (heapPeek(obj->maxHeap) + heapPeek(obj->minHeap)) / 2.0;
}

8. 图(Graph)

8.1 基本概念

text

1
2
3
4
5
6
7
8
9
10
11
12
13
无向图:                  有向图:
0 --- 1 0 → 1
| / | ↑ ↓
| / | 3 ← 2
| / |
2 --- 3

邻接矩阵: 邻接表:
0 1 2 3 0: [1, 2]
0[0 1 1 0] 1: [0, 2, 3]
1[1 0 1 1] 2: [0, 1, 3]
2[1 1 0 1] 3: [1, 2]
3[0 1 1 0]

8.2 图的实现

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_VERTICES 100

// ============ 邻接表实现 ============
typedef struct AdjNode {
int vertex;
int weight;
struct AdjNode *next;
} AdjNode;

typedef struct {
AdjNode *adjList[MAX_VERTICES];
int numVertices;
bool directed;
} Graph;

Graph* createGraph(int vertices, bool directed) {
Graph *g = (Graph*)malloc(sizeof(Graph));
g->numVertices = vertices;
g->directed = directed;
for (int i = 0; i < vertices; i++) {
g->adjList[i] = NULL;
}
return g;
}

void addEdge(Graph *g, int src, int dest, int weight) {
AdjNode *node = (AdjNode*)malloc(sizeof(AdjNode));
node->vertex = dest;
node->weight = weight;
node->next = g->adjList[src];
g->adjList[src] = node;

if (!g->directed) {
node = (AdjNode*)malloc(sizeof(AdjNode));
node->vertex = src;
node->weight = weight;
node->next = g->adjList[dest];
g->adjList[dest] = node;
}
}

// ============ BFS ============
void bfs(Graph *g, int start) {
bool visited[MAX_VERTICES] = {false};
int queue[MAX_VERTICES];
int front = 0, rear = 0;

visited[start] = true;
queue[rear++] = start;

printf("BFS: ");
while (front < rear) {
int v = queue[front++];
printf("%d ", v);

AdjNode *adj = g->adjList[v];
while (adj) {
if (!visited[adj->vertex]) {
visited[adj->vertex] = true;
queue[rear++] = adj->vertex;
}
adj = adj->next;
}
}
printf("\n");
}

// ============ DFS ============
void dfsHelper(Graph *g, int v, bool *visited) {
visited[v] = true;
printf("%d ", v);

AdjNode *adj = g->adjList[v];
while (adj) {
if (!visited[adj->vertex]) {
dfsHelper(g, adj->vertex, visited);
}
adj = adj->next;
}
}

void dfs(Graph *g, int start) {
bool visited[MAX_VERTICES] = {false};
printf("DFS: ");
dfsHelper(g, start, visited);
printf("\n");
}

// DFS(迭代版,用栈)
void dfsIterative(Graph *g, int start) {
bool visited[MAX_VERTICES] = {false};
int stack[MAX_VERTICES];
int top = -1;

stack[++top] = start;
printf("DFS(iterative): ");

while (top >= 0) {
int v = stack[top--];
if (visited[v]) continue;

visited[v] = true;
printf("%d ", v);

AdjNode *adj = g->adjList[v];
while (adj) {
if (!visited[adj->vertex]) {
stack[++top] = adj->vertex;
}
adj = adj->next;
}
}
printf("\n");
}

void freeGraph(Graph *g) {
for (int i = 0; i < g->numVertices; i++) {
AdjNode *cur = g->adjList[i];
while (cur) {
AdjNode *tmp = cur;
cur = cur->next;
free(tmp);
}
}
free(g);
}

int main() {
/*
* 图结构:
* 0 --- 1
* | / |
* | / |
* 2 --- 3
*/
Graph *g = createGraph(4, false);
addEdge(g, 0, 1, 1);
addEdge(g, 0, 2, 1);
addEdge(g, 1, 2, 1);
addEdge(g, 1, 3, 1);
addEdge(g, 2, 3, 1);

bfs(g, 0); // BFS: 0 2 1 3
dfs(g, 0); // DFS: 0 2 3 1

freeGraph(g);
return 0;
}

8.3 LeetCode 实战

LeetCode 200. 岛屿数量

C

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
/*
* '1'为陆地,'0'为水域,计算岛屿数量
*
* 示例:
* 1 1 0 0 0
* 1 1 0 0 0
* 0 0 1 0 0
* 0 0 0 1 1
* 输出:3
*
* DFS:遇到'1'就把整个岛标记为'0'
*/
void dfsIsland(char **grid, int rows, int cols, int r, int c) {
if (r < 0 || r >= rows || c < 0 || c >= cols || grid[r][c] == '0')
return;

grid[r][c] = '0'; // 标记已访问
dfsIsland(grid, rows, cols, r + 1, c);
dfsIsland(grid, rows, cols, r - 1, c);
dfsIsland(grid, rows, cols, r, c + 1);
dfsIsland(grid, rows, cols, r, c - 1);
}

int numIslands(char** grid, int gridSize, int* gridColSize) {
int count = 0;
int rows = gridSize;
int cols = gridColSize[0];

for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == '1') {
count++;
dfsIsland(grid, rows, cols, i, j);
}
}
}
return count;
}

LeetCode 994. 腐烂的橘子(BFS多源)

C

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
/*
* 2=腐烂,1=新鲜,0=空
* 每分钟腐烂橘子的四周会传染
* 返回所有橘子腐烂所需的最少分钟
*
* 多源BFS
*/
int orangesRotting(int** grid, int gridSize, int* gridColSize) {
int rows = gridSize, cols = gridColSize[0];
int queue[rows * cols][2];
int front = 0, rear = 0;
int fresh = 0;

// 把所有腐烂橘子入队,统计新鲜橘子
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == 2) {
queue[rear][0] = i;
queue[rear][1] = j;
rear++;
} else if (grid[i][j] == 1) {
fresh++;
}
}
}

if (fresh == 0) return 0;

int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int minutes = 0;

while (front < rear) {
int size = rear - front;
bool rotted = false;

for (int i = 0; i < size; i++) {
int r = queue[front][0], c = queue[front][1];
front++;

for (int d = 0; d < 4; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& grid[nr][nc] == 1) {
grid[nr][nc] = 2;
queue[rear][0] = nr;
queue[rear][1] = nc;
rear++;
fresh--;
rotted = true;
}
}
}
if (rotted) minutes++;
}

return fresh == 0 ? minutes : -1;
}

LeetCode 207. 课程表(拓扑排序)

C

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
66
67
68
/*
* 判断是否能完成所有课程(检测有向图是否有环)
*
* numCourses=4, prerequisites=[[1,0],[2,0],[3,1],[3,2]]
* 0 → 1 → 3
* 0 → 2 → 3
* 可以完成:0→1→2→3 或 0→2→1→3
*
* BFS拓扑排序(Kahn算法)
*/
bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize,
int* prerequisitesColSize) {
// 建图 + 统计入度
int *inDegree = (int*)calloc(numCourses, sizeof(int));

// 邻接表
int **adj = (int**)malloc(sizeof(int*) * numCourses);
int *adjSize = (int*)calloc(numCourses, sizeof(int));
int *adjCap = (int*)malloc(sizeof(int) * numCourses);
for (int i = 0; i < numCourses; i++) {
adjCap[i] = 4;
adj[i] = (int*)malloc(sizeof(int) * 4);
}

for (int i = 0; i < prerequisitesSize; i++) {
int course = prerequisites[i][0];
int prereq = prerequisites[i][1];

// prereq → course
if (adjSize[prereq] == adjCap[prereq]) {
adjCap[prereq] *= 2;
adj[prereq] = (int*)realloc(adj[prereq], sizeof(int) * adjCap[prereq]);
}
adj[prereq][adjSize[prereq]++] = course;
inDegree[course]++;
}

// BFS:入度为0的节点入队
int *queue = (int*)malloc(sizeof(int) * numCourses);
int front = 0, rear = 0;

for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
queue[rear++] = i;
}
}

int completed = 0;
while (front < rear) {
int course = queue[front++];
completed++;

for (int i = 0; i < adjSize[course]; i++) {
int next = adj[course][i];
inDegree[next]--;
if (inDegree[next] == 0) {
queue[rear++] = next;
}
}
}

// 清理
for (int i = 0; i < numCourses; i++) free(adj[i]);
free(adj); free(adjSize); free(adjCap);
free(inDegree); free(queue);

return completed == numCourses;
}

LeetCode 733. 图像渲染(Flood Fill)

C

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
/*
* 类似画图工具的"油漆桶"功能
*/
void fill(int **image, int rows, int cols, int r, int c,
int oldColor, int newColor) {
if (r < 0 || r >= rows || c < 0 || c >= cols) return;
if (image[r][c] != oldColor) return;

image[r][c] = newColor;
fill(image, rows, cols, r+1, c, oldColor, newColor);
fill(image, rows, cols, r-1, c, oldColor, newColor);
fill(image, rows, cols, r, c+1, oldColor, newColor);
fill(image, rows, cols, r, c-1, oldColor, newColor);
}

int** floodFill(int** image, int imageSize, int* imageColSize,
int sr, int sc, int color, int* returnSize,
int** returnColumnSizes) {
*returnSize = imageSize;
*returnColumnSizes = imageColSize;

int oldColor = image[sr][sc];
if (oldColor != color) {
fill(image, imageSize, imageColSize[0], sr, sc, oldColor, color);
}
return image;
}

第二部分:经典算法


9. 排序算法

9.1 总览

text

1
2
3
4
5
6
7
8
排序算法      平均时间    最坏时间    空间    稳定性
─────────────────────────────────────────────────
冒泡排序 O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(1) 不稳定
插入排序 O(n²) O(n²) O(1) 稳定
归并排序 O(nlogn) O(nlogn) O(n) 稳定
快速排序 O(nlogn) O(n²) O(logn) 不稳定
堆排序 O(nlogn) O(nlogn) O(1) 不稳定

9.2 实现

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void swap(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}

void printArr(int *arr, int n) {
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
}

// ============ 冒泡排序 ============
void bubbleSort(int *arr, int n) {
for (int i = 0; i < n - 1; i++) {
bool swapped = false;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
swap(&arr[j], &arr[j+1]);
swapped = true;
}
}
if (!swapped) break; // 已经有序
}
}

// ============ 选择排序 ============
void selectionSort(int *arr, int n) {
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) minIdx = j;
}
swap(&arr[i], &arr[minIdx]);
}
}

// ============ 插入排序 ============
void insertionSort(int *arr, int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}

// ============ 归并排序 ============
void mergeSortHelper(int *arr, int *temp, int left, int right) {
if (left >= right) return;

int mid = left + (right - left) / 2;
mergeSortHelper(arr, temp, left, mid);
mergeSortHelper(arr, temp, mid + 1, right);

// 合并
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) temp[k++] = arr[i++];
else temp[k++] = arr[j++];
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];

memcpy(arr + left, temp + left, sizeof(int) * (right - left + 1));
}

void mergeSort(int *arr, int n) {
int *temp = (int*)malloc(sizeof(int) * n);
mergeSortHelper(arr, temp, 0, n - 1);
free(temp);
}

// ============ 快速排序 ============
int partitionQS(int *arr, int left, int right) {
// 随机选择pivot避免最坏情况
int randIdx = left + rand() % (right - left + 1);
swap(&arr[randIdx], &arr[right]);

int pivot = arr[right];
int i = left;
for (int j = left; j < right; j++) {
if (arr[j] < pivot) {
swap(&arr[i], &arr[j]);
i++;
}
}
swap(&arr[i], &arr[right]);
return i;
}

void quickSortHelper(int *arr, int left, int right) {
if (left >= right) return;
int pivot = partitionQS(arr, left, right);
quickSortHelper(arr, left, pivot - 1);
quickSortHelper(arr, pivot + 1, right);
}

void quickSort(int *arr, int n) {
quickSortHelper(arr, 0, n - 1);
}

// ============ 堆排序 ============
void heapify(int *arr, int n, int i) {
int largest = i;
int left = 2*i + 1, right = 2*i + 2;

if (left < n && arr[left] > arr[largest]) largest = left;
if (right < n && arr[right] > arr[largest]) largest = right;

if (largest != i) {
swap(&arr[i], &arr[largest]);
heapify(arr, n, largest);
}
}

void heapSort(int *arr, int n) {
// 建堆
for (int i = n/2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 逐步取出最大值
for (int i = n - 1; i > 0; i--) {
swap(&arr[0], &arr[i]);
heapify(arr, i, 0);
}
}

int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
int temp[7];

memcpy(temp, arr, sizeof(arr));
quickSort(temp, n);
printf("快速排序: "); printArr(temp, n);

memcpy(temp, arr, sizeof(arr));
mergeSort(temp, n);
printf("归并排序: "); printArr(temp, n);

memcpy(temp, arr, sizeof(arr));
heapSort(temp, n);
printf("堆排序: "); printArr(temp, n);

return 0;
}

9.3 LeetCode 实战

LeetCode 912. 排序数组

C

1
2
3
4
5
6
7
/* 直接使用上面的快速排序或归并排序 */
int* sortArray(int* nums, int numsSize, int* returnSize) {
*returnSize = numsSize;
// 使用归并排序(稳定,保证O(nlogn))
mergeSort(nums, numsSize);
return nums;
}

LeetCode 56. 合并区间

C

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
/*
* 示例:[[1,3],[2,6],[8,10],[15,18]] → [[1,6],[8,10],[15,18]]
*
* 先按起点排序,然后逐一合并
*/
int cmpIntervals(const void *a, const void *b) {
int *ia = *(int**)a, *ib = *(int**)b;
return ia[0] - ib[0];
}

int** mergeIntervals(int** intervals, int intervalsSize,
int* intervalsColSize, int* returnSize,
int** returnColumnSizes) {
if (intervalsSize == 0) {
*returnSize = 0;
return NULL;
}

qsort(intervals, intervalsSize, sizeof(int*), cmpIntervals);

int **result = (int**)malloc(sizeof(int*) * intervalsSize);
*returnColumnSizes = (int*)malloc(sizeof(int) * intervalsSize);
*returnSize = 0;

result[0] = (int*)malloc(sizeof(int) * 2);
result[0][0] = intervals[0][0];
result[0][1] = intervals[0][1];
(*returnColumnSizes)[0] = 2;
*returnSize = 1;

for (int i = 1; i < intervalsSize; i++) {
if (intervals[i][0] <= result[*returnSize - 1][1]) {
// 重叠,合并
if (intervals[i][1] > result[*returnSize - 1][1]) {
result[*returnSize - 1][1] = intervals[i][1];
}
} else {
// 不重叠,新增
result[*returnSize] = (int*)malloc(sizeof(int) * 2);
result[*returnSize][0] = intervals[i][0];
result[*returnSize][1] = intervals[i][1];
(*returnColumnSizes)[*returnSize] = 2;
(*returnSize)++;
}
}

return result;
}

LeetCode 75. 颜色分类(荷兰国旗问题)

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 只含0,1,2,原地排序
* 三指针:p0, p1(current), p2
*/
void sortColors(int* nums, int numsSize) {
int p0 = 0, cur = 0, p2 = numsSize - 1;

while (cur <= p2) {
if (nums[cur] == 0) {
swap(&nums[cur], &nums[p0]);
p0++;
cur++;
} else if (nums[cur] == 2) {
swap(&nums[cur], &nums[p2]);
p2--;
// 注意cur不前进,因为换来的可能是0
} else {
cur++;
}
}
}

10. 二分查找(Binary Search)

10.1 基本模板

C

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
/*
* 标准二分查找
* 在有序数组中查找 target
*
* 示例:[1, 3, 5, 7, 9, 11] target = 7
*
* 步骤1: left=0, right=5, mid=2 → arr[2]=5 < 7 → left=3
* 步骤2: left=3, right=5, mid=4 → arr[4]=9 > 7 → right=3
* 步骤3: left=3, right=3, mid=3 → arr[3]=7 == 7 → 找到!
*/

// 模板一:精确查找
int binarySearch(int *arr, int n, int target) {
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防溢出
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}

// 模板二:查找左边界(第一个 >= target 的位置)
int lowerBound(int *arr, int n, int target) {
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < target) left = mid + 1;
else right = mid;
}
return left;
}

// 模板三:查找右边界(最后一个 <= target 的位置)
int upperBound(int *arr, int n, int target) {
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target) left = mid + 1;
else right = mid;
}
return left - 1;
}

10.2 LeetCode 实战

LeetCode 704. 二分查找

C

1
2
3
4
5
6
7
8
9
10
int search(int* nums, int numsSize, int target) {
int left = 0, right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}

LeetCode 33. 搜索旋转排序数组

C

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
/*
* 示例:[4,5,6,7,0,1,2], target=0 → 输出4
*
* 关键:旋转后至少有一半是有序的
*/
int searchRotated(int* nums, int numsSize, int target) {
int left = 0, right = numsSize - 1;

while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;

// 左半边有序
if (nums[left] <= nums[mid]) {
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// 右半边有序
else {
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}

LeetCode 153. 寻找旋转排序数组中的最小值

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* [3,4,5,1,2] → 1
* [4,5,6,7,0,1,2] → 0
*/
int findMin(int* nums, int numsSize) {
int left = 0, right = numsSize - 1;

while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
left = mid + 1; // 最小值在右边
} else {
right = mid; // 最小值在左边(含mid
}
}
return nums[left];
}

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置

C

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
/*
* 示例:[5,7,7,8,8,10], target=8 → [3,4]
* [5,7,7,8,8,10], target=6 → [-1,-1]
*/
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
*returnSize = 2;
int *result = (int*)malloc(sizeof(int) * 2);
result[0] = result[1] = -1;

// 查找左边界
int left = 0, right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) right = mid - 1;
else left = mid + 1;
}
if (left >= numsSize || nums[left] != target) return result;
result[0] = left;

// 查找右边界
right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) left = mid + 1;
else right = mid - 1;
}
result[1] = right;

return result;
}

LeetCode 4. 寻找两个正序数组的中位数(困难)

C

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
/*
* 要求 O(log(m+n))
*
* 思路:在较短数组上二分,找到分割点使得
* 左半部分总数 = 右半部分总数
* 左半最大 <= 右半最小
*/
double findMedianSortedArrays(int* nums1, int nums1Size,
int* nums2, int nums2Size) {
// 确保 nums1 是较短的
if (nums1Size > nums2Size) {
return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
}

int m = nums1Size, n = nums2Size;
int left = 0, right = m;

while (left <= right) {
int i = (left + right) / 2; // nums1 的分割点
int j = (m + n + 1) / 2 - i; // nums2 的分割点

int maxLeft1 = (i == 0) ? INT_MIN : nums1[i-1];
int minRight1 = (i == m) ? INT_MAX : nums1[i];
int maxLeft2 = (j == 0) ? INT_MIN : nums2[j-1];
int minRight2 = (j == n) ? INT_MAX : nums2[j];

if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
// 找到正确的分割
int maxLeft = maxLeft1 > maxLeft2 ? maxLeft1 : maxLeft2;
int minRight = minRight1 < minRight2 ? minRight1 : minRight2;

if ((m + n) % 2 == 1) return maxLeft;
return (maxLeft + minRight) / 2.0;
}
else if (maxLeft1 > minRight2) {
right = i - 1;
}
else {
left = i + 1;
}
}
return 0.0;
}

11. 双指针 & 滑动窗口

11.1 LeetCode 实战

LeetCode 11. 盛最多水的容器

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* height = [1,8,6,2,5,4,8,3,7]
* 最大面积 = min(8,7) * (8-1) = 49
*
* 双指针从两端向中间移动
* 每次移动较短的那一边(因为面积受短边限制)
*/
int maxArea(int* height, int heightSize) {
int left = 0, right = heightSize - 1;
int maxWater = 0;

while (left < right) {
int h = height[left] < height[right] ? height[left] : height[right];
int area = h * (right - left);
if (area > maxWater) maxWater = area;

if (height[left] < height[right]) left++;
else right--;
}
return maxWater;
}

LeetCode 15. 三数之和

C

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
/*
* 找出所有不重复的三元组使得 a+b+c=0
* 示例:[-1,0,1,2,-1,-4] → [[-1,-1,2],[-1,0,1]]
*
* 排序 + 固定一个 + 双指针
*/
int cmpInt(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}

int** threeSum(int* nums, int numsSize, int* returnSize,
int** returnColumnSizes) {
qsort(nums, numsSize, sizeof(int), cmpInt);

int **result = (int**)malloc(sizeof(int*) * numsSize * numsSize);
*returnColumnSizes = (int*)malloc(sizeof(int) * numsSize * numsSize);
*returnSize = 0;

for (int i = 0; i < numsSize - 2; i++) {
if (i > 0 && nums[i] == nums[i-1]) continue; // 跳过重复
if (nums[i] > 0) break; // 最小值>0不可能

int left = i + 1, right = numsSize - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result[*returnSize] = (int*)malloc(sizeof(int) * 3);
result[*returnSize][0] = nums[i];
result[*returnSize][1] = nums[left];
result[*returnSize][2] = nums[right];
(*returnColumnSizes)[*returnSize] = 3;
(*returnSize)++;

while (left < right && nums[left] == nums[left+1]) left++;
while (left < right && nums[right] == nums[right-1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}

return result;
}

LeetCode 42. 接雨水(困难)

C

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
/*
* height = [0,1,0,2,1,0,1,3,2,1,2,1]
* 输出:6
*
* 方法一:双指针
* 每个位置能接的水 = min(左边最高, 右边最高) - 自身高度
*/
int trap(int* height, int heightSize) {
if (heightSize < 3) return 0;

int left = 0, right = heightSize - 1;
int leftMax = 0, rightMax = 0;
int water = 0;

while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
water += leftMax - height[left];
}
left++;
} else {
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
water += rightMax - height[right];
}
right--;
}
}
return water;
}

/* 方法二:单调栈 */
int trap_stack(int* height, int heightSize) {
int *stack = (int*)malloc(sizeof(int) * heightSize);
int top = -1;
int water = 0;

for (int i = 0; i < heightSize; i++) {
while (top >= 0 && height[i] > height[stack[top]]) {
int bottom = height[stack[top--]];
if (top < 0) break;
int w = i - stack[top] - 1;
int h = (height[i] < height[stack[top]] ? height[i] : height[stack[top]]) - bottom;
water += w * h;
}
stack[++top] = i;
}

free(stack);
return water;
}

LeetCode 3. 无重复字符的最长子串(滑动窗口)

C

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
/*
* 示例:"abcabcbb"3 ("abc")
* "bbbbb"1 ("b")
* "pwwkew"3 ("wke")
*
* 滑动窗口 + 哈希表记录字符位置
*/
int lengthOfLongestSubstring(char* s) {
int charIndex[128]; // ASCII字符到最近位置的映射
memset(charIndex, -1, sizeof(charIndex));

int maxLen = 0;
int left = 0;

for (int right = 0; s[right]; right++) {
// 如果字符在窗口内出现过,移动left
if (charIndex[(unsigned char)s[right]] >= left) {
left = charIndex[(unsigned char)s[right]] + 1;
}
charIndex[(unsigned char)s[right]] = right;

int len = right - left + 1;
if (len > maxLen) maxLen = len;
}
return maxLen;
}

LeetCode 438. 找到字符串中所有字母异位词

C

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
/*
* s = "cbaebabacd", p = "abc"
* 输出:[0, 6] ("cba" 和 "bac")
*
* 固定窗口大小为 p 的长度
*/
int* findAnagrams(char* s, char* p, int* returnSize) {
int sLen = strlen(s), pLen = strlen(p);
*returnSize = 0;
int *result = (int*)malloc(sizeof(int) * sLen);

if (sLen < pLen) return result;

int pCount[26] = {0}, sCount[26] = {0};
for (int i = 0; i < pLen; i++) {
pCount[p[i] - 'a']++;
sCount[s[i] - 'a']++;
}

if (memcmp(pCount, sCount, sizeof(pCount)) == 0) {
result[(*returnSize)++] = 0;
}

for (int i = pLen; i < sLen; i++) {
sCount[s[i] - 'a']++;
sCount[s[i - pLen] - 'a']--;

if (memcmp(pCount, sCount, sizeof(pCount)) == 0) {
result[(*returnSize)++] = i - pLen + 1;
}
}
return result;
}

LeetCode 76. 最小覆盖子串(困难)

C

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
/*
* s = "ADOBECODEBANC", t = "ABC"
* 输出:"BANC"
*
* 滑动窗口:先扩大右边找到有效窗口,再收缩左边优化
*/
char* minWindow(char* s, char* t) {
int sLen = strlen(s), tLen = strlen(t);
if (sLen < tLen) return "";

int tCount[128] = {0};
for (int i = 0; i < tLen; i++) tCount[(unsigned char)t[i]]++;

int required = 0;
for (int i = 0; i < 128; i++) {
if (tCount[i] > 0) required++;
}

int windowCount[128] = {0};
int formed = 0;
int left = 0;
int minLen = INT_MAX, minLeft = 0;

for (int right = 0; right < sLen; right++) {
char c = s[right];
windowCount[(unsigned char)c]++;

if (tCount[(unsigned char)c] > 0 &&
windowCount[(unsigned char)c] == tCount[(unsigned char)c]) {
formed++;
}

// 收缩左边
while (formed == required) {
if (right - left + 1 < minLen) {
minLen = right - left + 1;
minLeft = left;
}

char d = s[left];
windowCount[(unsigned char)d]--;
if (tCount[(unsigned char)d] > 0 &&
windowCount[(unsigned char)d] < tCount[(unsigned char)d]) {
formed--;
}
left++;
}
}

if (minLen == INT_MAX) return "";

char *result = (char*)malloc(minLen + 1);
strncpy(result, s + minLeft, minLen);
result[minLen] = '\0';
return result;
}

12. 动态规划(Dynamic Programming)

12.1 核心思想

text

1
2
3
4
5
6
动态规划解题步骤:
1. 定义状态(dp数组的含义)
2. 找状态转移方程
3. 确定初始条件
4. 确定遍历顺序
5. 推导验证

12.2 LeetCode 实战

LeetCode 70. 爬楼梯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 每次爬1或2个台阶,到第n阶有多少种方法?
*
* dp[i] = 到第i阶的方法数
* dp[i] = dp[i-1] + dp[i-2] (从i-1走1步 或 从i-2走2步)
*
* n=1: 1
* n=2: 2
* n=3: 3 (1+1+1, 1+2, 2+1)
* n=4: 5
*/
int climbStairs(int n) {
if (n <= 2) return n;
int prev2 = 1, prev1 = 2;
for (int i = 3; i <= n; i++) {
int cur = prev1 + prev2;
prev2 = prev1;
prev1 = cur;
}
return prev1;
}

LeetCode 198. 打家劫舍

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 不能偷相邻的房屋,求最大金额
*
* dp[i] = 到第i间能偷到的最大金额
* dp[i] = max(dp[i-1], dp[i-2] + nums[i])
* (不偷i) (偷i)
*
* 示例:[2,7,9,3,1]
* dp: [2,7,11,11,12] → 12 (偷0,2,4号)
*/
int rob(int* nums, int numsSize) {
if (numsSize == 0) return 0;
if (numsSize == 1) return nums[0];

int prev2 = nums[0];
int prev1 = nums[0] > nums[1] ? nums[0] : nums[1];

for (int i = 2; i < numsSize; i++) {
int cur = prev1 > (prev2 + nums[i]) ? prev1 : (prev2 + nums[i]);
prev2 = prev1;
prev1 = cur;
}
return prev1;
}

LeetCode 322. 零钱兑换

C

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
/*
* 给定面值和总金额,求最少硬币数
*
* coins = [1,2,5], amount = 11
* dp[i] = 凑成金额i的最少硬币数
* dp[i] = min(dp[i - coin] + 1) for each coin
*
* dp[0]=0, dp[1]=1, dp[2]=1, dp[3]=2, dp[4]=2, dp[5]=1,
* dp[6]=2, dp[7]=2, dp[8]=3, dp[9]=3, dp[10]=2, dp[11]=3
*/
int coinChange(int* coins, int coinsSize, int amount) {
int *dp = (int*)malloc(sizeof(int) * (amount + 1));

for (int i = 0; i <= amount; i++) dp[i] = amount + 1;
dp[0] = 0;

for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coinsSize; j++) {
if (coins[j] <= i && dp[i - coins[j]] + 1 < dp[i]) {
dp[i] = dp[i - coins[j]] + 1;
}
}
}

int result = dp[amount] > amount ? -1 : dp[amount];
free(dp);
return result;
}

LeetCode 300. 最长递增子序列 (LIS)

C

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
/*
* 示例:[10,9,2,5,3,7,101,18] → 4 (子序列 [2,3,7,101])
*
* 方法一:DP O(n²)
* dp[i] = 以nums[i]结尾的最长递增子序列长度
*/
int lengthOfLIS_dp(int* nums, int numsSize) {
int *dp = (int*)malloc(sizeof(int) * numsSize);
int maxLen = 1;

for (int i = 0; i < numsSize; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
}
}
if (dp[i] > maxLen) maxLen = dp[i];
}

free(dp);
return maxLen;
}

/*
* 方法二:贪心 + 二分 O(n log n)
* tails[i] = 长度为i+1的递增子序列的最小尾元素
*/
int lengthOfLIS(int* nums, int numsSize) {
int *tails = (int*)malloc(sizeof(int) * numsSize);
int len = 0;

for (int i = 0; i < numsSize; i++) {
// 二分查找 tails 中第一个 >= nums[i] 的位置
int left = 0, right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (tails[mid] < nums[i]) left = mid + 1;
else right = mid;
}
tails[left] = nums[i];
if (left == len) len++;
}

free(tails);
return len;
}

LeetCode 1143. 最长公共子序列 (LCS)

C

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
/*
* text1 = "abcde", text2 = "ace" → 3 ("ace")
*
* dp[i][j] = text1[0..i-1] 和 text2[0..j-1] 的 LCS 长度
*
* 如果 text1[i-1] == text2[j-1]: dp[i][j] = dp[i-1][j-1] + 1
* 否则: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
*/
int longestCommonSubsequence(char* text1, char* text2) {
int m = strlen(text1), n = strlen(text2);

// 优化:只用两行
int *prev = (int*)calloc(n + 1, sizeof(int));
int *curr = (int*)calloc(n + 1, sizeof(int));

for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1[i-1] == text2[j-1]) {
curr[j] = prev[j-1] + 1;
} else {
curr[j] = prev[j] > curr[j-1] ? prev[j] : curr[j-1];
}
}
int *temp = prev;
prev = curr;
curr = temp;
memset(curr, 0, sizeof(int) * (n + 1));
}

int result = prev[n];
free(prev);
free(curr);
return result;
}

LeetCode 72. 编辑距离

C

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
/*
* word1 = "horse", word2 = "ros" → 3
* 操作:插入、删除、替换
*
* dp[i][j] = word1[0..i-1] 转换为 word2[0..j-1] 的最少操作
*
* 如果相等: dp[i][j] = dp[i-1][j-1]
* 否则: dp[i][j] = 1 + min(dp[i-1][j], // 删除
* dp[i][j-1], // 插入
* dp[i-1][j-1]) // 替换
*/
int minDistance(char* word1, char* word2) {
int m = strlen(word1), n = strlen(word2);
int **dp = (int**)malloc(sizeof(int*) * (m + 1));
for (int i = 0; i <= m; i++) {
dp[i] = (int*)malloc(sizeof(int) * (n + 1));
}

// 初始化
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;

for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
int a = dp[i-1][j], b = dp[i][j-1], c = dp[i-1][j-1];
int minVal = a < b ? a : b;
minVal = minVal < c ? minVal : c;
dp[i][j] = 1 + minVal;
}
}
}

int result = dp[m][n];
for (int i = 0; i <= m; i++) free(dp[i]);
free(dp);
return result;
}

LeetCode 139. 单词拆分

C

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
/*
* s = "leetcode", wordDict = ["leet","code"] → true
*
* dp[i] = s[0..i-1] 能否被拆分
* dp[i] = dp[j] && s[j..i-1] in dict (for some j)
*/
bool wordBreak(char* s, char** wordDict, int wordDictSize) {
int n = strlen(s);
bool *dp = (bool*)calloc(n + 1, sizeof(bool));
dp[0] = true;

for (int i = 1; i <= n; i++) {
for (int j = 0; j < wordDictSize; j++) {
int wLen = strlen(wordDict[j]);
if (i >= wLen && dp[i - wLen]) {
if (strncmp(s + i - wLen, wordDict[j], wLen) == 0) {
dp[i] = true;
break;
}
}
}
}

bool result = dp[n];
free(dp);
return result;
}

LeetCode 152. 乘积最大子数组

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 示例:[2,3,-2,4] → 6 ([2,3])
* [-2,0,-1] → 0
*
* 需要同时维护最大值和最小值(负负得正)
*/
int maxProduct(int* nums, int numsSize) {
int maxProd = nums[0], minProd = nums[0], result = nums[0];

for (int i = 1; i < numsSize; i++) {
if (nums[i] < 0) {
// 负数会让最大变最小,最小变最大
int temp = maxProd;
maxProd = minProd;
minProd = temp;
}

maxProd = nums[i] > maxProd * nums[i] ? nums[i] : maxProd * nums[i];
minProd = nums[i] < minProd * nums[i] ? nums[i] : minProd * nums[i];

if (maxProd > result) result = maxProd;
}
return result;
}

LeetCode 416. 分割等和子集(0-1背包)

C

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
/*
* 能否将数组分为和相等的两个子集
* [1,5,11,5] → true (1+5+5=11)
*
* 转化为0-1背包:能否从数组中选出若干元素和为 sum/2
*/
bool canPartition(int* nums, int numsSize) {
int sum = 0;
for (int i = 0; i < numsSize; i++) sum += nums[i];
if (sum % 2 != 0) return false;

int target = sum / 2;
bool *dp = (bool*)calloc(target + 1, sizeof(bool));
dp[0] = true;

for (int i = 0; i < numsSize; i++) {
// 从后往前遍历(0-1背包的优化)
for (int j = target; j >= nums[i]; j--) {
dp[j] = dp[j] || dp[j - nums[i]];
}
}

bool result = dp[target];
free(dp);
return result;
}

LeetCode 62. 不同路径

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* m×n 网格,从左上到右下,每次只能向下或向右
*
* dp[i][j] = 到达(i,j)的路径数
* dp[i][j] = dp[i-1][j] + dp[i][j-1]
*/
int uniquePaths(int m, int n) {
int *dp = (int*)malloc(sizeof(int) * n);
for (int j = 0; j < n; j++) dp[j] = 1;

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[j] += dp[j-1];
}
}

int result = dp[n-1];
free(dp);
return result;
}

LeetCode 64. 最小路径和

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* 网格中每个位置有非负整数,找从左上到右下的最小路径和
*/
int minPathSum(int** grid, int gridSize, int* gridColSize) {
int m = gridSize, n = gridColSize[0];

// 原地修改
for (int i = 1; i < m; i++) grid[i][0] += grid[i-1][0];
for (int j = 1; j < n; j++) grid[0][j] += grid[0][j-1];

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
int fromUp = grid[i-1][j], fromLeft = grid[i][j-1];
grid[i][j] += fromUp < fromLeft ? fromUp : fromLeft;
}
}

return grid[m-1][n-1];
}

13. 回溯算法(Backtracking)

13.1 核心模板

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 回溯 = DFS + 撤销选择
*
* void backtrack(路径, 选择列表) {
* if (满足结束条件) {
* 结果.add(路径);
* return;
* }
* for (选择 in 选择列表) {
* 做选择;
* backtrack(路径, 选择列表);
* 撤销选择;
* }
* }
*/

LeetCode 46. 全排列

C

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
/*
* [1,2,3] → [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
*/
void permuteHelper(int *nums, int n, int *path, bool *used,
int depth, int **result, int *returnSize) {
if (depth == n) {
result[*returnSize] = (int*)malloc(sizeof(int) * n);
memcpy(result[*returnSize], path, sizeof(int) * n);
(*returnSize)++;
return;
}

for (int i = 0; i < n; i++) {
if (used[i]) continue;

path[depth] = nums[i]; // 做选择
used[i] = true;

permuteHelper(nums, n, path, used, depth + 1, result, returnSize);

used[i] = false; // 撤销选择
}
}

int** permute(int* nums, int numsSize, int* returnSize,
int** returnColumnSizes) {
int total = 1;
for (int i = 1; i <= numsSize; i++) total *= i;

int **result = (int**)malloc(sizeof(int*) * total);
*returnColumnSizes = (int*)malloc(sizeof(int) * total);
*returnSize = 0;

int *path = (int*)malloc(sizeof(int) * numsSize);
bool *used = (bool*)calloc(numsSize, sizeof(bool));

permuteHelper(nums, numsSize, path, used, 0, result, returnSize);

for (int i = 0; i < *returnSize; i++) {
(*returnColumnSizes)[i] = numsSize;
}

free(path);
free(used);
return result;
}

LeetCode 78. 子集

C

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
/*
* [1,2,3] → [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
*/
void subsetsHelper(int *nums, int n, int start, int *path, int pathLen,
int **result, int *returnSize, int **returnColumnSizes) {
// 每个状态都是一个子集
result[*returnSize] = (int*)malloc(sizeof(int) * pathLen);
memcpy(result[*returnSize], path, sizeof(int) * pathLen);
(*returnColumnSizes)[*returnSize] = pathLen;
(*returnSize)++;

for (int i = start; i < n; i++) {
path[pathLen] = nums[i];
subsetsHelper(nums, n, i + 1, path, pathLen + 1,
result, returnSize, returnColumnSizes);
}
}

int** subsets(int* nums, int numsSize, int* returnSize,
int** returnColumnSizes) {
int total = 1 << numsSize; // 2^n
int **result = (int**)malloc(sizeof(int*) * total);
*returnColumnSizes = (int*)malloc(sizeof(int) * total);
*returnSize = 0;

int *path = (int*)malloc(sizeof(int) * numsSize);
subsetsHelper(nums, numsSize, 0, path, 0,
result, returnSize, returnColumnSizes);

free(path);
return result;
}

LeetCode 39. 组合总和

C

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
/*
* candidates = [2,3,6,7], target = 7
* 输出:[[2,2,3],[7]] (元素可以重复使用)
*/
void combinationHelper(int *candidates, int n, int target, int start,
int *path, int pathLen, int **result,
int *returnSize, int **returnColumnSizes) {
if (target == 0) {
result[*returnSize] = (int*)malloc(sizeof(int) * pathLen);
memcpy(result[*returnSize], path, sizeof(int) * pathLen);
(*returnColumnSizes)[*returnSize] = pathLen;
(*returnSize)++;
return;
}

for (int i = start; i < n; i++) {
if (candidates[i] > target) continue;

path[pathLen] = candidates[i];
// i 不是 i+1,因为可以重复使用
combinationHelper(candidates, n, target - candidates[i], i,
path, pathLen + 1, result, returnSize,
returnColumnSizes);
}
}

int** combinationSum(int* candidates, int candidatesSize, int target,
int* returnSize, int** returnColumnSizes) {
int **result = (int**)malloc(sizeof(int*) * 200);
*returnColumnSizes = (int*)malloc(sizeof(int) * 200);
*returnSize = 0;

int *path = (int*)malloc(sizeof(int) * target);
combinationHelper(candidates, candidatesSize, target, 0,
path, 0, result, returnSize, returnColumnSizes);

free(path);
return result;
}

LeetCode 17. 电话号码的字母组合

C

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
/*
* digits = "23" → ["ad","ae","af","bd","be","bf","cd","ce","cf"]
*/
const char *phoneMap[] = {
"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
};

void letterHelper(char *digits, int index, char *path, int pathLen,
char **result, int *returnSize) {
if (!digits[index]) {
result[*returnSize] = (char*)malloc(pathLen + 1);
memcpy(result[*returnSize], path, pathLen);
result[*returnSize][pathLen] = '\0';
(*returnSize)++;
return;
}

const char *letters = phoneMap[digits[index] - '0'];
for (int i = 0; letters[i]; i++) {
path[pathLen] = letters[i];
letterHelper(digits, index + 1, path, pathLen + 1, result, returnSize);
}
}

char** letterCombinations(char* digits, int* returnSize) {
*returnSize = 0;
if (!digits || !digits[0]) return NULL;

char **result = (char**)malloc(sizeof(char*) * 1000);
char *path = (char*)malloc(strlen(digits) + 1);

letterHelper(digits, 0, path, 0, result, returnSize);

free(path);
return result;
}

LeetCode 22. 括号生成

C

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
/*
* n=3 → ["((()))","(()())","(())()","()(())","()()()"]
*/
void generateHelper(int n, int open, int close, char *path, int len,
char **result, int *returnSize) {
if (len == 2 * n) {
result[*returnSize] = (char*)malloc(2 * n + 1);
memcpy(result[*returnSize], path, 2 * n);
result[*returnSize][2 * n] = '\0';
(*returnSize)++;
return;
}

if (open < n) {
path[len] = '(';
generateHelper(n, open + 1, close, path, len + 1, result, returnSize);
}
if (close < open) {
path[len] = ')';
generateHelper(n, open, close + 1, path, len + 1, result, returnSize);
}
}

char** generateParenthesis(int n, int* returnSize) {
*returnSize = 0;
char **result = (char**)malloc(sizeof(char*) * 5000);
char *path = (char*)malloc(2 * n + 1);

generateHelper(n, 0, 0, path, 0, result, returnSize);

free(path);
return result;
}

LeetCode 79. 单词搜索

C

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
/*
* 在二维网格中搜索单词,可以上下左右移动,每个格子只能用一次
*/
bool dfsWord(char **board, int rows, int cols, int r, int c,
char *word, int index) {
if (!word[index]) return true;
if (r < 0 || r >= rows || c < 0 || c >= cols) return false;
if (board[r][c] != word[index]) return false;

char temp = board[r][c];
board[r][c] = '#'; // 标记已访问

bool found = dfsWord(board, rows, cols, r+1, c, word, index+1) ||
dfsWord(board, rows, cols, r-1, c, word, index+1) ||
dfsWord(board, rows, cols, r, c+1, word, index+1) ||
dfsWord(board, rows, cols, r, c-1, word, index+1);

board[r][c] = temp; // 恢复
return found;
}

bool exist(char** board, int boardSize, int* boardColSize, char* word) {
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < boardColSize[0]; j++) {
if (dfsWord(board, boardSize, boardColSize[0], i, j, word, 0)) {
return true;
}
}
}
return false;
}

LeetCode 51. N皇后(困难)

C

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
/*
* N×N棋盘放N个皇后,互不攻击
*
* n=4 的解:
* . Q . . . . Q .
* . . . Q Q . . .
* Q . . . . . . Q
* . . Q . . Q . .
*/
bool isValid51(int *queens, int row, int col, int n) {
for (int i = 0; i < row; i++) {
if (queens[i] == col) return false; // 同列
if (abs(queens[i] - col) == abs(i - row)) return false; // 对角线
}
return true;
}

void solveNQueensHelper(int n, int row, int *queens,
char ***result, int *returnSize) {
if (row == n) {
result[*returnSize] = (char**)malloc(sizeof(char*) * n);
for (int i = 0; i < n; i++) {
result[*returnSize][i] = (char*)malloc(n + 1);
for (int j = 0; j < n; j++) {
result[*returnSize][i][j] = (queens[i] == j) ? 'Q' : '.';
}
result[*returnSize][i][n] = '\0';
}
(*returnSize)++;
return;
}

for (int col = 0; col < n; col++) {
if (isValid51(queens, row, col, n)) {
queens[row] = col;
solveNQueensHelper(n, row + 1, queens, result, returnSize);
}
}
}

char*** solveNQueens(int n, int* returnSize, int** returnColumnSizes) {
char ***result = (char***)malloc(sizeof(char**) * 100);
*returnSize = 0;

int *queens = (int*)malloc(sizeof(int) * n);
solveNQueensHelper(n, 0, queens, result, returnSize);

*returnColumnSizes = (int*)malloc(sizeof(int) * (*returnSize));
for (int i = 0; i < *returnSize; i++) {
(*returnColumnSizes)[i] = n;
}

free(queens);
return result;
}

14. 贪心算法(Greedy)

LeetCode 55. 跳跃游戏

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* nums[i] 表示从位置i最多跳nums[i]步
* 判断是否能到达最后一个位置
*
* [2,3,1,1,4] → true
* [3,2,1,0,4] → false
*
* 贪心:维护能到达的最远位置
*/
bool canJump(int* nums, int numsSize) {
int maxReach = 0;
for (int i = 0; i < numsSize; i++) {
if (i > maxReach) return false;
int reach = i + nums[i];
if (reach > maxReach) maxReach = reach;
}
return true;
}

LeetCode 45. 跳跃游戏 II

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 最少跳几次能到达最后
* [2,3,1,1,4] → 2 (跳到1,再跳到4)
*/
int jump(int* nums, int numsSize) {
int jumps = 0, curEnd = 0, farthest = 0;

for (int i = 0; i < numsSize - 1; i++) {
int reach = i + nums[i];
if (reach > farthest) farthest = reach;

if (i == curEnd) { // 到达当前跳跃的边界
jumps++;
curEnd = farthest;
}
}
return jumps;
}

LeetCode 763. 划分字母区间

C

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
/*
* 把字符串划分为尽可能多的片段,每个字母只出现在一个片段中
*
* "ababcbacadefegdehijhklij" → [9,7,8]
*/
int* partitionLabels(char* s, int* returnSize) {
// 记录每个字符最后出现的位置
int lastIndex[26] = {0};
int len = strlen(s);
for (int i = 0; i < len; i++) {
lastIndex[s[i] - 'a'] = i;
}

int *result = (int*)malloc(sizeof(int) * len);
*returnSize = 0;

int start = 0, end = 0;
for (int i = 0; i < len; i++) {
int last = lastIndex[s[i] - 'a'];
if (last > end) end = last;

if (i == end) {
result[(*returnSize)++] = end - start + 1;
start = i + 1;
}
}

return result;
}

15. 字典树(Trie / 前缀树)

LeetCode 208. 实现 Trie

C

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
* 前缀树示例(插入 "apple", "app"):
*
* root
* |
* a
* |
* p
* |
* p (end: "app")
* |
* l
* |
* e (end: "apple")
*/
typedef struct TrieNode {
struct TrieNode *children[26];
bool isEnd;
} TrieNode;

typedef struct {
TrieNode *root;
} Trie;

TrieNode* createTrieNode() {
TrieNode *node = (TrieNode*)calloc(1, sizeof(TrieNode));
return node;
}

Trie* trieCreate() {
Trie *trie = (Trie*)malloc(sizeof(Trie));
trie->root = createTrieNode();
return trie;
}

void trieInsert(Trie* obj, char* word) {
TrieNode *node = obj->root;
while (*word) {
int idx = *word - 'a';
if (!node->children[idx]) {
node->children[idx] = createTrieNode();
}
node = node->children[idx];
word++;
}
node->isEnd = true;
}

bool trieSearch(Trie* obj, char* word) {
TrieNode *node = obj->root;
while (*word) {
int idx = *word - 'a';
if (!node->children[idx]) return false;
node = node->children[idx];
word++;
}
return node->isEnd;
}

bool trieStartsWith(Trie* obj, char* prefix) {
TrieNode *node = obj->root;
while (*prefix) {
int idx = *prefix - 'a';
if (!node->children[idx]) return false;
node = node->children[idx];
prefix++;
}
return true;
}

void freeTrieNode(TrieNode *node) {
if (!node) return;
for (int i = 0; i < 26; i++) {
freeTrieNode(node->children[i]);
}
free(node);
}

void trieFree(Trie* obj) {
freeTrieNode(obj->root);
free(obj);
}

16. 并查集(Union-Find / Disjoint Set)

C

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
/*
* 用于处理不相交集合的合并和查询
* 操作:Find(查找所属集合)、Union(合并两个集合)
*
* 优化:路径压缩 + 按秩合并 → 近乎 O(1)
*/
typedef struct {
int *parent;
int *rank;
int count; // 连通分量个数
} UnionFind;

UnionFind* createUF(int n) {
UnionFind *uf = (UnionFind*)malloc(sizeof(UnionFind));
uf->parent = (int*)malloc(sizeof(int) * n);
uf->rank = (int*)calloc(n, sizeof(int));
uf->count = n;
for (int i = 0; i < n; i++) uf->parent[i] = i;
return uf;
}

// 查找 + 路径压缩
int find(UnionFind *uf, int x) {
if (uf->parent[x] != x) {
uf->parent[x] = find(uf, uf->parent[x]);
}
return uf->parent[x];
}

// 合并 + 按秩
void unionSets(UnionFind *uf, int x, int y) {
int rootX = find(uf, x), rootY = find(uf, y);
if (rootX == rootY) return;

if (uf->rank[rootX] < uf->rank[rootY]) {
uf->parent[rootX] = rootY;
} else if (uf->rank[rootX] > uf->rank[rootY]) {
uf->parent[rootY] = rootX;
} else {
uf->parent[rootY] = rootX;
uf->rank[rootX]++;
}
uf->count--;
}

bool connected(UnionFind *uf, int x, int y) {
return find(uf, x) == find(uf, y);
}

void freeUF(UnionFind *uf) {
free(uf->parent);
free(uf->rank);
free(uf);
}

LeetCode 200. 岛屿数量(并查集解法)

C

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
int numIslands_UF(char** grid, int gridSize, int* gridColSize) {
int rows = gridSize, cols = gridColSize[0];
int total = rows * cols;

UnionFind *uf = createUF(total);
int water = 0;

int dx[] = {1, 0}; // 只需要向右和向下
int dy[] = {0, 1};

for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == '0') {
water++;
continue;
}
for (int d = 0; d < 2; d++) {
int ni = i + dx[d], nj = j + dy[d];
if (ni < rows && nj < cols && grid[ni][nj] == '1') {
unionSets(uf, i * cols + j, ni * cols + nj);
}
}
}
}

int result = uf->count - water;
freeUF(uf);
return result;
}

17. 位运算

LeetCode 191. 位1的个数

C

1
2
3
4
5
6
7
8
int hammingWeight(uint32_t n) {
int count = 0;
while (n) {
n &= (n - 1); // 消除最低位的1
count++;
}
return count;
}

LeetCode 338. 比特位计数

C

1
2
3
4
5
6
7
8
9
10
11
12
/*
* 返回 0n 每个数的二进制中1的个数
* dp[i] = dp[i >> 1] + (i & 1)
*/
int* countBits(int n, int* returnSize) {
*returnSize = n + 1;
int *result = (int*)calloc(n + 1, sizeof(int));
for (int i = 1; i <= n; i++) {
result[i] = result[i >> 1] + (i & 1);
}
return result;
}

18. 数学技巧

LeetCode 48. 旋转图像

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
/*
* 顺时针旋转90度:先转置,再左右翻转
*
* [1,2,3] [1,4,7] [7,4,1]
* [4,5,6][2,5,8][8,5,2]
* [7,8,9] [3,6,9] [9,6,3]
*/
void rotate(int** matrix, int matrixSize, int* matrixColSize) {
int n = matrixSize;

// 转置
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}

// 左右翻转
for (int i = 0; i < n; i++) {
for (int j = 0; j < n / 2; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n-1-j];
matrix[i][n-1-j] = temp;
}
}
}

LeetCode 54. 螺旋矩阵

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
/*
* [1, 2, 3]
* [4, 5, 6] → [1,2,3,6,9,8,7,4,5]
* [7, 8, 9]
*/
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize,
int* returnSize) {
int m = matrixSize, n = matrixColSize[0];
*returnSize = m * n;
int *result = (int*)malloc(sizeof(int) * m * n);
int idx = 0;

int top = 0, bottom = m - 1, left = 0, right = n - 1;

while (top <= bottom && left <= right) {
// 右
for (int j = left; j <= right; j++) result[idx++] = matrix[top][j];
top++;
// 下
for (int i = top; i <= bottom; i++) result[idx++] = matrix[i][right];
right--;
// 左
if (top <= bottom) {
for (int j = right; j >= left; j--) result[idx++] = matrix[bottom][j];
bottom--;
}
// 上
if (left <= right) {
for (int i = bottom; i >= top; i--) result[idx++] = matrix[i][left];
left++;
}
}

return result;
}

LeetCode 73. 矩阵置零

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
/*
* 如果某元素为0,将其所在行列都置为0
* 要求原地,用第一行第一列作为标记
*/
void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {
int m = matrixSize, n = matrixColSize[0];
bool firstRowZero = false, firstColZero = false;

// 检查第一行和第一列
for (int j = 0; j < n; j++) if (matrix[0][j] == 0) firstRowZero = true;
for (int i = 0; i < m; i++) if (matrix[i][0] == 0) firstColZero = true;

// 用第一行第一列作为标记
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}

// 根据标记置零
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}

// 处理第一行第一列
if (firstRowZero) for (int j = 0; j < n; j++) matrix[0][j] = 0;
if (firstColZero) for (int i = 0; i < m; i++) matrix[i][0] = 0;
}

19. 区间问题

LeetCode 435. 无重叠区间

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
/*
* 最少删除多少区间使得不重叠
* [[1,2],[2,3],[3,4],[1,3]] → 删除1个([1,3])
*
* 贪心:按右端点排序,尽量选结束早的
*/
int cmpByEnd(const void *a, const void *b) {
return (*(int**)a)[1] - (*(int**)b)[1];
}

int eraseOverlapIntervals(int** intervals, int intervalsSize,
int* intervalsColSize) {
if (intervalsSize == 0) return 0;

qsort(intervals, intervalsSize, sizeof(int*), cmpByEnd);

int count = 1; // 不重叠的区间数
int end = intervals[0][1];

for (int i = 1; i < intervalsSize; i++) {
if (intervals[i][0] >= end) {
count++;
end = intervals[i][1];
}
}

return intervalsSize - count;
}

20. 复杂数据结构综合

LeetCode 146. LRU 缓存(哈希表 + 双向链表)

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* LRU: Least Recently Used
* get 和 put 都是 O(1)
*
* 双向链表:按使用顺序排列,最近使用的在头部
* 哈希表:key → 链表节点,实现 O(1) 查找
*/
typedef struct DListNode {
int key, value;
struct DListNode *prev, *next;
} DListNode;

#define LRU_HASH_SIZE 10007

typedef struct LRUHashNode {
int key;
DListNode *node;
struct LRUHashNode *next;
} LRUHashNode;

typedef struct {
DListNode *head, *tail; // 哑节点
LRUHashNode *hash[LRU_HASH_SIZE];
int capacity;
int size;
} LRUCache;

DListNode* createDListNode(int key, int value) {
DListNode *node = (DListNode*)malloc(sizeof(DListNode));
node->key = key;
node->value = value;
node->prev = node->next = NULL;
return node;
}

LRUCache* lRUCacheCreate(int capacity) {
LRUCache *cache = (LRUCache*)calloc(1, sizeof(LRUCache));
cache->capacity = capacity;
cache->size = 0;
cache->head = createDListNode(0, 0);
cache->tail = createDListNode(0, 0);
cache->head->next = cache->tail;
cache->tail->prev = cache->head;
return cache;
}

// 链表操作
void removeNode(DListNode *node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}

void addToHead(LRUCache *cache, DListNode *node) {
node->next = cache->head->next;
node->prev = cache->head;
cache->head->next->prev = node;
cache->head->next = node;
}

void moveToHead(LRUCache *cache, DListNode *node) {
removeNode(node);
addToHead(cache, node);
}

DListNode* removeTail(LRUCache *cache) {
DListNode *node = cache->tail->prev;
removeNode(node);
return node;
}

// 哈希操作
int lruHash(int key) {
return ((key % LRU_HASH_SIZE) + LRU_HASH_SIZE) % LRU_HASH_SIZE;
}

DListNode* hashGet(LRUCache *cache, int key) {
int h = lruHash(key);
LRUHashNode *cur = cache->hash[h];
while (cur) {
if (cur->key == key) return cur->node;
cur = cur->next;
}
return NULL;
}

void hashPut(LRUCache *cache, int key, DListNode *node) {
int h = lruHash(key);
LRUHashNode *hNode = (LRUHashNode*)malloc(sizeof(LRUHashNode));
hNode->key = key;
hNode->node = node;
hNode->next = cache->hash[h];
cache->hash[h] = hNode;
}

void hashRemove(LRUCache *cache, int key) {
int h = lruHash(key);
LRUHashNode *cur = cache->hash[h], *prev = NULL;
while (cur) {
if (cur->key == key) {
if (prev) prev->next = cur->next;
else cache->hash[h] = cur->next;
free(cur);
return;
}
prev = cur;
cur = cur->next;
}
}

int lRUCacheGet(LRUCache* obj, int key) {
DListNode *node = hashGet(obj, key);
if (!node) return -1;
moveToHead(obj, node);
return node->value;
}

void lRUCachePut(LRUCache* obj, int key, int value) {
DListNode *node = hashGet(obj, key);

if (node) {
node->value = value;
moveToHead(obj, node);
} else {
DListNode *newNode = createDListNode(key, value);
hashPut(obj, key, newNode);
addToHead(obj, newNode);
obj->size++;

if (obj->size > obj->capacity) {
DListNode *tail = removeTail(obj);
hashRemove(obj, tail->key);
free(tail);
obj->size--;
}
}
}

void lRUCacheFree(LRUCache* obj) {
DListNode *cur = obj->head;
while (cur) {
DListNode *next = cur->next;
free(cur);
cur = next;
}
for (int i = 0; i < LRU_HASH_SIZE; i++) {
LRUHashNode *cur = obj->hash[i];
while (cur) {
LRUHashNode *next = cur->next;
free(cur);
cur = next;
}
}
free(obj);
}

📊 LeetCode 题目索引(按编号)

编号 题目 分类 难度
1 两数之和 哈希表 简单
3 无重复字符的最长子串 滑动窗口 中等
4 寻找两个正序数组的中位数 二分查找 困难
11 盛最多水的容器 双指针 中等
15 三数之和 双指针 中等
17 电话号码的字母组合 回溯 中等
19 删除链表的倒数第N个节点 链表 中等
20 有效的括号 简单
21 合并两个有序链表 链表 简单
22 括号生成 回溯 中等
25 K个一组翻转链表 链表 困难
26 删除有序数组中的重复项 数组 简单
27 移除元素 数组 简单
33 搜索旋转排序数组 二分查找 中等
34 在排序数组中查找范围 二分查找 中等
39 组合总和 回溯 中等
42 接雨水 双指针/栈 困难
45 跳跃游戏 II 贪心 中等
46 全排列 回溯 中等
48 旋转图像 数学 中等
49 字母异位词分组 哈希表 中等
51 N皇后 回溯 困难
53 最大子数组和 动态规划 中等
54 螺旋矩阵 矩阵 中等
55 跳跃游戏 贪心 中等
56 合并区间 排序 中等
62 不同路径 动态规划 中等
64 最小路径和 动态规划 中等
70 爬楼梯 动态规划 简单
72 编辑距离 动态规划 困难
73 矩阵置零 矩阵 中等
75 颜色分类 排序 中等
76 最小覆盖子串 滑动窗口 困难
78 子集 回溯 中等
79 单词搜索 回溯 中等
84 柱状图中最大的矩形 困难
88 合并两个有序数组 数组 简单
98 验证二叉搜索树 二叉树 中等
101 对称二叉树 二叉树 简单
102 二叉树的层序遍历 二叉树 中等
104 二叉树的最大深度 二叉树 简单
105 从前序与中序构造二叉树 二叉树 中等
110 平衡二叉树 二叉树 简单
114 二叉树展开为链表 二叉树 中等
124 二叉树中的最大路径和 二叉树 困难
128 最长连续序列 哈希表 中等
136 只出现一次的数字 位运算 简单
139 单词拆分 动态规划 中等
141 环形链表 链表 简单
142 环形链表 II 链表 中等
146 LRU 缓存 设计 中等
150 逆波兰表达式求值 中等
152 乘积最大子数组 动态规划 中等
153 寻找旋转排序数组的最小值 二分查找 中等
155 最小栈 中等
160 相交链表 链表 简单
169 多数元素 数组 简单
191 位1的个数 位运算 简单
198 打家劫舍 动态规划 中等
200 岛屿数量 图/DFS 中等
206 反转链表 链表 简单
207 课程表 图/拓扑排序 中等
208 实现 Trie 字典树 中等
215 第K个最大元素 中等
225 用队列实现栈 队列 简单
226 翻转二叉树 二叉树 简单
232 用栈实现队列 简单
234 回文链表 链表 简单
236 二叉树的最近公共祖先 二叉树 中等
238 除自身以外数组的乘积 数组 中等
239 滑动窗口最大值 队列 困难
242 有效的字母异位词 哈希表 简单
295 数据流的中位数 困难
300 最长递增子序列 动态规划 中等
322 零钱兑换 动态规划 中等
338 比特位计数 位运算 简单
347 前K个高频元素 中等
383 赎金信 哈希表 简单
416 分割等和子集 动态规划 中等
435 无重叠区间 贪心 中等
438 找到字符串中所有字母异位词 滑动窗口 中等
543 二叉树的直径 二叉树 简单
704 二分查找 二分查找 简单
733 图像渲染 图/DFS 简单
739 每日温度 中等
763 划分字母区间 贪心 中等
912 排序数组 排序 中等
994 腐烂的橘子 图/BFS 中等
1143 最长公共子序列 动态规划 中等

总计:75+ 道 LeetCode 题目,覆盖所有核心数据结构与算法类别。


📋 复杂度速查表

text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
数据结构操作复杂度:
┌──────────────┬─────────┬─────────┬─────────┬─────────┐
│ 数据结构 │ 访问 │ 搜索 │ 插入 │ 删除 │
├──────────────┼─────────┼─────────┼─────────┼─────────┤
│ 数组 │ O(1)O(n)O(n)O(n)
│ 链表 │ O(n)O(n)O(1)O(1)
│ 栈 │ O(n)O(n)O(1)O(1)
│ 队列 │ O(n)O(n)O(1)O(1)
│ 哈希表 │ N/AO(1)*O(1)*O(1)*
BSTO(logn)O(logn)O(logn)O(logn)
│ 堆 │ O(1)顶 │ O(n)O(logn)O(logn)
TrieO(k)O(k)O(k)O(k)
└──────────────┴─────────┴─────────┴─────────┴─────────┘
* 均摊/平均 k = 字符串长度

力扣(LeetCode)刷题
https://xtanguser.github.io/2026/03/12/力扣(LeetCode)刷题/
作者
小唐
发布于
2026年3月12日
许可协议