链表
1 | //Definition for singly-linked list. |
—————————旋转链表
LC_206.反转链表(最基础的)
1 | //1.递归 |
1 | //2.辅助双指针 |
LC_25. K个一组_反转
给你这个链表:1->2->3->4->5
- 当 k = 2 时,应当返回: 2->1->4->3->5
- 当 k = 3 时,应当返回: 3->2->1->4->5

1 | ListNode* reverseKGroup(ListNode* head, int k) { |
LC_92.第N到M个节点_反转
1.哨兵节点,定位pre、cur节点
2.开始反转
辅助节点pre、cur是不会变化的,next节点不断前移

1 | ListNode* reverseBetween(ListNode* head, int m, int n) { |
LC_24.相邻节点_反转
反转链表:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
案列:- 给定 1->2->3->4, 你应该返回 2->1->4->3.

1 | ListNode* swapPairs(ListNode* head) { |
—————————————
—————————删除节点
LC_83.排序链表删除重复节点 / 保留一个
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
输入: 1->1->2
输出: 1->2
输入: 1->1->2->3->3
输出: 1->2->3
解:关键是要删除节点
1 | ListNode* deleteDuplicates(ListNode* head) { |
LC_82.排序链表删除重复节点 / 不保留
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
输入: 1->2->3->3->4->4->5
输出: 1->2->5
输入: 1->1->1->2->3
输出: 2->3
题解:

1 | ListNode* deleteDuplicates(ListNode* head) { |
链表倒数第K个节点
1 | ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { |
LC_19.倒数第K个节点删除(快慢指针)
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
解:
- 快慢指针实现:注意的是while(fast->next)
1 | ListNode* removeNthFromEnd(ListNode* head, int n) { |
—————————————
LC_148.无序链表排序(归并)
时间复杂度O(nlogn)
- 归并排序:递归,需要额外空间复杂度
- 快慢指针、找到中心节点,进行切断
- cut->next = NULL;同时先分割再合并
1 | /** |
两个链表是否有交点
解法:
- 1.都不是环形(走A+C+B = B+C+A)
- 2.一个环一个不是环(无交点)
- 3.都是环(快慢指针分别找到一个随机点p1、p2。p1往前走能遇到p2有交点)
LC_2.两数相加
输入:L1:(2 -> 4 -> 3) + L2:(5 -> 6 -> 4)
- 输出:7 -> 0 -> 8
- 原因:342 + 465 = 807
- 有点类似归并,一个一个接上去
1 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { |
LC_61.指定向右移动链表K
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
- 输入: 1->2->3->4->5->NULL, k = 2
- 输出: 4->5->1->2->3->NULL
- 解释:
- 向右旋转 1 步: 5->1->2->3->4->NULL
- 向右旋转 2 步: 4->5->1->2->3->NULL
1 | ListNode* rotateRight(ListNode* head, int k) { |
LC_138 / J_35. 复制带随机指针的链表
输入
1 | //深拷贝:复制对象 浅拷贝:复制对象的指针 |
21.合并2个升序链表
1 | ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { |
23.合并K个升序链表
分治法
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
38ListNode* mergeKLists(vector<ListNode*>& lists) {
return divideAndMerge(lists, 0, lists.size() - 1); // 使用分治策略优化时间效率
}
//合并两个有序链表
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* pre = new ListNode(-1);
ListNode* cur = pre;
while (l1 != NULL && l2 != NULL) {
if (l1->val < l2->val) {
cur->next = l1;
cur = cur->next;
l1 = l1->next;
}
else {
cur->next = l2;
cur = cur->next;
l2 = l2->next;
}
}
if (l1 == NULL)
cur->next = l2;
else
cur->next = l1;
return pre->next;
}
//类似归并排序:分治法
ListNode *divideAndMerge(vector<ListNode*> &lists, int l, int r) {
if (l == r)
return lists[l];
if (l > r)
return NULL;
int mid = (l + r) / 2;
return mergeTwoLists(divideAndMerge(lists, l, mid),
divideAndMerge(lists, mid + 1, r));
}
树
1 | struct Node { //这是二叉树的节点 |
1.二叉树的层序、先序、中序、后续
1.1层序
1 | void cal(Node* root, vector<int>&res) { |
第二种序列化时用:加入#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25string cal(Node* root) {
if (root == NULL)
return "";
string out;
queue<Node*>q;
q.push(root);
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
Node* temp = q.front();
q.pop();
if (temp) {
out += to_string(temp->value) + " ";
q.push(temp->left);
q.push(temp->right);
}
else {
out += "# ";
}
}
}
return out;
}
1.2先序遍历(LeetCode144)
递归:中、左、右
1
2
3
4
5
6
7
8void preOrder(Node* root) {
if (root == NULL)
return;
cout << root->value;
preOrder(root->left);
preOrder(root->right);
}迭代:利用stack特性来做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void preorder(Node* head) { //先序遍历
if (head == NULL)
return;
stack<Node*>q;
q.push(head); //利用栈的特性,先进右孩子
while (!q.empty())
{
head = q.top();
q.pop();
cout << head->value << " ";
//先进右孩子,stack后输出右孩子
if (head->right)q.push(head->right);
if (head->left)q.push(head->left);
}
}
1.3中序(LeetCode94)
递归:左、中、右
1
2
3
4
5
6
7
8void inOrder(Node* root) {
if (root == NULL)
return;
inOrder(root->left);
cout << root->value;
inOrder(root->right);
}迭代:利用栈特性来做
- 一直延申指向左孩子,同时存入元素
- 当左孩子为空,输出,同时指向右孩子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19void inorder(Node* head)//中序遍历
{
if (head == NULL)
return;
stack <Node*>q;
while (!q.empty() || head != NULL) {
if (head != NULL) {
q.push(head);
head = head->left;//如果一直不空,就一直指向左孩子
}
else {
Node* tmp = q.top();
q.pop();
cout << head->value << " "; //如果为空,输出,同时指针指向右孩子
head = tmp->right;
}
}
}
1.4后序(LeetCode145)
利用先序和两个栈来实现
左、右、中
- 类似前序,前序:中左右,所以先进右
- 后序:左右中,所以先形成中右左,然后利用stack反向打印,就变成了左右中
1
2
3
4
5
6
7
8void afterOrder(Node* root) {
if (root == NULL)
return;
afterOrder(root->left);
afterOrder(root->right);
cout << root->value;
}
利用栈特性来做
- 先实现右中左,然后反向打印。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void postOrder(Node* head) {
if (head == NULL)
return;
stack<Node*>arr;
stack<Node*>q;
q.push(head); //利用栈的特性,先进右孩子
while (!q.empty())
{
head = q.top();
arr.push(head);
q.pop();
//和先序相反:先进左
if (head->left)q.push(head->left);
if (head->right)q.push(head->right);
}
while (!arr.empty()) {
Node* tmp = arr.top();
arr.pop();
cout << tmp->value << " ";
}
}
- 先实现右中左,然后反向打印。
2.剑指Offer树相关题型
J_7.重建二叉树
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
解:先序定根、中序划分区间
1 | struct TreeNode { |
J_26.判断是不是树的子结构
每个节点都有可能是节点
1.判断每个节点
- 2.然后验证每个节点

1 | bool isSubStructure(TreeNode* A, TreeNode* B) { |
J_27.二叉树的镜像

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//递归
TreeNode* dfs(TreeNode* root) {
if (root == NULL)
return root;
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
dfs(root->left);
dfs(root->right);
return root;
}
//队列,迭代
TreeNode* mirrorTree(TreeNode* root) {
queue<TreeNode*> que;
que.push(root);
while (!que.empty())
{
TreeNode* tmp = que.front();
que.pop();
if (tmp == NULL) continue;
swap(tmp->left, tmp->right);
if (tmp->left) que.push(tmp->left);
if (tmp->right) que.push(tmp->right);
}
return root;
}
J_28.对称的二叉树
验证每个余下的节点都需要满足,每个节点都要满足,传入左孩子节点和右孩子节点。

1 | bool isSymmetric(TreeNode* root) { |
J_32-1.层序打印二叉树
典型的层序遍历,按层依次打印节点value值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22vector<int> levelOrder(TreeNode* root) {
//利用队列层序遍历
if (root == NULL)
return {};
queue<TreeNode*>q;
q.push(root);
vector<int>res;
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode* temp = q.front();
int num = temp->val;
res.push_back(num);
q.pop();
if (temp->left)q.push(temp->left);
if (temp->right)q.push(temp->right);
}
}
return res;
}
J_33.二叉搜索树的后序遍历判断
一个数组,判断数组是不是二叉树的后序遍历
输入: [1,6,3,2,5]
输出: false

1 | bool verifyPostorder(vector<int>& postorder) { |
J_34.二叉树中的路径和(回溯)
需要遍历到底部:每条路都是有可能的,所以用回溯

1 | //找到所有路 |
J_34.拓展.二叉树中的路径和

1 | bool hasPathSum(TreeNode* root, int sum) { |
J_37.序列化二叉树(先序遍历构建)
1 | struct TreeNode { |
J_55-1.计算二叉树的高度
递归下去
1 | int maxDepth(TreeNode* root) { |
J_55.拓展.求二叉树的最大直径

利用上一题的求节点的最大深度
在求数深度的过程中,不单单只是MAX(left,right)+1
- 而是比较max(res,L+R);
在求数的深度的过程中,比较maxLength
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int length;
int diameterOfBinaryTree(TreeNode* root) {
length = 0;
dfs(root);
return length;
}
int dfs(TreeNode* root) {
if (root == NULL)
return 0;
int L = dfs(root->left);
int R = dfs(root->right);
int curlength = L + R;
length = max(length, curlength); //这个节点的最大值
return max(L, R) + 1; //返回该子节点的最大分治
}
J_55-2.判断是否平衡二叉树
平衡二叉树:高度差小于等于1即可。
1 | bool dfs(TreeNode* root) { |
J_68-1.二叉搜索树的最近公共祖先
利用二叉搜索树的特性
- 左 < 中 < 右
- 利用节点的值,来进行计算,只有当的Value值一个大于,一个小于:找到节点
1
2
3
4
5
6
7
8
9
10
11
12TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL)
return NULL;
if (root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left, p, q);
if (root->val < p->val &&root->val < q->val)
return lowestCommonAncestor(root->right, p, q);
return root;
}
J_68-2.二叉树的最近公共祖先
依次往下遍历节点
如果找到了:q、p返回
- 否则:继续往下遍历,查看左树和右树的查询结果。

1 | TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { |
222.完全二叉树的节点数
- 方法一:递归
- 方法二:利用性质logn * logn
- 方法三:利用二分法计算
1 | //方法一:递归 |
1 | //方法二:利用性质logn * logn |
1 | //方法三:利用二分法计算 |
二分
头文件:algorithm
- lower_bound( begin,end,num )
- 查找第一个大于或等于num的数字—— >=num
- upper_bound( begin,end,num )
- 查找第一个大于num的数字 —— >num
auto iter = upper_bound(A.begin(), A.end(), B[i]);
- 返回:迭代器
- ans.push_back(* iter);
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//大于或等于target的第一个数
int lower_bound(vector<int>& nums, int target) {
int L = 0;
int R = nums.size();
while (L < R) {
int mid = (L + R) >> 1;
if (nums[mid] < target)
L = mid + 1;
else
R = mid;
}
return L;
}
//大于target的第一个数
int upper_bound(vector<int>& nums, int target) {
int L = 0;
int R = nums.size();
while (L < R) {
int mid = (L + R) >> 1;
if (nums[mid] <= target)
L = mid + 1;
else
R = mid;
}
return L;
}
33.搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
1 | int search(vector<int>& nums, int target) { |
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
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//大于等于target的第一个数
int lower_bound(vector<int>& nums, int target) {
int L = 0;
int R = nums.size();
while (L < R) {
int mid = (L + R) >> 1;
if (nums[mid] < target)
L = mid + 1;
else
R = mid;
}
return L;
}
//大于target的第一个数
int upper_bound(vector<int>& nums, int target) {
int L = 0;
int R = nums.size();
while (L < R) {
int mid = (L + R) >> 1;
if (nums[mid] <= target)
L = mid + 1;
else
R = mid;
}
return L;
}
vector<int> searchRange(vector<int>& nums, int target) {
int L = lower_bound(nums, target);
int R = upper_bound(nums, target);
//说明不存在target
if (L == R)
return vector<int>{-1, -1};
return vector<int>{L, R - 1};
}
4.重要:两个正序数组,第K小的数

采用二分的方法
1.先取数组中各个中间的数,小的部分舍去这个区间。
2.第K小的数,随之减去舍去这个区间的大小。
- 1.当一个区间为0,再剩下的数组中
- 2.当K=1,说明还剩下一个,min(top1,top2).
为了保证当一个区间为0,直接返回:我们永远让上一个数组的容量为小,再进行判断。
1 | int find(vector<int>arr1, int begin1, int end1, vector<int>arr2, int begin2, int end2, int k) { |
一个数的平方根 / pow(x,n)的实现
1 | int sqrt(int x) { |
