K-Space


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索

Leetcode-378-有序矩阵中第K小的元素

发表于 2020-07-02 | 分类于 Leetcode

题目

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。

示例:

matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,

返回 13。

提示:
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n2 。

二分查找

由题目给出的性质可知, 矩阵内的元素左上角最小, 右下角最大. 假设left和right分别为二分法使用时的最大最小值. 使用二分法的步骤是

  1. 计算出矩阵内最大值和最小值的均值(即mid=(left+right)/2)

  2. 然后从矩阵的左下角(matrix[n-1][0])到右上角(matrix[0][n-1])进行遍历, 找出小于等于mid的元素的个数.

    如图所示, 如果当前元素matrix[i][j]小于等于mid, 则小于等于mid的元素共有i+1个(考虑到数组下标从0开始), 并且向右移动. 否则向上移动. 直至最后走出矩阵.

  3. 计算出来小于等于mid的元素数目大于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
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int left = matrix[0][0], right = matrix[n - 1][n - 1];
while (left < right) {
int mid = (left + right) / 2;
if (lessEqualThanMid(matrix, mid) < k) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}

private int lessEqualThanMid(int[][] matrix, int mid) {
int n = matrix.length;
int i = n - 1, j = 0, sum = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] <= mid) {
sum += i + 1;
j++;
} else {
i--;
}
}
return sum;
}
}

Leetcode-718 最长重复子数组

发表于 2020-07-01 | 分类于 Leetcode

题目

给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。

示例 1:

输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]

输出: 3

解释:
长度最长的公共子数组是 [3, 2, 1]。

说明:

1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100

暴力解法

首先看一下暴力解法.

使用下标i遍历数组A, 使用下标j遍历数组B. 寻找以元素A[i]和B[j]为首的最长公共数组长度$s_{ij}$, 那么全局的最长公共数组长度为

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int findLength(int[] A, int[] B) {
int s = 0;
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < B.length; j++) {
int k = 0;
while (i + k < A.length && j + k < B.length
&& A[i + k] == B[j + k]) {
k++;
s = Math.max(s, k);
}
}
}
return s;
}
}

时间复杂度为$O(n^3)$

动态规划

在暴力解法当中, 元素A[i]和元素B[j]会进行多次比较. 可以利用动态规划来进行优化, 使得元素A[i]和元素B[j]只会进行一次比较.

令dp[i][j]为元素A[i]和元素B[j]的最长公共重复子数组长度. 那么我们可以知道, 若A[i]==B[j], 则有dp[i][j]=dp[i+1][j+1]+1; 若A[i]!=B[j], 则有dp[i][j]=0. 由于dp[i][j]依赖于dp[i+1][j+1], 所以i和j需要从后往前遍历.

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int findLength(int[] A, int[] B) {
int m = A.length, n = B.length;
int[][] dp = new int[m + 1][n + 1]; // default 0
int s = 0;
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
if (A[i] == B[j]) {
dp[i][j] = dp[i + 1][j + 1] + 1;
} else {
dp[i][j] = 0;
}
s = Math.max(s, dp[i][j]);
}
}
return s;
}
}

滑动窗口

如图所示, 分别枚举数组A向右移动和数组B向右移动的所有情况, 然后计算红框内最大的公共数组长度.

代码如下所示

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
class Solution {
public int findLength(int[] A, int[] B) {
int m = A.length, n = B.length;
int s = 0;

// A move
for (int i = 0; i < n; i++) {
int len = Math.min(n - i, m); // 取重叠处的长度
int tmp = maxLength(A, B, 0, i, len); // 计算红框处的最大公共子数组长度
s = Math.max(s, tmp);
}

// B move
for (int i = 0; i < m; i++) {
int len = Math.min(m - i, n);
int tmp = maxLength(A, B, i, 0, len);
s = Math.max(s, tmp);
}

return s;
}

private int maxLength(int[] A, int[] B, int startA, int startB, int len) {
int s = 0, k = 0;
for (int i = 0; i < len; i++) {
if (A[startA + i] == B[startB + i]) {
k++;
} else {
k = 0;
}
s = Math.max(s, k);
}
return s;
}
}

树的非递归遍历

发表于 2020-05-09 | 分类于 算法笔记

前言

