二叉树遍历
先序(非递归) 先访问root节点,再left子树,再right子树
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
}
先序(递归)
private void traversal(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
result.add(root.val);
traversal(root.left, result);
traversal(root.right, result);
}
61.旋转链表
题解:闭合为环 链表长度n,右移k步,最后节点是:(n-1 - (k mod n)),索引从0开始 算法: 1 遍历,求得链表长度n 2 移动到last节点,根据公式(n-1 - (k mod n)), 3 在last节点断开环
31 下一个排列
题解:两边扫描
算法:
1 从后向前寻找第一个顺序对 (i,i+1),满足a[i]<a[i+1],a[i]即较小数;
2 从后向前遍历[i+1, n),寻找第一个j,满足a[i] <a[j],a[j]即较大数;
3 swap a[i] a[j],此时[i+1, n)是降序,使用双指针翻转区间[i+1, n),使其变为升序reverse(nums.begin() + i + 1, nums.end()); // 双指针翻转区间[i+1, n)
199. 二叉树右视图
dfs递归遍历,使用depth变量
void dfs(TreeNode *root, int depth, vector<int> &res) {
// return
if (root == nullptr) {
return;
}
if (depth == res.size()) { // 每层访问到的第一个节点,一定是最右边节点
res.push_back(root->val);
}
dfs(root->right, depth + 1, res);
dfs(root->left, depth + 1, res);
}
170. 交易逆序对总数
题解: 准备知识归并排序: www.hello-algo.com/chapter_sor…
求逆序对和归并排序什么关系呢?归并排序过程“并”的步骤,在lptr移动过程中,j表示右区间指针,如果lptr指向的数比rptr数小,(j - (mid+1))表示 逆序对贡献。
算法:归并排序
int mergeSort(vector<int> &record, vector<int> &tmp, int l, int r) {
if (l >= r) { //返回条件
return 0;
}
int mid = (l + r) / 2;
int inv_count = mergeSort(record, tmp, l, mid) + mergeSort(record, tmp, mid + 1, r); // 左右子区间已排好序,并返回子区间内逆序对数。
int i = l, j = mid + 1, pos = l;
while (i <= mid && j <= r) {
if (record[i] <= record[j]) {
tmp[pos] = record[i];
++i;
inv_count += (j - (mid+1)); // key point!!!
} else {
tmp[pos] = record[j];
++j;
}
pos++;
}
for (int k =i; k<= mid; k++) {
tmp[pos++] = record[k];
inv_count += (j - (mid+1));
}
for (int k =j; k<= r; k++) {
tmp[pos++] = record[k];
}
copy(tmp.begin() + l, tmp.begin()+r+1, record.begin() + l);
return inv_count;
}
315 计算右侧小于当前元素的个数
题解:与170 交易逆序对总数是同类题 归并排序
105. 从前序与中序遍历序列构造二叉树
题解:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
210 课程表
拓扑序
对于图 G 中的任意一条有向边 (u,v),u 在排列中都出现在 v 的前面。 题解:bfs
可以将本题建模成一个求拓扑排序的问题:
- 我们将每一门课看成一个节点;
- 如果想要学习课程 A 之前必须完成课程 B,那么我们从 B 到 A 连接一条有向边。这样以来,在拓扑排序中,B 一定出现在 A 的前面。 问题解转化为 求图G的拓扑序。
算法:
- 取出队首节点u,
- 移除u的所有出边,即u的相邻节点入度减一,如果存在相邻节点v入度变为0,即加入队列
遍历完毕,判断是否得到包含所有节点的排列,否则返回空。
// 存储有向图
vector<vector<int>> edges;
// 存储每个节点的入度
vector<int> indeg;
// 存储答案
vector<int> result;