编程总结
每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
3
/
9 20
/
15 7
返回它的最大深度 3
int maxDepth(struct TreeNode *root) {
if (root == NULL) {
return 0;
} else { // 递归调用
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
}
递归需要两个条件:
1)递归终止条件
2)递归过程
递归的递与归。
递归有分递和归,切记别忘了归,下面列举刚才二叉树深度遍历的例子来说明:
上面利用二叉树深度递归的结果打印输出:
1)先传入root 节点
2)逗号表达式,先遍历right,传入右节点的根节点 right
3)根节点访问,先遍历右节点 right_r,传入右节点 right_r
4) right_r->right 为null,return 0
5) right_r->left 为null,return 0,至此,右节点遍历完成了,开始 “归” 的过程,depth = (1 + max(0 + 0)) ==>> 1. (两个0,就是return返回来的0).
6)遍历左节点 right_l, 同理,左右节点都为null, 所以,depth = (1 + max(0 + 0)) ==>> 1.
7)right_r 和 right_l 点都遍历完成了,depth = (1 + max(1 + 1)) ==>> 2. right 节点遍历完成。
8)接着遍历 left 节点,depth = (1 + max(0 + 0)) ==>> 1.
9)最后 depth = (1 + max(1 + 2)) ==>> 3.
递即先递到右节点的叶子节点,然后由叶子节点逐层由下往上归回来到 root 节点。有点像后序遍历,先右再左。
int maxDepth(struct TreeNode *root) {
if (root == NULL) {
printf("root is NULL\n");
return 0;
} else {
printf("root is %s, depth is %d\n", root->val, depth);
depth = (1 + max(maxDepth(root->left), maxDepth(root->right)));
return depth;
}
}
112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
bool hasPathSum(struct TreeNode *root, int sum) {
// 注意:题意要求是根节点到叶子节点的访问,不能只有根节点达到sum
if (root == NULL) {
return false; // 异常情况
}
// 遍历到叶子节点了
if (root->left == NULL && root->right == NULL) {
if (sum - root->val == 0) { // root 是叶子节点,所以应该判断 sum - root->val
return true;
} else {
return false;
}
}
if (hasPathSum(root->left, sum - root->val) == 1) {
return true;
}
if (hasPathSum(root->right, sum - root->val) == 1) {
return true;
}
return false;
}
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
(二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值,左边小于根,右边大于根)
struct TreeNode *lowestCommonAncestor(struct TreeNode *root, struct TreeNode *p, struct TreeNode *q) {
if (root == NULL || q == NULL || p == NULL) {
return NULL;
}
// 如果节点 p 和节点 q 都在右子树上,那么以右孩子为根节点继续 1 的操作
if ((p->val > root->val) && (q->val > root->val)) {
return lowestCommonAncestor(root->right, p ,q);
}
// 如果节点 p 和节点 q 都在左子树上,那么以左孩子为根节点继续 1 的操作
if ((p->val < root->val) && (q->val < root->val)) {
return lowestCommonAncestor(root->left, p ,q);
}
// 分布在两边,则已经找到节点是root节点为最近公共祖先
return root;
}
144. 二叉树的前序遍历
给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1
2
/
3
输出: [1,2,3]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
void iterator(struct TreeNode *root, int *arr, int *returnSize) {
if (root == NULL) {
return;
}
*(arr + *(returnSize)) = root->val;
*(returnSize) = *(returnSize) + 1;
iterator(root->left, arr, returnSize);
iterator(root->right, arr, returnSize);
}
int* preorderTraversal(struct TreeNode *root, int *returnSize) {
int *arr = (int *)malloc(sizeof(int) * 1000);
*(returnSize) = 0;
if (root == NULL) {
return arr;
}
iterator(root, arr, returnSize);
return arr;
}
int main() {
struct TreeNode *root;
struct TreeNode *root_r;
struct TreeNode *root_second_l;
int *returnSize = NULL;
returnSize = (int *)malloc(4);
root = (struct TreeNode *)malloc(sizeof(struct TreeNode));
root->right = (struct TreeNode *)malloc(sizeof(struct TreeNode));
root_r = (struct TreeNode *)malloc(2*sizeof(struct TreeNode));
root_r->left = (struct TreeNode *)malloc(2 * sizeof(struct TreeNode));
root_second_l = (struct TreeNode *)malloc(sizeof(struct TreeNode));
root->val = 1;
root_r->val = 2;
root_second_l->val = 3;
root_r = root->right;
root_second_l = root_r->left;
preorderTraversal(root, returnSize);
return 0;
}
二叉树的遍历:
前序/中序/后序:
给定一个二叉树,返回它的 前序 遍历。
示例:
void iterator(struct TreeNode *root, int *arr, int *returnSize) {
if (root == NULL) { // 递归终止条件是递归到NULL叶子节点
return;
}
*(arr + *(returnSize)) = root->val; // 存储根节点值到 *arr
*(returnSize) = *(returnSize) + 1; // count++
iterator(root->left, arr, returnSize);
iterator(root->right, arr, returnSize);
}
int *preorderTraversal(struct TreeNode* root, int *returnSize) {
int *arr = (int *)malloc(sizeof(int) * 1000);
*(returnSize) = 0;
if (root == NULL) { // 异常判断
return arr;
}
iterator(root, arr, returnSize); // 进入递归
return arr;
}
遍历顺序: 1 -> null -> 2 ->3->null(3’left)->null(3’right)->null(2’right)->结束
中序遍历的区别是赋值代码段的位置
void iterator(struct TreeNode *root, int *arr, int *returnSize) {
if (root == NULL) { // 递归终止条件是递归到NULL叶子节点
return;
}
iterator(root->left, arr, returnSize);
*(arr + *(returnSize)) = root->val; // 赋值断码段 -- 存储根节点值到 *arr
*(returnSize) = *(returnSize)+1; // 赋值断码段 -- count++
iterator(root->right, arr, returnSize);
}
int *inorderTraversal(struct TreeNode* root, int *returnSize) {
int *arr = (int *)malloc(sizeof(int) * 1000);
*(returnSize) = 0;
if (root == NULL) { // 异常判断
return arr;
}
iterator(root, arr, returnSize); // 进入递归
return arr;
}
遍历顺序: null(1’left) -> 1 ->2->3->null(3’left)->null(3’right)->null(2’right)->结束
后序遍历:
void iterator(struct TreeNode *root, int *arr, int *returnSize) {
if (root == NULL) { // 递归终止条件是递归到NULL叶子节点
return;
}
iterator(root->left, arr, returnSize);
iterator(root->right, arr, returnSize);
*(arr + *(returnSize)) = root->val; // 赋值断码段 -- 存储根节点值到 *arr
*(returnSize) = *(returnSize)+1; // 赋值断码段 -- count++
}
int *postorderTraversal(struct TreeNode* root, int *returnSize) {
int *arr = (int *)malloc(sizeof(int) * 1000);
*(returnSize) = 0;
if (root == NULL) { // 异常判断
return arr;
}
iterator(root, arr, returnSize); // 进入递归
return arr;
}
遍历顺序: null(1’left)->2->3->null(3’left) -> null(3’right) ->Output:3->null(2’right)->Output:2->Output:1->结束
79. 单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
bool visited[1000][1000] = { 0 };
bool searchWord(char **board, int boardSize, int *boardColSize, char *word, int index, int startx, int starty) {
bool result = 0;
if ((startx < 0 || startx >= boardSize || starty < 0 || starty >= (*boardColSize))) {
return false;
}
if (visited[startx][starty] == 1) {
return false;
}
if (board[startx][starty] != word[index]) { // 此时已经说明 [0, strlen(word)-1] 相等,因为 index 的范围就是 [0, strlen(word)-1]
return false;
}
if (index + 1 == strlen(word)) {
return true;
}
visited[startx][starty] = 1; // 从四个方向寻找
result = searchWord(board, boardSize, boardColSize, word, index + 1, startx - 1, starty) ||
searchWord(board, boardSize, boardColSize, word, index + 1, startx, starty + 1) ||
searchWord(board, boardSize, boardColSize, word, index + 1, startx + 1, starty) ||
searchWord(board, boardSize, boardColSize, word, index + 1, startx, starty - 1);
visited[startx][starty] = 0;
return result;
}
bool exist(char **board, int boardSize, int *boardColSize, char *word) {
for (int v = 0; v < 1000; v++) {
memset(visited[v], 0, sizeof(visited[0]));
}
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < *boardColSize; j++) {
if (searchWord(board, boardSize, boardColSize, word, 0, i, j)) {
return true;
}
}
}
return false;
}
101. 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
对称二叉树就是要求: L.right == R.left && L.left == R.right
bool isSymmetric(struct TreeNode *root) {
bool res = false;
if (root == NULL) {
return true;
}
return isSymTree(root->left, root->right);
}
bool isSymTree(struct TreeNode *root_left, struct TreeNode *root_right)
{
bool res = false;
// 递归终止条件
if (root_left == NULL && root_right == NULL) {
return true;
}
else if (root_left == NULL || root_right == NULL) { // 注意root没有左或者右节点时,为False
return false;
}
if (root_left->val != root_right->val) {
return false;
}
res = isSymTree(root_left->left, root_right->right) && isSymTree(root_left->right, root_right->left);
return res;
}
108. 将有序数组转换成二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。