众所周知, 树的遍历主要有四种方法: 前序遍历, 中序遍历, 后序遍历和层序遍历. 其中, 前中后序遍历最简洁的实现方法就是递归实现, 以二叉树为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 前序遍历递归实现的伪代码
void preOrder(Node root) {
if (root == null) return;
visit(root);
preOrder(root.left);
preOrder(root.right);
}

// 中序遍历递归实现的伪代码
void inOrder(Node root) {
if (root == null) return;
inOrder(root.left);
visit(root);
inorder(root.right);
}

// 后序遍历递归实现的伪代码
void postOrder(Node root) {
if (root == null) return;
postOrder(root.left);
postOrder(root.right);
visit(root);
}

而非递归的实现则会使用栈来代替递归, 因为计算机内部也是用栈来实现递归的.

中序遍历

为了方便理解, 先从中序遍历开始讲起.

回忆一下, 二叉树中中序遍历的顺序是: 左子树 - 根 - 右子树, 那么我们一开始肯定要走到树的最左下角, 并将经过的结点进行入栈

1
2
3
4
5
Node p = root;
while (p != null) {
stack.push(p)
p = p.left;
}

从图中可以看见, 当p左边的路径走到尽头之后, 栈顶结点为3, 且有两种情况:

  1. 没有右儿子
  2. 有右儿子

对于情况1, 栈顶结点没有右儿子, 直接出栈栈顶结点, 然后访问, p指向右儿子(为空)

1
2
3
p = stack.pop();
visit(p);
p = p.right; // null

对于情况2, 栈顶结点有右儿子, 直接出栈栈顶结点, 然后访问, p指向右儿子(不为空)

1
2
3
p = stack.pop();
visit(p);
p = p.right; // not null

上面两个代码将两种情况都统一表示了, 那么如何去区分两种情况呢? 方法很简单, 只要在迭代里面增加对p是否为null进行判断就可以了.

  • 如果p为null, 说明栈顶结点没有右子树, 那么我们继续处理栈的下一个结点
  • 如果p不为null, 说明栈顶结点有右子树, 那么我们需要对这个右子树进行一遍上面所说的操作, 也就是说需要走到右子树的”最左边”. 到达”最左边”之后, 继续进行我们所说的这些处理.

伪代码如下:

1
2
3
4
5
6
7
8
9
10
while (some condition) {
if (p != null) { // p不为空, 则需要走到"最左边"
stack.push(p); // 走的过程中将路过的结点入栈
p = p.left;
} else { // p为空, 那么处理栈剩下的结点
p = stack.pop(); // 出栈
visit(p); // 访问结点
p = p.right; // 指向p的右子树, 留到下一个循环去判断p是否为空
}
}

那么代码的大体框架已经写好了, 最后只剩下while()循环里面的条件了, 很明显当栈空且p也为null的情况下需要中止循环, 已经遍历完了. (注意: 当栈为空时, 不一定遍历结束, 因为p不为空的话, 说明还有右子树没有遍历). 转换一下逻辑, 也就是说当栈不空或者p不为null的时候可以继续循环. 完整的伪代码如下:

1
2
3
4
5
6
7
8
9
10
while (p != null || !stack.isEmpty()) {
if (p != null) { // p不为空, 则需要走到"最左边"
stack.push(p); // 走的过程中将路过的结点入栈
p = p.left;
} else { // p为空, 那么处理栈剩下的结点
p = stack.pop(); // 出栈
visit(p); // 访问结点
p = p.right; // 指向p的右子树, 留到下一个循环去判断p是否为空
}
}

OK! 伪代码写出来了, 那我们来实践一下吧! Leetcode中的94.二叉树的中序遍历这一题就是

考察二叉树中序遍历的迭代实现, 根据上面所说的, 可以很轻松地写下代码(使用Java实现)

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode p = root;
while (p != null || !stack.isEmpty()) {
if (p != null) {
stack.push(p);
p = p.left;
} else {
p = stack.pop();
result.add(p.val);
p = p.right;
}
}
return result;
}
}

前序遍历

前序遍历和中序遍历思想是类似的, 中序遍历需要先入栈再访问, 而先序遍历则是访问再入队. 这样就会满足: 根结点 - 左子树 - 右子树

