LeetcodePracticeJava(一)hotcode_rank100_easy
算法orTips记录
SumUpOfTwoNumbers001
import java.util.HashMap;
import java.util.Map;
class Solution {
public static int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<>();
//新建hashmap
//HashMap<Integer,Integer>类型名,两个变量为整形
//map为变量名,new为动态申请
int[] result = new int[2];
//用于保存结果,长度为2的数组
for (int i = 0; i <nums.length ; i++) {
//一次循环,时间复杂度O(n)
if(map.containsKey(nums[i])){
//判断条件:输入中的第i个,作为键,在map中有对应的值
//map.containsKey()返回true or false,代表在map中是否有key存在
result[0] = map.get(nums[i]);
//很明显,put是存,get是取,既返回键对应的值,get到的是对于num[i]的目标值所在的位置
result[1] = i;
//i是当前已知值的位置
return result;
}
map.put(target-nums[i],i);
//存入的键值对 目标值:已知值的位置,这个很关键,虽然coding时思维多一圈,但是加速了程序
//也可以理解成 已知值:目标值的位置,这个操作有点意思
//Map.put() 方法将获取 Map 集合的所有键名,并存放在一个 Set 集合对象中;存入
}
return result;
}
}
破题要点:
哈希表,对撞
键值对,把位置序号 i 作为值来存放
多想一层,补数的概念
mark:
这样只能输出一组答案
EffectiveBrackets020
使用栈的进出判断(low)
public class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
//Character 类用于对单个字符进行操作。
//Character 类在对象中包装一个基本类型 char 的值
for(int i = 0; i < s.length(); i++) {
Character character1 = s.charAt(i);
if(character1 == '(' || character1 == '[' || character1 == '{') {
stack.push(character1);
}else if(character1 == ')') {
if(stack.isEmpty()) {
return false;
}
Character character2 = stack.pop();
if(character2 != '(') {
return false;
}
}else if(character1 == ']') {
if(stack.isEmpty()) {
return false;
}
Character character2 = stack.pop();
if(character2 != '[') {
return false;
}
}else if(character1 == '}') {
if(stack.isEmpty()) {
return false;
}
Character character2 = stack.pop();
if(character2 != '{') {
return false;
}
}
}
return stack.isEmpty();
}
}
基于字符翻译或大小的逻辑判断
待完善
[ 的下一位只能是( or ]
( 28
) 29
[ 91
] 93
{ 123
} 125
用哈希表,键值对,分别赋值123456
那么原string就变成了一个数组
eg:()]]{{[()]}>12445531246
so 从数字逻辑上,1后面只能是2,3后面只能是1.4,5后面只能是1.3.6
and 2前面必须是1,4前面必须是2.3,6前面必须是2.4.5
package hotcode_rank100;
import java.util.HashMap;
public class EffectiveBrackets020 {
public static void main(String[] args) {
System.out.println( isValid (")"));
}
public static boolean isValid(String s) {
HashMap<Character, Integer> map = new HashMap<>();
map.put('(', 1);
map.put(')', 2);
map.put('[', 3);
map.put(']', 4);
map.put('{', 5);
map.put('}', 6);
int l = s.length();
int []arr = new int[ l+1 ];
for (int j = 0; j < l; j++){
arr[j] = map.get(s.charAt(j));
arr[j+1]=0;
}
int i = 0;
while (i<l) {
if (arr[i] % 2 == 1 || i==0) {
if (arr[i + 1] == arr[i] + 1 || arr[i + 1] == arr[i] - 2 || arr[i + 1] == arr[i] - 4) {
i++;
} else {
return false;
}
}
else {
if (arr[i - 1] == arr[i] - 1 || arr[i - 1] == arr[i] - 2 || arr[i - 1] == arr[i] - 4) {
i++;
} else {
return false;
}
}
}
return true;
}
}
弃了,判断条件和字符转换过于复杂;由于涉及太多的arr[i+1]、arr[i-1],在Java中并不好处理,还是乖乖用栈解决吧
利用ascll码进行判断
class Solution {
public:
bool isValid(string s) {
int j = 0;
string temp;
int len = s.size();
temp += s[0];
if (s[0] == '(' || s[0] == '[' || s[0] == '{'){
for (int i = 1;i < len;i++){
if ((s[i] == temp[j] + 1) || (s[i] == temp[j] + 2)){
temp.erase (temp.end()-1);
j--;
}
else {
temp += s[i];
j++;
}
}
if (temp.empty())
return true;
else return false;
}
else{
if (s == "")
return true;
else return false;
}
}
};
使用栈的最快解法
package hotcode_rank100;
import java.util.Stack;
public class EffectiveBrackets020_2 {
public static void main(String[] args) {
System.out.println(isValid("{[()]"));
}
public static boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.isEmpty() || stack.pop() != c)
return false;
}
return stack.isEmpty();
}
}
还是之前的大哥,往栈里输入的是补数!!
然后取出来和c比较就行了,简单快捷,目前来说发现的行数最少的代码
栈的特性,先进后出,正好匹配到三级括号的需求
Stack<Character> stack = new Stack<Character>();
stack.push()//入栈
stack.pop()//出栈
//强调出入顺序的问题
MergeOrderedList021
可以使用,递归或者迭代
实现归并排序的子过程
归并排序
是创建在归并操作上的一种有效的排序算法。采用分治法,各层分治递归可以同时进行。
归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
迭代法
代码冗余问题,cur1 cur2没太必要?直接对l1 l2操作即可
https://www.cnblogs.com/easyidea/p/13371863.html
public class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//三个指针,分别指向L1、L2、dummyHead的头结点
ListNode cur1 = l1;
ListNode cur2 = l2;
ListNode dummyHead = new ListNode(0);
//为新的链表设置虚拟头结点,节点值为0(anyway输出的时候也不管他)
ListNode cur = dummyHead;
//在两个链表任一不为空的情况下,执行循环
while(cur1 != null || cur2 != null) {
if(cur1 == null) {
cur.next = cur2;//若L1空了,则将L2接在结果后(指针cur2连接cur.next>cur2.next)
cur2 = cur2.next;
}else if(cur2 == null) {
cur.next = cur1;//若L2空了,则将L1接在结果后(指针cur1连接cur.next>cur1.next)
cur1 = cur1.next;
}else if(cur1 != null && cur2 != null) {
//当cur1 cur2都不空
if(cur1.val > cur2.val) {
cur.next = cur2;//将cur2的当前值给到cur.next
cur2 = cur2.next;//将指针cur2,向后一位,也就形成了遍历
}else {
cur.next = cur1;
cur1 = cur1.next;
}
}
cur = cur.next;//将上面if中得到的cur.next接到cur后面,然后继续循环
}
return dummyHead.next;//返回头结点之后的部分
}
}
递归法
public class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}
if(l1.val > l2.val) {//如果l2小,那么这个值在前面,所以接下来需要合并的链表就是l1和l2.next
l2.next = mergeTwoLists(l1, l2.next);//标准递归,调用自己
return l2;
}else {
l1.next = mergeTwoLists(l1.next, l2);//当l1的值小于等于l2的值时,我们令l1指向合并好l1.next和l2的结果,并返回l1
return l1;
//也就是说最后,l1和l2,一个为空,一个为合并好的结果
//合并结果在l1.next和l2.next前反复横跳
}
}
}
MaxSubArray053
有推荐用分治算法待研究
我的想法:
从第一个位置开始,判断正负零,[0]+[1],大于0则继续向后加,当sum<=0时,直接跳到下一个位置
判断正负零时,正开始计算,零负跳过当前位置
需要一个sum保存历史最大值,一个sum1保存实时计算结果
当sum1>sum,更新sum
当某个位置的值大于sum,更新(这其实是上述条件的特殊情况,不用单独语句实现)
package hotcode_rank100;
public class MaxSubArray053 {
public static void main(String[] args) {
int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
System.out.println(maxSubArray(nums));//
System.out.println(maxSubArray2(nums));//分治算法
}
public static int maxSubArray(int[] nums) {
// dp[i] 表示以nums[i]结尾的最大和
int[] dp = new int[nums.length];
dp[0] = nums[0];
int res = dp[0];
for (int i = 1; i <nums.length ; i++) {
dp[i] = dp[i-1]>0 ? dp[i-1]+nums[i] : nums[i];
//?为三元运算符,若前式成立,返回冒号左侧值,这一行为关键逻辑,这种写法足够简单
//思路为,建立一个数组,来记录过程中产生的历史sum,便于下一次循环时取出来比较
//利用一个整型变量,一个if语句,配合continue也可以实现
res = Math.max(res,dp[i]);
}
return res;
}
public static int maxSubArray2(int[] nums) {
return DivideConquer(nums,0,nums.length-1);
}
public static int DivideConquer(int[] nums, int start, int end){
if(start == end)
return nums[start];
else{
int mid = (start+end)/2;
int left = DivideConquer(nums, start, mid);
int right = DivideConquer(nums, mid+1, end);
int temp = 0;
int lmax = nums[mid];
for (int i = mid; i >=start ; i--) {
temp += nums[i];
lmax = Math.max(temp,lmax);
}
temp = 0;
int rmax = nums[mid+1];
for (int i = mid+1; i <= end ; i++) {
temp += nums[i];
rmax = Math.max(temp,rmax);
}
return Math.max(Math.max(left,right),lmax+rmax);
}
}
//在线算法,很巧的是和我之前的想法很像
public int maxSubArray3(int[] nums) {
int n = nums.length;
int result = Integer.MIN_VALUE;
//初始化result,为能去得的最小数,防止越界,看起来很唬人,但只要比最大和小,就不会有bug,但这样更保险
int sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
result = Math.max(result, sum);
if(sum < 0) {
sum = 0;
//精华所在,置零代表着上一个子序列,不参与考虑了,只要result还在,从下一个元素重新从头考虑就行了
}
}
return result;
}
}
ClimbStairs070
public class lc70 {
public static void main(String[] args) {
System.out.println(climbStairs(3));
}
public static int climbStairs(int n) {
int[] re = new int[n+1];
//n个有效值,这样是为了让n级台阶和re[n]相对应
re[0] = 1;
re[1] = 1;
for (int i = 2; i <= n ; i++ ){
re[i] = re[i-1] + re[i-2];
//在面对n级台阶时,先迈一步,当前step跨1级,则剩余走法re[n-1]
//同理,当前step跨越2级,则剩余走法re[n-2]
}
return re[n];
}
}
IsSymmetric101
public class IsSymmetric101 {
public static void main(String[] args) {
//
}
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
//从这里开始是有用的代码,大佬的思路和写法都太强了
//唯一的就是,如果用行数衡量工作量,这样岂不是很吃亏[滑稽]
public boolean isSymmetric(TreeNode root) {
//构造函数,输入树的根节点
return root==null || func(root.left, root.right);
//根为空or根的左右子树满足条件,为true,进入递归判断func
}
//条件功能func,输入左右子树
public static boolean func(TreeNode left, TreeNode right){
if(left==null || right==null)
//如果左树or右树为空,则判断是否相等,返回true、false
return left == right;
//在左右都不为空的情况下,有以下三个必须同时成立的条件
//左右树的值相等且左左和右右、左右和右左满足func成立条件(递归调用自己)
return (left.val==right.val) && func(left.left,right.right) && func(left.right,right.left);
}
}
maxDepth104
public class lc104 {
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
//递归,超简单
public int maxDepth(TreeNode root) {
if(root==null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
MaxProfit121
低价策略
public int maxProfit(int[] prices) {
//初始化,最小值为max,防止越界,res置零
int min = Integer.MAX_VALUE, res=0;
//遍历所有位置的价格
for(int i=0; i<prices.length; i++){
min = Math.min(min, prices[i]);//在当前价格和历史低价中寻找小值作为买入点
res = Math.max(res, prices[i]-min);
//当前价格与历史低价的差,作为假设利润,与之前的假设利润比较,取其中大值作为最后的maxprofit
}
return res;
}
动态规划
public class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if(prices.length == 0){
return result;
}
//新建一个数组 0到len-1
int[] dp = new int[prices.length];
dp[0] = Integer.MAX_VALUE;
//对于每一个i天,当天价格prices[i-1],今天之前的历史低价dp[i-1]
for(int i = 1; i < dp.length; i++){
if(prices[i - 1] < dp[i - 1]){
dp[i] = prices[i - 1];
}else{
//若今天的价格高于历史价格,那么今天的历史低价,和前一天一样
dp[i] = dp[i - 1];
}
}
for(int i = 1; i < dp.length; i++){
//顺序拿出历史低价,用今日价格减历史低价,得到利润,re寻找其中最大值
//因为第一天不能又买又卖,所以不用考虑i=0,i=1代表第二天,i=len-1代表最后一天
//如果使用i-1,在第一天时,if不成立,只要把循环条件改成<=,i=len时,访问位置i-1,也是没有错的
if(prices[i] - dp[i] > result){
result = prices[i] - dp[i];
}
}
return result;
}
}
SingleNumber136
思路1:栈?类似括号那题
思路2:按位异或
public static int singleNumber(int[] nums) {
int res = nums[0];
for (int i = 1; i < nums.length ; i++) {
res = res ^ nums[i];
//二进制的按位异或。
}
return res;
}
HasCycle141
哈希表
在遍历的过程中,将每个节点存入HashSet,如果发现已遍历点证明有环
public class Solution1 {
public boolean hasCycle(ListNode head) {
HashSet<ListNode> hashSet = new HashSet<>();
//声明HashSet,类型为ListNode
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode cur = dummyHead;
//指针定位在虚拟头,下一位是head链表
while(null != cur.next){
if(hashSet.contains(cur.next)){
return true;
}
//如果hashSet中含有cur.next,证明有环;否则更新指针,并把更新后的节点存入hashSet
cur = cur.next;
hashSet.add(cur);
}
return false;
}
}
双指针
两个指针同时开始遍历链表,一个快一个慢,如果快的追上了慢的,证明套圈了,有环
public class Solution {
public boolean hasCycle(ListNode head) {
//声明两个指针,都指向虚拟头
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode cur1 = dummyHead;
ListNode cur2 = dummyHead;
while(true){
if(null == cur2.next || null == cur2.next.next){
//循环条件,两个指针的下一位不为空,有一个为空则返回false
return false;
}
cur1 = cur1.next;
//慢指针,一次一步
cur2 = cur2.next.next;
//快指针,一次两步
if(cur1 == cur2){
return true;
}
}
}
}
MinStack155
思路:借助一个其他的容器,辅助记录栈中最小值(可以是一个int数,也可以是一个栈)
(如果用一个栈,不断push,始终保证pop是最小值,岂不快活)
一开始没能考虑到取出pop后Min的变化,所以stackMIn中需要记录历史最小值
注意pop和peek的区别
class MinStack{
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public MinStack(){
stackData = new Stack<Integer>();
stackMin = new Stack<Integer>();
}
public void push(int x){
if(stackMin.empty()||x <= getMin()){
stackMin.push(x);
}
else {
int newMin = getMin();
stackMin.push(newMin);
//保留历史最小值,当pop取出min变化时,从这里拿到
}
stackData.push(x);
}
public void pop() {
if(stackData.empty()) {
throw new RuntimeException("your stack is empty!");
}
stackData.pop();
stackMin.pop();//关键代码,在取出pop时,有可能Min发生了变化
}
public int top() {
if(stackData.empty()) {
throw new RuntimeException("your stack is empty!");
}
return stackData.peek();
}
public int getMin() {
if(stackMin.empty()) {
throw new RuntimeException("your stack is empty!");
}
return stackMin.peek();
}
}
GetIntersectionNode160
思路:设置两个指针,从头到尾,获得两个链表长度
巧妙点:长度做差,得到一个差值,让长链表的指针先移动差值个位数
然后二者同频,直到发现某个点两个指针等值,这就找到了交点
如果一直没有发现则返回null,证明没有交点
public class Solution{
public ListNode getIntersectionNode(ListNode headA, ListNode headB){
int lenA = getLength(headA);
int lenB = getLength(headB);
ListNode curA = headA;
ListNode curB = headB;
int gap = Math.abs(lenA -lenB);
if (lenA >= lenB) {
while(gap-- > 0){
curA = curA.next;
}
} else {
while(gap-- > 0){
curB = curB.next;
}
}
while (null!=curA){
if(curA==curB){
return curA;}
curA = curA.next;
curB = curB.next;
}
return null;
}
//设计了一个private函数实现计算长度的功能
private int getLength(ListNode head){
int len = 0;
ListNode cur = head;
while(null != cur){
cur = cur.next;
len++;
}
}
}
MajorityElement169
思路1:hashmap,用哈希表对应保存元素和出现次数,第二个循环找出出现次数最多的元素
思路2:排序法,先排序,然后输出第n/2个元素作为结果
思路3:巧解
public static int majorityElement(int[] nums) {
int num = nums[0], count = 1;
for(int i=1;i<nums.length;i++){
//从第2个数开始,判断和第一个数是否一样
if(nums[i] == num) {
count++;
} else if(--count < 0) {
//当发现count被扣到0,就重新开始计数(众数总会在一个区域取得绝对优势,题中说多于n/2次)
num = nums[i];
count = 1;
}
}
return num;
}
ReserveList206
一般来说,树递归,线性迭代,这样用的多一点,但并不绝对
递归
思路:链表翻转equals翻转除头结点外的链表,再将头结点接在最后
public class Solution {
public ListNode reverseList(ListNode head) {
//递归结束条件头结点自身为null或指向null(一会儿测试下不考虑自身为空的情况)
if(head == null || head.next == null) {
return head;
}
//实际上reverse没有进行翻转操作,只需要将头结点接在子链表的后面,再对子链表调用这个函数就可以了
ListNode newHead = reverseList(head.next);
head.next.next = head;
//原头结点接在子链表的后面(关键操作)子链表在递归语句中,从return中获得
head.next = null;
//原头的下一位指向null,也就是将现在的尾结点指向null
return newHead;
}
}
迭代
思路:借助三个指针,作为中间量
if(head == null || head.next==null) return head;
ListNode p = head;
ListNode q = head.next;
ListNode m;
p.next = null;
while(q!=null){
m = q.next;//记录一下q后面是什么鬼
q.next = p;//把p放在q后面,p是之前的头
p = q;//q放在头位置了
q = m;//更新一下,对于下一个循环,q=q.next
}
return p;
Invertree226
思路:迭代,循环while(当前node的子节点不为null),从底层开始左右子节点交换(应该需要一个中间量)
if (null == root){
return root;}
TreeNode leftChild = invertTree(root.right);
TreeNode rightChild = invertTree(root.left);
root.left = leftChild;
root.right = rightChild;
//这里用了两个中间量,为了让代码更好看
return root;
设立二叉树的输入!
public class Solution {
Scanner scanner = new Scanner(System.in);
// 建立二叉树
public TreeNode createTree(TreeNode root) {
String val;
val = scanner.next(); // next方法每次取到一个间隔符前面的数据
if(val.equals("#")) {
return null;
}
root = new TreeNode(Integer.parseInt(val));// System.out.println("输入的数据为:" + val);
root.left = createTree(root.left);
root.right = createTree(root.right);
return root;
}
// 得到二叉树的镜像 —— 递归的方式
public void Mirror(TreeNode root) {
if(root == null) {
return;
}
if((root.left == null) && (root.right == null)) {
return;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
Mirror(root.left);
Mirror(root.right);
}
// 得到二叉树的镜像 —— 不使用递归
public void MirrorNotRecursive(TreeNode root) {
java.util.LinkedList<TreeNode> queue = new java.util.LinkedList<TreeNode>();
TreeNode temp = null;
if(root == null) {
return;
}
queue.add(root);
while(queue.size() != 0) {
TreeNode node = queue.removeFirst();
temp = node.left;
node.left = node.right;
node.right = temp;
if(node.right != null) {
queue.add(node.right); // 入队的为原来的左孩子
}
if(node.left != null) {
queue.add(node.left);
}
}
}
// 层次遍历二叉树
public void levelTraverse(TreeNode root) {
if (root == null) {
return;
}
LinkedList<TreeNode> list = new LinkedList<TreeNode>();
list.add(root);
while (list.size() != 0) {
TreeNode node = list.removeFirst(); // list.removeFirst() 该方法LinkedList才有
System.out.print(node.val + " ");
if(node.left != null) {
list.add(node.left); // list.add()添加单个元素,如果不指定索引的话,元素将被添加到链表的最后
}
if(node.right != null) {
list.add(node.right);
}
}
}
public static void main(String[] args) {
Solution solution = new Solution();
TreeNode root = null;
root = solution.createTree(root);
System.out.println("原二叉树的层次遍历");
solution.levelTraverse(root);
solution.Mirror(root);
System.out.println("\n输出该二叉树的镜像");
solution.levelTraverse(root);
solution.MirrorNotRecursive(root);
System.out.println("\n输出该二叉树的镜像(非递归方式)");
solution.levelTraverse(root);
}
}
/*
* 测试数据:
* 1 2 3 # 4 # # 5 6 # # # 7 8 # # 9 10 # # 11 # # (说明:其中#说明左右子树为空)
* 用先序遍历来建立树后,层次遍历结果为: 1 2 7 3 5 8 9 4 6 10 11
* 反转二叉树之后:1 7 2 9 8 5 3 11 10 6 4
*/
IsPalindrome234
思路1:全部写入数组,判断数组是否回文
思路2:快慢指针找到中点,将后半部分翻转,逐位判断是否相同
//思路2
public class solution{
public boolean isPalindrome(ListNode head){
if (head == null || head.next == null){
return true;}
ListNode middle = partition(head);
middle = reverseList(middle);
while (head != null && middle != null){
if (head.val != middle.val){
return false;}
head = head.next;
middle = middle.next;
}
return true;
}
private ListNode partition(ListNode head){
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;}
return slow;
}
private ListNode reverseList(ListNode head){
if (head == null || head.next == null){
return head;}
ListNode p = head;
ListNode q = head.next;
ListNode m = null;
p.next = null;
while (q!=null){
m = q.next;
q.next = p;
p = q;
q = m;}
return p;//p才是头啊臭宝!!p是头,只不过值不是一开始的head的值了
}
}
MoveZeros283
思路1:使用一个额外的数组,初始化均为0,对原数组中所有非零元素进行复制(但是这样不符合说明1)
思路2:使用k记录遇到零的次数,遇到k次后,则对后续元素向前移动k个位置
思路3:对数组中每个元素,不断的交换非0元素和0元素之间的位置
class Solution:
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
k = 0
for i, num in enumerate(nums):
if num != 0:
#在这加一句 if num != k:进行优化,参考摩尔算法
nums[i], nums[k] = nums[k], nums[i]
k += 1
class Solution {
public void moveZeroes(int[] nums) {
int k=0;
int temp = 0;
for(int i = 0; i < nums.length; i++){
if (nums[i] != 0){
temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
k++;
}
}
}
}
FindDisappearedNumbers448
思路:以值的正负,来代表该值索引的数字是否已经出现过(索引注意加减1)
List<Integer> results = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (nums[Math.abs(nums[i]) - 1] > 0) {
nums[Math.abs(nums[i]) - 1] = - nums[Math.abs(nums[i]) - 1];
//Math.abs(nums[i])-1位置上的值,代表了Math.abs(nums[i])有没有出现过
//绝对值还在,不影响后续遍历,但正负号已经写入了
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
//i位置上的正负,代表了i+1这个数字是否出现过
results.add(i + 1);
}
}
return results;
HanmingDistance461
思路:异或,数1
//异或操作得到101
int z = x ^ y;
int sum = 0;
while (z!=0){
sum += z & 1;
//按位与运算 101 & 001 = 001
z = z>>1;
//右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃
}
return sum;
//根据循环,z=101,sum=1,z=10,10&01=00,sum=1,z=1,1&1=1,sum=2,z=0
DiameterOfBinaryTree543
思路:对左右子树求最大直径,然后加和(循环条件该节点有子节点)
key:直径有可能不经过根节点
递归
public class Solution {
int maxLen =0;
public int diameterOfBinaryTree(TreeNode root) {
pathLen(root);
return maxLen;
//最后我们要的是最长路径返回的是maxLen中保存的最大值
}
public int pathLen(TreeNode root){
if(root==null){
return 0;
}
int left = pathLen(root.left);
int right = pathLen(root.right);
maxLen = Math.max(maxLen, left+right);
return Math.max(left+1, right+1);
//这个return回去给pathLen的,所以要返回子节点中的最长路径
}
}
MergeTrees617
思路:简单递归,但前提要处理好root1、root2中null的问题,对于每次合并,都先合并根节点,然后递归合并子树
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null){
return root2;
}
if (root2 == null){
return root1;
}
root1.val = root1.val + root2.val;
root1.left = mergeTrees(root1.left,root2.left);
root1.right = mergeTrees(root1.right,root2.right);
return root1;
}
}