力扣刷题7- 二叉树、旋转链表、逆序对、课程表

50 阅读4分钟
二叉树遍历

先序(非递归) 先访问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;