1
2
3
4
5
6
7
8
9
10
while (p != null || stack.isEmpty()) {
if (p != null) { // p 不为空, 则需要走到"最左边"
visit(p); // 在入栈之前进行访问结点
stack.push(p);
p = p.left;
} else {
p = stack.pop(); // 出栈
p = p.right; // 指向p的右子树, 留到下一个循环去判断p是否为空
}
}

OK! 同样地, 去Leetcode实践一下->二叉树的前序遍历

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
LinkedList<TreeNode> stack = new LinkedList<>();
ArrayList<Integer> result = new ArrayList<>();
TreeNode p = root;
while (p != null || !stack.isEmpty()) {
if (p != null) {
result.add(p.val);
stack.push(p);
p = p.left;
} else {
p = stack.pop();
p = p.right;
}
}
return result;
}
}

后序遍历

后序遍历的情况会更复杂一些, 因为后序遍历的顺序是: 左子树 - 右子树 - 根结点. 所以取出栈顶元素之后, 栈顶元素如果:

  • 位于左子树中, 需要跳过根结点, 直接访问右子树
  • 位于右子树中, 访问根结点

有一种实现方法是在树的结构中增加标记来判断是否需要跳过根结点. 但是我们可以使用更简便地方法, 就是将顺序颠倒一下.

后序遍历的顺序颠倒过来就是: 根结点 - 右子树 - 左子树.

仔细观察一下, 是不是很像前序遍历的顺序? 前序遍历中的顺序是: 根结点 - 左子树 - 右子树.

我们只需要将前序遍历代码中的left和right交换一下, 就可以得到: 根结点 - 右子树 - 左子树的顺序, 最后再将结果反转, 就可以得到”左子树 - 右子树 - 根结点”的顺序. 而实现的时候只需要将结点添加到List的头部就可以了

1
2
3
4
5
6
7
8
9
10
11
while (p != null || stack.isEmpty()) {
if (p != null) { // p 不为空, 则需要走到"最右边"
result.addFirst(p); // 在入栈之前进行访问结点
stack.push(p);
p = p.right; //
} else {
p = stack.pop(); // 出栈
p = p.left; // 指向p的左子树, 留到下一个循环去判断p是否为空
}
}
System.out.println(result);

OK! 去Leetcode实践一下->二叉树的后序遍历

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> result = new LinkedList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode p = root;
while (p != null || !stack.isEmpty()) {
if (p != null) {
result.addFirst(p.val);
stack.push(p);
p = p.right;
} else {
p = stack.pop();
p = p.left;
}
}
return result;
}
}

N叉树的遍历

既然二叉树的遍历已经搞清楚了, 那么我们接下来就推广到N叉树吧. 要注意的是N叉树的中序遍历暂无明确的定义, 所以这里我们只讨论N叉树的前序遍历和后序遍历.

N叉树遍历的递归实现

先来看看前序遍历和后序遍历的递归伪代码实现吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// N叉树的前序遍历
void preOrder(Node root) {
if (root == null) return;
visit(root)
for (Node child : root.children) {
preorder(child);
}
}

// N叉树的后序遍历
void postOrder(Node root) {
if (root == null) return;
for (Node child : root.children) {
postOrder(child);
}
visit(root);
}

N叉树前序遍历

  1. 先将根结点入栈
  2. 队中结点出列, 并将结点的值放入结果队列中, 然后将其子结点从右至左的顺序入队(保证下一个结点是最左边的子结点)
  3. 重复第二步直至栈为空

直接在Leetocde上面实现看看! 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
29
30
31
32
33
34
35
36
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;

public Node() {}

public Node(int _val) {
val = _val;
}

public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/

class Solution {
public List<Integer> preorder(Node root) {
LinkedList<Node> stack = new LinkedList<>();
LinkedList<Integer> result = new LinkedList<>();
if (root == null) return result;
stack.push(root);
while (!stack.isEmpty()) {
Node tmpNode = stack.pop();
result.add(tmpNode.val);
Collections.reverse(tmpNode.children); // 逆序
for (Node child : tmpNode.children) {
stack.push(child);
}
}
return result;
}
}

