力扣爆刷第172天之TOP200五连刷126-130(逆序对、三数之和、组合总和)
文章目录
一、LCR 170. 交易逆序对的总数
题目链接:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/description/
思路:求数组中逆序对的总数,要求如下:
示例 1:
输入:record = [9, 7, 5, 4, 6]
输出:8
解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。
1、题目很简单就是让求逆序对的个数,正常来说如何测试用例的量不大,直接双层循环暴力就能做,但是本题数组长度都到5万了,o(n2)属实是太大了,过不了。
2、那有没有时间复杂度低一些的方法呢?其实提起逆序对的时候就应该想起来排序,排序就是遇到逆序对进行交换,变成正序的,而在排序算法中的归并排序正好可以边排序,边计算逆序对。
3、归并排序如何计算逆序对?归并的思想是先分治,再合并。分治区间为nums[left, mid] nums[mid+1, right],合并时,如果是逆序,逆序的个数为 mid - i + 1,所以利用这个特性,直接写归并排序即可。
class Solution {
int[] tmp;
public int reversePairs(int[] record) {
tmp = new int[record.length];
return merge(record, 0, record.length-1);
}
public int merge(int[] record, int left, int right) {
if(left >= right) return 0;
int mid = left + (right - left) / 2;
int res = merge(record, left, mid) + merge(record, mid+1, right);
int i = left, j = mid+1;
for(int k = left; k <= right; k++) tmp[k] = record[k];
for(int k = left; k <= right; k++) {
if(i == mid + 1) {
record[k] = tmp[j++];
}else if(j == right + 1 || tmp[i] <= tmp[j]) {
record[k] = tmp[i++];
}else{
res += mid - i + 1;
record[k] = tmp[j++];
}
}
return res;
}
}
二、16. 最接近的三数之和
题目链接:https://leetcode.cn/problems/3sum-closest/description/
思路:求最接近的三数之和,本题让从数组中找出三个数,然后和最接近给出的target,也是很经典的题目。
1、首先明确一点,三数之和、四数之和都是用双指针之类的思想,两数之和用的是哈希表map。
2、三数之和知道了使用双指针,但是还有一个前提,是给数组排序,排序之后应用双指针,这样可以凭借三数之和与目标和的大小移动左右指针去寻找最合适的结果。
3、如何寻找最接近的和,只需要在寻找过程中记录下来三数之和与目标和的最小距离(绝对值距离),最小距离只要更新就更新记录的三数之和。
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int min = Integer.MAX_VALUE, res = 0;
for(int i = 0; i < nums.length-2; i++) {
int j = i + 1, k = nums.length-1;
while(j < k) {
int temp = nums[i] + nums[j] + nums[k];
if(Math.abs(temp - target) < min) {
min = Math.abs(temp - target);
res = temp;
}
if(temp == target) return target;
else if(temp < target) j++;
else k--;
}
}
return res;
}
}
三、145. 二叉树的后序遍历
题目链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/description/
思路:这个没事好说的,就是单纯的先遍历左子树再遍历右子树,最后遍历自己。
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
traverse(root);
return list;
}
void traverse(TreeNode root) {
if(root == null) return ;
traverse(root.left);
traverse(root.right);
list.add(root.val);
}
}
四、50. Pow(x, n)
题目链接:https://leetcode.cn/problems/powx-n/description/
思路:本题让模拟幂函数,可以从快速幂的角度也就是分治的角度来考虑,2的4次方,为2 * 2 = 4,4 * 4= 16,两步就完成了计算,如果遍历计算需要222*2,计算3次,也就是分治可以达到log(N)的时间复杂度,具体来说,只需要把负幂数转化为正幂数,然后考虑奇偶情况即可。
class Solution {
public double myPow(double x, int n) {
if(x == 0.0f) return 0.0f;
long k = n;
double res = 1.0d;
if(k < 0) {
x = 1.0 / x;
k = -k;
}
while(k > 0) {
if((k & 1) == 1) res *= x;
x *= x;
k >>= 1;
}
return res;
}
}
五、40. 组合总和 II
题目链接:https://leetcode.cn/problems/combination-sum-ii/description/
思路:说一下本题的情况,即元素有重,不可复用,但是要求结果集不可重复。
所以,要确定两点。
1、组合,需要指定递归起始位置。
2、元素有重,不可复用,需要横向去重。
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTracking(candidates, 0, target);
return res;
}
void backTracking(int[] candidates, int index, int target) {
if(sum == target) {
res.add(new ArrayList(list));
return;
}
for(int i = index; i < candidates.length && sum + candidates[i] <= target; i++) {
if(i > index && candidates[i] == candidates[i-1]) continue;
sum += candidates[i];
list.add(candidates[i]);
backTracking(candidates, i+1, target);
sum -= candidates[i];
list.remove(list.size()-1);
}
}
}