78.给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同
有想法,为空返回空,接下来就是上一个+上一个的组合里加新元素的所有。实现起来很困难。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> temp=new ArrayList<>();
//先放一个空进来
res.add(temp);
for(int i=0;i<nums.length;i++){
//因为内层循环一直在给res做添加,所以必须保存内层循环开始前的尺寸。
int k=res.size();
for(int j=0;j<k;j++){
temp=res.get(j);
//为什么这里一定要新建一个temp1?add方法的返回值是一个对象的引用,不是一个简单的值!!!
List<Integer> temp1=new ArrayList<>(temp);
temp1.add(nums[i]);
res.add(temp1);
}
}
return res;
}
}
答案的递归法:
class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
dfs(0, nums);
return ans;
}
public void dfs(int cur, int[] nums) {
if (cur == nums.length) {
ans.add(new ArrayList<Integer>(t));
return;
}
t.add(nums[cur]);
dfs(cur + 1, nums);
t.remove(t.size() - 1);
dfs(cur + 1, nums);
}
}
1.动态规划
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> initSub=new ArrayList<>();
res.add(initSub);//加一个null
for (int i = 0; i < nums.length; i++) {
int time=res.size();
for (int j = 0; j < time; j++) {
List<Integer> list=res.get(j);
List<Integer> sub=new ArrayList<>(list);
sub.add(nums[i]);
res.add(sub);
}
}
return res;
}
}
461.两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
示例 1:
输入:x = 1, y = 4
输出:2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
示例 2:输入:x = 3, y = 1
输出:1
提示:
0 <= x, y <= 231 - 1
class Solution {
public int hammingDistance(int x, int y) {
int res=x^y;
int num=0;
while(res!=0){
int temp=res&1;
num+=temp;
res=res>>1;
}
return num;
}
}
94. 给定一个二叉树的根节点 root ,返回它的 中序 遍历。示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:输入:root = []
输出:[]
示例 3:输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[2,1]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100
一开始执迷于把链表拿到复制,后来想起可以自己写一个带链表的方法。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
inorder(root,list);
return list;
}
public static void inorder(TreeNode root,List<Integer> res){
if(root==null){
res=null;
return;
}
if(root.left!=null){
inorder(root.left,res);
}
res.add(root.val);
if(root.right!=null){
inorder(root.right,res);
}
}
}
1.迭代-迭代的本质是维护一个空间复杂度为n的栈。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
//迭代法,维护一个队列存储结果
Deque<TreeNode> deque=new LinkedList<>();
while(root!=null||!deque.isEmpty()){
while(root!=null){
deque.push(root);
root=root.left;
}
root=deque.pop();
list.add(root.val);
//
root=root.right;
}
return list;
}
}
2.Morris中序遍历-莫里斯遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
TreeNode predecessor = null;
while(root!=null){
if(root.left!=null){
//向左走一步
predecessor=root.left;
while(predecessor.right!=null){
//走到左子树的最右节点
predecessor=predecessor.right;
}
//让根节点称为最右节点的右节点,并剪断根节点的左树
predecessor.right=root;
TreeNode temp=root.left;
root.left=null;
root=temp;
}else{
//现在左边的树已经完全变成了一个链表,只有右节点了,
//所以往右走,如果后面又遇到了有左节点的节点,往左走一步,继续噶右树,类似递归
res.add(root.val);
root=root.right;
}
}
return res;
}
}
226.给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
示例 2:输入:root = [2,1,3]
输出:[2,3,1]
示例 3:输入:root = []
输出:[]
提示:
树中节点数目范围在 [0, 100] 内
-100 <= Node.val <= 100
递归就完事了
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return null;
}
TreeNode temp1=invertTree(root.left);
TreeNode temp2=invertTree(root.right);
root.left=temp2;
root.right=temp1;
return root;
}
}
想想迭代怎么做。
class Solution {
public TreeNode invertTree(TreeNode root) {
//递归终止条件
if(root == null) {
return null;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
//当前节点出栈
TreeNode node = stack.pop();
//将当前节点的左右子树交换
TreeNode tmp = node.right;
node.right = node.left;
node.left = tmp;
//右子树入栈
if(node.right != null) {
stack.push(node.right);
}
//左子树入栈
if(node.left != null) {
stack.push(node.left);
}
}
return root;
}
}
96.给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3
输出:5
示例 2:输入:n = 1
输出:1
提示:
1 <= n <= 19
1.动态规划
class Solution {
public int numTrees(int n) {
int[] G=new int[n+1];
G[0]=1;
G[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
G[i]+=G[j-1]*G[i-j];
}
}
return G[n];
}
}
2.数学公式法
class Solution {
public int numTrees(int n) {
// 提示:我们在这里需要用 long 类型防止计算过程中的溢出
long C = 1;
for (int i = 0; i < n; ++i) {
C = C * 2 * (2 * i + 1) / (i + 2);
}
return (int) C;
}
}
101.给你一个二叉树的根节点 root , 检查它是否轴对称。
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false
提示:
树中节点数目在范围 [1, 1000] 内
-100 <= Node.val <= 100
进阶:你可以运用递归和迭代两种方法解决这个问题吗?
1.递归 -一开始就想到这个方法了,甚至想到了用两个根节点,但是把问题想复杂了,老是去判断子节点,应该直接判断根节点。
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root,root);
}
public boolean check(TreeNode p,TreeNode q){
if(p==null&&q==null){
return true;
}
if(p==null||q==null){
return false;
}
return (p.val==q.val)&&check(p.left,q.right)&&check(p.right,q.left);
}
}
2.迭代-使用到队列和两个方法,offer和poll方法,其作用类似于add和remove,但是不会抛出异常,只会返回false和null。
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root,root);
}
public boolean check(TreeNode p,TreeNode q){
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(p);
queue.offer(q);
while(!queue.isEmpty()){
TreeNode temp1=queue.poll();
TreeNode temp2=queue.poll();
if(temp1==null&&temp2==null){
continue;
}
if(temp1==null||temp2==null){
return false;
}
if(temp1.val!=temp2.val){
return false;
}
queue.offer(temp1.left);
queue.offer(temp2.right);
queue.offer(temp1.right);
queue.offer(temp2.left);
}
return true;
}
}
48.给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
提示:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
1.先上下翻,再对角线翻。
class Solution {
public void rotate(int[][] matrix) {
int x=matrix.length;
int temp;
//左右翻一次
for(int i=0;i<(x/2);i++){
for(int j=0;j<x;j++){
temp=matrix[i][j];
matrix[i][j]=matrix[x-1-i][j];
matrix[x-1-i][j]=temp;
}
}
//再沿对角线来一次
for(int i=0;i<x;i++){
for(int j=0;j<i;j++){
temp=matrix[i][j];
matrix[i][j]=matrix[j][i];
matrix[j][i]=temp;
}
}
}
}
2.使用辅助数组-平平无奇
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
int[][] matrix_new = new int[n][n];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
matrix_new[j][n - i - 1] = matrix[i][j];
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
matrix[i][j] = matrix_new[i][j];
}
}
}
}
3.原地翻转-一直转反了方向emo了
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
}
46.给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
和那个子集很像,于是自己尝试写了一下,差点忘记add这个坏东西。
class Solution {
public List<List<Integer>> permute(int[] nums) {
//回溯
List<List<Integer>> res=new ArrayList<>();
List<Integer> temp=new ArrayList<>();
//找到nums长度-1的数组的结果,假设一共k个数组
//然后把nums[i]插到k个数组的所有位置,一共nums.length个位置
//每次用temp存储插好的数组,插一次就往res里加一次
//返回最终结果
if(nums.length==1){
temp.add(nums[0]);
res.add(temp);
return res;
}
int[] pre=new int[nums.length-1];
for(int i=0;i<pre.length;i++){
pre[i]=nums[i];
}
List<List<Integer>> preRes=permute(pre);
for(int i=0;i<preRes.size();i++){
for(int j=0;j<nums.length;j++){
temp=preRes.get(i);
List<Integer> temp1=new ArrayList<>(temp);
temp1.add(j,nums[nums.length-1]);
res.add(temp1);
}
}
return res;
}
}
虽然一会忘记add,一会忘记写递归结束条件,一会写错条件,但最后靠自己写出来,挺高兴,说明子集那个题目也学会了。
104.给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
一个是递归,看左右子树的深度最大值再加1;一个是广度优先搜索,感觉和那个轴对称类似,使用队列来存进来的节点。
1.递归
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}else{
int leftDepth=maxDepth(root.left);
int rightDepth=maxDepth(root.right);
return Math.max(leftDepth,rightDepth)+1;
}
}
}
2.广度优先搜索树
差点new queue去了,记得底层用链表。
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> queue=new LinkedList<>();
if(root==null){
return 0;
}
queue.offer(root);
int num=0;
//只要队列不为空,就说明没处理完所有节点
while(!queue.isEmpty()){
int size=queue.size();
//类似递归的思想,来遍历树
while(size>0){
TreeNode temp=queue.poll();
if(temp.left!=null){
queue.offer(temp.left);
}
if(temp.right!=null){
queue.offer(temp.right);
}
size--;
}
num++;
}
return num;
}
}
102.给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:输入:root = [1]
输出:[[1]]
示例 3:输入:root = []
输出:[]
提示:
树中节点数目在范围 [0, 2000] 内
-1000 <= Node.val <= 1000
感觉类似前几个题目,用队列存储广度优先搜索。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> temp=new ArrayList<>();
TreeNode curr=root;
if(curr==null){
return res;
}
//根节点那一层
temp.add(curr.val);
List<Integer> temp1=new ArrayList<>(temp);
res.add(temp1);
//下一层
//还得是队列
Queue<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size=q.size();
List<Integer> l=new ArrayList<>();
while(size>0){
TreeNode treeNode=q.poll();
if(treeNode.left!=null){
q.offer(treeNode.left);
l.add(treeNode.left.val);
}
if(treeNode.right!=null){
q.offer(treeNode.right);
l.add(treeNode.right.val);
}
size--;
}
//这一层遍历完了
List<Integer> r=new ArrayList<>(l);
if(r.size()>0){
res.add(r);
}
}
return res;
}
}
答案也是广度优先搜索,不过略有不同。
75.给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:输入:nums = [2,0,1]
输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
本质上是要写一个含重复数字的排序算法,要求空间复杂度为O(1),时间复杂度为O(n)。
1.双指针搞一哈-应该有两种解法
class Solution {
public void sortColors(int[] nums) {
//双指针,指向头部
//遍历一次,如果遇到0,一起移动,遇到1,只有right移动
int left=0;
int right=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==0){
int temp=nums[i];
nums[i]=nums[left];
nums[left]=temp;
//
if(left<right){
int temp1=nums[i];
nums[i]=nums[right];
nums[right]=temp1;
}
left++;
right++;
}else if(nums[i]==1){
int temp=nums[i];
nums[i]=nums[right];
nums[right]=temp;
right++;
}
}
}
}
如果left和right之间有1了,那么这个0就和1交换了,所以需要在换一次。如果left和right一直相等,那就随便换。
class Solution {
public void sortColors(int[] nums) {
//双指针,一个指向头部,一个指向尾部
//0放前面,2放后面
int left=0;
int right=nums.length-1;
for(int i=0;i<nums.length;i++){
//如果交换的也是个2,且i没有大过right,不然会多交换
while(i<right&&nums[i]==2){
int temp=nums[i];
nums[i]=nums[right];
nums[right]=temp;
right--;
}
if(nums[i]==0){
int temp=nums[i];
nums[i]=nums[left];
nums[left]=temp;
left++;
}
}
}
}
2.单指针遍历两遍,第一遍把所有0放前面,第二遍把所有1放0后面。
class Solution {
public void sortColors(int[] nums) {
int left=0;
//单指针遍历两次
//第一次先将所有的0放到数组前面
//第二次将所有的1放在0后面
for(int i=0;i<nums.length;i++){
if(nums[i]==0){
int temp=nums[i];
nums[i]=nums[left];
nums[left]=temp;
left++;
}
}
//此时left指向最后一个0的下一个
for(int i=left;i<nums.length;i++){
if(nums[i]==1){
int temp=nums[i];
nums[i]=nums[left];
nums[left]=temp;
left++;
}
}
}
}