N叉树后序遍历

  1. 先将根结点入栈
  2. 取出栈中结点, 并将结点的值放入结果队列的头部, 将其子结点按左到右的顺序入栈
  3. 重复第二步直至栈为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public List<Integer> postorder(Node root) {
LinkedList<Node> stack = new LinkedList<>();
LinkedList<Integer> output = new LinkedList<>();
if (root == null) return output;
stack.add(root);
while (!stack.isEmpty()) {
Node tmpNode = stack.pollLast();
output.addFirst(tmpNode.val);
for (Node child : tmpNode.children) {
stack.add(child);
}
}
return output;
}
}

Leetcode-590 N叉树的后序遍历

发表于 2020-05-07 | 分类于 Leetcode

题目

给定一个 N 叉树,返回其节点值的后序遍历。

例如,给定一个 3叉树 :

返回其后序遍历: [5,6,3,2,4,1].

说明: 递归法很简单,你可以使用迭代法完成此题吗?

思路一: 递归法

正如题目中所言, 递归法确实很简单, 利用二叉树中的”左-右-根”顺序进行推广, 即先遍历子树, 最后将根的值放入list中即可

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
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;

public Node() {}

public Node(int _val) {
val = _val;
}

public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/

class Solution {
public List<Integer> postorder(Node root) {
List<Integer> list = new ArrayList<>();
postorder(root, list);
return list;
}

private void postorder(Node root, List<Integer> list) {
if (root == null) return;
for (Node child : root.children) {
postorder(child, list);
}
list.add(root.val);
}
}

思路二: 迭代

树的遍历除了用递归实现, 还可以用迭代实现.

  1. 先将根结点入栈
  2. 取出栈中结点, 并将结点的值放入结果队列的头部, 将其子结点按左到右的顺序入栈
  3. 重复第二步直至栈为空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public List<Integer> postorder(Node root) {
LinkedList<Node> stack = new LinkedList<>();
LinkedList<Integer> output = new LinkedList<>();
if (root == null) return output;
stack.add(root);
while (!stack.isEmpty()) {
Node tmpNode = stack.pollLast();
output.addFirst(tmpNode.val);
for (Node child : tmpNode.children) {
stack.add(child);
}
}
return output;
}
}

Leetcode-700 二叉搜索树中的搜索

发表于 2020-05-07 | 分类于 Leetcode

题目

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

例如,

你应该返回如下子树:

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL

思路

思路非常简单, 利用搜索二叉树左小右大的性质进行递归即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) return root;
else if (root.val > val) return searchBST(root.left, val);
else return searchBST(root.right, val);
}
}

Leetcode-226 翻转二叉树

发表于 2020-05-06 | 分类于 Leetcode

题目

翻转一棵二叉树。

示例:

备注:
这个问题是受到 Max Howell 的原问题启发的 :

谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。

思路

直接递归翻转就可以了

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
// 空树直接返回
if (root == null) return root;

// 交换当前结点的左右子树
TreeNode tmp = root.right;
root.right = root.left;
root.left = tmp;

// 递归处理左右子树
root.left = invertTree(root.left);
root.right = invertTree(root.right);

// 返回根结点
return root;
}
}

1588736749671

Leetcode-617 合并二叉树

发表于 2020-05-06 | 分类于 Leetcode

题目

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。


注意: 合并必须从两个树的根节点开始

思路

直接使用前序遍历同时遍历两棵树, 将二者的结点相加起来(以t1树为基)

1
2
3
t1.val += t2.val;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);

这是递归体, 然后解决递归基的问题. 这个也很简单, 当t1为空时返回t2, t2为空时返回t1即可.

1
2
if (t1 == null) return t2;
if (t2 == null) return t1;

汇总一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null) return t2;
if (t2 == null) return t1;
t1.val += t2.val;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
return t1;
}
}

Leetcode-938 二叉搜索树的范围和

发表于 2020-05-06 | 分类于 Leetcode

题目

给定二叉搜索树的根结点 root,返回 L 和 R(含)之间的所有结点的值的和。

二叉搜索树保证具有唯一的值。

示例 1:

输入:root = [10,5,15,3,7,null,18], L = 7, R = 15
输出:32

示例 2:

输入:root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10
输出:23

提示:

  1. 树中的结点数量最多为 10000 个。
  2. 最终的答案保证小于 2^31。

思路一

