【Week】No.178

C_01 有多少小于当前数字的数字

给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。

换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。

以数组形式返回答案。

输入:nums = [8,1,2,2,3]
输出:[4,0,1,1,3]
解释: 
对于 nums[0]=8 存在四个比它小的数字:(1223)。 
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。 
对于 nums[3]=2 存在一个比它小的数字:(1)。 
对于 nums[4]=3 存在三个比它小的数字:(122)。

方法一:排序

public int[] smallerNumbersThanCurrent1(int[] nums) {
  final int N = nums.length;
  int[] sortedArr = nums.clone();
  Arrays.sort(sortedArr);
  int[] arr = new int[N];
  HashMap<Integer, Integer> map = new HashMap<>();
  for (int i = 0; i < N; i++) {
    map.putIfAbsent(sortedArr[i], i);
  }

  for (int i = 0; i < N; i++) {
    arr[i] = map.get(nums[i]);
  }
  return arr;
}

复杂度分析

  • 时间复杂度: O ( N l o g N ) O(N logN)
  • 空间复杂度: O ( N ) O(N)

B_02 通过投票对团队排名

输入:votes = [“ABC”,“ACB”,“ABC”,“ACB”,“ACB”]
输出:“ACB”
解释:

  • A 队获得五票「排位第一」,没有其他队获得「排位第一」,所以 A 队排名第一。
  • B 队获得两票「排位第二」,三票「排位第三」。
  • C 队获得三票「排位第二」,两票「排位第三」。
    由于 C 队「排位第二」的票数较多,所以 C 队排第二,B 队排第三。

思想与算法

  • 预先统计不同团队 t e a m i team_i 的在不同名次上所得票数。
  • 按照题意排序:
    • 比较同一名次,按每个名次下的投票数降序排列。
    • 如果相同名次下的得到的投票数也相同,则以团队的字典序升序排列。

方法一:模拟(排序新技巧)

public String rankTeams(String[] votes) {
  if (votes.length == 1)  return votes[0];
  int teams = votes[0].length(); //参赛选手人数
  int ranks = teams;			 //排名的名次数
  int[][] voteCount = new int[26][teams]; //记录选手的每个名次投票的次数

  for (String vote : votes) {
    char[] s = vote.toCharArray();
    for (int i = 0; i < ranks; i++) {
      int teamId = s[i] - 'A';
      voteCount[teamId][i]++;   //队伍s[i]在排名i的数量
    }
  }

  Character[] S = new Character[teams];
  for (int i = 0; i < teams; i++) {
    S[i] = votes[0].charAt(i);
  }

  Arrays.sort(S, new Comparator<Character>() {
    @Override
    public int compare(Character c1, Character c2) {
      //按每个名次下的投票数降序排列
      for (int i = 0; i < ranks; i++) {
        int teamId1 = c1-'A';
        int teamId2 = c2-'A';
        //按投票数降序排列
        if (voteCount[teamId1][i] != voteCount[teamId2][i]) {
          return voteCount[teamId2][i] - voteCount[teamId1][i];
        }
      }
      //如果得到的投票数相同,则以字典序排列
      return c1 - c2;
    }
  });

  char[] res = new char[teams];
  for (int i = 0; i < teams; i++) {
    res[i] = S[i];
  }
  return String.valueOf(res);
}

复杂度分析

  • 时间复杂度: O ( N 2 ) O(N^2)
  • 空间复杂度: O ( N ) O(N)

B_03 二叉树中的列表

给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。

如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。

一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。

输入:head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
输出:true
解释:树中蓝色的节点构成了与链表对应的子路径。

方法一:层次遍历 + dfs

  • 层次遍历所有结点,对每一个可匹配 tNode.val == head.val 的结点进行 dfs 深搜。
  • dfs 过程中,可以采用递归套路:
    • 结束条件
      • 如果链表走到空,则表明存在二叉树该子结构。
      • 如果链表没空,但是二叉树走到空了,证明从某一结点开始的深搜的路径是不匹配的,返回 false 即可.
    • 本层递归的责任
      • 寻找下一个可匹配的结点(即链表和二叉树的下一个结点)。
private boolean dfs(ListNode head, TreeNode root) {
  if (head == null) return true;  //链表到头
  if (root == null) return false; //树某一条分支遍历完成
  if (head.val != root.val) return false;
  return dfs(head.next, root.left) || dfs(head.next, root.right);
}
public boolean isSubPath(ListNode head, TreeNode root) {

  if (head == null) return true;
  if (root == null) return false;

  LinkedList<TreeNode> q = new LinkedList<>();
  q.add(root);

  while (!q.isEmpty()) {
    TreeNode tNode = q.poll();
    if (tNode.val == head.val) {
      if (dfs(head, tNode) == true) 
      	return true;
    }
    if (tNode.left != null)  q.add(tNode.left);
    if (tNode.right != null) q.add(tNode.right);
  }
  return false;
}

复杂度分析

  • 时间复杂度: O ( N 2 ) O(N^2) ,N 为二叉树的结点个数。
  • 空间复杂度: O ( N ) O(N)

方法二:前序遍历 + dfs

递归三部曲:

  • 结束条件
    • 如果链表走到空,则表明存在二叉树该子结构。
    • 如果链表没空,但是二叉树走到空了,证明从某一结点开始的深搜的路径是不匹配的,返回 false 即可.
    • 如果 dfs 过程中,遇到不匹配代表从某结点开始的路径是不匹配的,直接返回 false.
  • 路径匹配
    • root.val != head.val 时,head 不变,继续判断左右子树是否存在可与 head 匹配的结点。
    • root.val == head.val 时,继续尝试匹配左右子树,head 应变为 head.next.
private boolean dfs1(ListNode head, TreeNode root) {
  if (head == null) return true;
  if (root == null) return false;
  if (head.val != root.val) 
  	return false;
  return dfs1(head.next, root.left) || dfs1(head.next, root.right);
}
/**
 * @thought:dfs    
 * @date: 3/1/2020 9:01 PM
 * @Execution info:2ms,100%,%
 */
public boolean isSubPath1(ListNode head, TreeNode root) {

  if (head == null) return true;
  if (root == null) return false;
  //匹配则继续从树的该结点深搜,尝试匹配链表的下一个结点
  if (head.val == root.val) {
    if (dfs1(head.next, root.left) || dfs1(head.next, root.right))
      return true;
  }
  //匹配不上,链表应该静止不变,继续递归左右子树
  return isSubPath(head, root.left) || isSubPath(head, root.right);
}

A_04 使网格图至少有一条有效路径的最小代价(没做)

发布了461 篇原创文章 · 获赞 102 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/104597899