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 存在四个比它小的数字:(1,2,2 和 3)。
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。
对于 nums[3]=2 存在一个比它小的数字:(1)。
对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
方法一:排序
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;
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
B_02 通过投票对团队排名
输入:votes = [“ABC”,“ACB”,“ABC”,“ACB”,“ACB”]
输出:“ACB”
解释:
- A 队获得五票「排位第一」,没有其他队获得「排位第一」,所以 A 队排名第一。
- B 队获得两票「排位第二」,三票「排位第三」。
- C 队获得三票「排位第二」,两票「排位第三」。
由于 C 队「排位第二」的票数较多,所以 C 队排第二,B 队排第三。
思想与算法
- 预先统计不同团队 的在不同名次上所得票数。
- 按照题意排序:
- 比较同一名次,按每个名次下的投票数降序排列。
- 如果相同名次下的得到的投票数也相同,则以团队的字典序升序排列。
方法一:模拟(排序新技巧)
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);
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
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;
}
复杂度分析
- 时间复杂度: ,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);
}