直接遍历整个二叉树, 寻找大于等于L和小于等于R的结点的总和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int rangeSumBST(TreeNode root, int L, int R) {
return cntBSt(root, L, R);
}

int cntBSt(TreeNode root, int L, int R) {
if (root == null) return 0;
int result;
if (root.val < L || root.val > R) result = 0;
else result = root.val;
result += cntBSt(root.left, L, R);
result += cntBSt(root.right, L, R);
return result;
}
}

思路二

对上面的方法进行优化, 因为给定的树是二叉搜索树, 所以

  • 当结点小于L时, 只搜索该结点的右子树(左子树均小于L)
  • 当结点大于R时, 只搜索该结点的左子树(右子树均大于R)

换句话说

  • 只有结点大于L时, 才搜索左子树
  • 只有结点小于R时, 才搜索右子树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int rangeSumBST(TreeNode root, int L, int R) {
return cntBSt(root, L, R);
}

int cntBSt(TreeNode root, int L, int R) {
if (root == null) return 0;
int result;
if (root.val < L || root.val > R) result = 0;
else result = root.val;
if (root.val > L) result += cntBSt(root.left, L, R); // val大于L才能进行搜索左子树
if (root.val < R) result += cntBSt(root.right, L, R); // val小于R才能进行搜索右子树
return result;
}
}

第一篇文章

发表于 2020-01-14

利用Hexo+Github Page从零开始搭建自己的博客(二)——开始写作

发表于 2020-01-12 | 分类于 从零开始创建博客

创建你的第一篇文章

新建文章

使用Hexo框架新建文章非常简单, 只需要在myBlog目录下执行以下命令即可

1
hexo new post '第一篇文章'

执行完成之后, 就可以在myBlog\source\_posts目录下找到"第一篇文章.md"文件.

md是markdown的缩写, 下一节会介绍什么是markdown.

markdown编辑器有很多, 我自己使用的是typora, 这里是typora的官网.

Hexo中的布局

刚刚提到的post是Hexo中布局(Layout)的一种. Hexo默认的布局有三种:post, page和draft, 分别代表了文章, 页面和草稿, 它们会被保存到不同的路径.

布局 路径
post source\\_posts
page source
draft source\\_drafts

比较常用的有文章post和草稿draft. 新建post的方法在上面也提到过了, 即

1
hexo new post '文章名'

新建草稿则是

1
hexo new draft '文章名'

如果想将草稿移动到source/_posts文件夹, 则可以使用publish命令

1
hexo publish post '文章名'

写好文章(post)之后, 需要在终端中进行生成(hexo g)和部署(). 不过在生成和部署之前, 最好使用clean命令清除缓存

1
2
hexo clean
hexo g && hexo d

分类与标签

新建的hexo文章会有一个头文件

1
2
title: xxx
date: yyyy-dd-mm

可以在下面添加categories字段来说明分类, tags字段说明标签

如

1
2
3
4
5
6
7
title: xxx
date: yyyy-dd-mm
categories:
- 分类
tags:
- 标签1
- 标签2

Markdown

Hexo中默认生成的新文章是Markdown格式的. Markdown是一种轻量级的标记语言, 相对于HTML, Latex等复杂的语法而言, Markdown的语法规则更为简单.

下面做一些简单的介绍, 详细的可以看John Gguber的博客以及其中译版.

标题

在Markdown中, 只要在文本前面加上#号, 并用空格隔开, 总共有六级标题. 例如

1
2
3
4
5
6
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题

在Typora中, 输入Markdown语法之后会立刻渲染, 显示如下.

image-20200705180524567

列表

无序列表使用-或者*

1
2
3
- 项目1
- 项目2
- 项目3
  • 项目1
  • 项目2
  • 项目3

有序列表使用数字+.

1
2
3
1. 项目1
2. 项目2
3. 项目3
  1. 项目1
  2. 项目2
  3. 项目3

插入超链接

插入超链接的方式为

1
[链接名称](超链接)

例如:

1
[百度](http://www.baidu.com)

效果为: 百度

插入图片

插入图片的方式为

1
![图片名称](图片路径)

其中图片路径可以是本地的相对路径或者绝对路径, 也可以是网络上的路径. 一般在部署博客时, 会使用图床来存放图片, 然后在博客上使用网络路径引用图片. 这样做的好处是部署博客时不需要上传图片, 加快部署速度.

具体的图床配置下一小节会提到.

引用

1
> 引用内容

引用内容

可以嵌套使用引用

引用内容

引用引用内容

引用引用引用内容

禁止套娃

粗体与斜体

1
2
**粗体**
*斜体*

粗体

斜体

表格

1
2
3
4
5
| 项目1 | 项目2 | 项目3 |
| ----- | ----- | ----- |
| 内容1 | 内容2 | 内容3 |
| 内容4 | 内容5 | 内容6 |
| 内容7 | 内容8 | 内容9 |
项目1 项目2 项目3
内容1 内容2 内容3
内容4 内容5 内容6
内容7 内容8 内容9

代码块

行内代码块

行内代码块使用两个` 将代码包裹起来, 比如

1
对变量`i`进行自增

效果如下:

对变量i进行自增

行间代码块

行间代码块使用一对```来将代码包裹起来. 在第一个```后面可以标记上所使用的语言, 比如:

1
2
3
4
5
​```Java
public static void main(String args[]) {
System.out.println("Hello, fucking world!");
}
​
1
2
3
4
5

```Java
public static void main(String args[]) {
System.out.println("Hello, fucking world!");
}

分割线

连续输入三个*号就是分割线

1
2
3
4
5
***
分割线
***
分割线
***

分割线


分割线


图床配置

图床是指存储图片的服务器, 使用图床的好处是可以减轻博客服务器的负担, 加快图片打开的速度.

免费图床

可以使用路过图床等免费图床上传图片. 在网站注册完成之后, 就可以上传图片了. 上传完成之后, 直接将Markdown的链接放到自己的Markdown文件就可以了.

image-20200706152534392

Typora图片自动上传

一旦文章里面用到的图片很多时, 一张张上传到图床并手动复制到Markdown文件里面是一件很麻烦的事情. 在Windows平台上可以通过Typora搭配PicGo来实现自动上传. 也是就说, 只要把图片复制到Typora上, 就可以实现自动上传到图床了.

安装Typora和PicGo

  • Typora需要到官网下载安装, 需要0.9.86版本以上
  • PicGo在对应的PicGorelease页面下载安装, 需要2.2.2版本以上

PicGo默认支持的图床包括了SM.MS图床等, 如图所示.

image-20200706155636562

SM.MS图床和GitHub图床使用是免费的, 但是SM.MS图床稳定性较差, Github图床访问需翻墙.

略, 待补充.

绑定域名

略, 待补充.

更换主题

Hexo支持更换主题, 可以在官方的主题市场中下载自己喜欢的主题.

下面以NexT主题为例, 介绍如何安装主题.

站点配置文件和主题配置文件

在配置主题前, 要先了解什么是站点配置文件和主题配置文件.

在myBlog目录下, 也就是站点根目录下的_config.yml文件称作站点配置文件.

在myBlog\themes\主题名\目录下的的_config.yml文件称作主题配置文件

下载NexT主题

首先在此链接中下载Hexo的5.1.2版本.

然后将压缩包解压, 将解压后的文件名(hexo-theme-next-5.1.2)改为next. 并将整个文件夹复制到myBlog\themes目录下面.

image-20200706170951254

主题配置

打开站点配置文件, 找到theme字段, 将值更改为next

image-20200706171219355

NexT主题有三种Scheme:

  • Muse - 默认 Scheme,这是 NexT 最初的版本,黑白主调,大量留白
  • Mist - Muse 的紧凑版本,整洁有序的单栏外观
  • Pisces - 双栏 Scheme,小家碧玉似的清新
  • Gemini - 目前本网站使用的Scheme.

如果要切换Scheme的话, 需要打开主题配置文件, 找到Scheme字段, 在scheme:后面输入你喜欢的Scheme名称即可.

image-20200706173335341

设置语言

在站点配置文件中, 将language设置成你所需要的语言, 一般设置为简体中文.

1
language: zh-Hans

如果需要其他更详细的配置, 可以参照官方文档

<i class="fa fa-angle-left"></i>1234<i class="fa fa-angle-right"></i>
Kelvin

Kelvin

一个简易小站

32 日志
3 分类
23 标签
© 2020 Kelvin
由 Hexo 强力驱动
主题 - NexT.Gemini