- Eg136 只出现一次的数字 (新鲜)
// 用异或操作解决问题,很新鲜
class Solution {
public int singleNumber(int[] nums) {
int result = 0;
for (int i = 0; i < nums.length; i++)
result ^= nums[i];
return result;
}
}
- Eg107. 二叉树的层次遍历
//①Java的Linklist<>代表链表,继承List类,
//指定链表元素只能是List列表,List代表列表List的元素只能是Integer
//LinkedList<List> 是 List<List>的子类
//②队列是一种特殊的线性表
//LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
//Queue的常见方法:1)queue.poll():获取并移除队首,如果为空,返回null;
//2)queue.remove():获取并移除队首,如果为空,返回异常;
//3)queue.peek():获取队首元素,如果为空,返回null;
//4)queue.element():获取队首元素,如果为空,返回异常;
//5)queue.offer():插入元素,若成功返回true,否则返回false;
//6)queue.add():插入元素,若不成功,抛出异常
//7)queue.size():求队列元素总数
//③List是抽象类,在创建的时候要说明是创建ArrayList(数组)、LinkedList(链表)
//④链表LinkedList的两种插入方法:1)LinkedList.addFirst();2)LinkedList.addLast()
//⑤Queue、LinkedList、ArrayList都是链表,所以在创建的时候不需要声明长度
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
//链表lli存放题目要求的元素
LinkedList<List<Integer>> lli = new LinkedList<>();
//注意:不能return null
if (root == null) return lli;
//队列queue用来遍历树
Queue<TreeNode> queue = new LinkedList<>();
//数组valarr用来存放每层元素
List<Integer> valarr = new ArrayList();
//结点ptr存放出队结点
TreeNode ptr;
//lvllen存放每层的结点总数,count存放该层已经出队列的结点数
int lvllen = 1, count = 0;
queue.add(root);
while (!queue.isEmpty()) {
//出队
ptr = queue.poll();
count++;
valarr.add(ptr.val);
if (ptr.left != null)
queue.add(ptr.left);
if (ptr.right != null)
queue.add(ptr.right);
//每层最后一个结点出队列后,重新set
if(count == lvllen) {
lli.addFirst(valarr);
//⑥由于上文已经定义了valarr的类型,此处重新申请内存时不需要再声明类型
valarr = new ArrayList();
count = 0;
lvllen = queue.size();
}
}
return lli;
}
}
- Eg108.将有序数组转为二叉树
//思路:由于题意是高度平衡二叉树,且输入数组nums是递增数列,所以正中间是root
//左右两边分别是左右子树。题意又说,一个序列可以是某个平衡二叉树,所以我们
//可以将一个序列转化成左右子树结点个数相差1个的树,即左右子序列个数相差1个
//所以m = l + (r-l)/2 可以用来解决问题。由于/运算是向下取的,所以(r-l)/2表示正中间或偏左
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public TreeNode sort
class Solution {
//思路:由于题意是高度平衡二叉树,且输入数组nums是递增数列,所以正中间是root
//左右两边分别是左右子树。题意又说,一个序列可以是某个平衡二叉树,所以我们
//可以将一个序列转化成左右子树结点个数相差1个的树,即左右子序列个数相差1个
//所以m = l + (r-l)/2 可以用来解决问题。由于/运算是向下取的,所以(r-l)/2表示正中间或偏左
public TreeNode sortedArrayToBST(int[] nums) {
return nums == null ? null : buildTree(nums, 0, nums.length-1);
}
public TreeNode buildTree(int[] nums, int l, int r) {
if (l > r) return null;
TreeNode root = new TreeNode();
//由于/运算是向下取的,所以(r-l)/2表示正中间或偏左
int m = l + (r-l)/2;
root.val = nums[m];
root.left = buildTree(nums, l, m-1);
root.right = buildTree(nums, m+1, r);
return root;
}
}
- Eg110. 平衡二叉树
//解题的关键:如果左右子树已经是不平衡树,逐渐地往上层传递-1;
//简单情况下可以用三目运算符,复杂情况下最好不要用
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
int leftheight = getTreeHeight(root.left);
int rightheight = getTreeHeight(root.right);
if (leftheight == -1 || rightheight == -1)
return false;
return Math.abs(leftheight - rightheight) > 1 ? false : true;
}
public int getTreeHeight(TreeNode root) {
if (root == null) return 0;
int leftheight = getTreeHeight(root.left);
int rightheight = getTreeHeight(root.right);
//如果子树开始不平衡
if (Math.abs(leftheight - rightheight) > 1) return -1;
//如果某个结点的孩子已经是不平衡树,一直往上传递,该边的子树不平衡
if (leftheight == -1 || rightheight == -1) return -1;
return 1 + Math.max(leftheight,rightheight);
}
}
- Eg111. 二叉树的最小深度:求二叉树的深度的变形
- 递归求二叉树的深度,只要知道左子树的深度,跟右子树的深度即可
class Solution {
public int minDepth(TreeNode root) {
if (root == null) return 0;
int leftheight = minDepth(root.left);
int rightheight = minDepth(root.right);
//只有右子树
if (leftheight == 0 && rightheight != 0)
return 1 + rightheight;
//只有左子树
else if (rightheight == 0 && leftheight != 0)
return 1 + leftheight;
//均有左右子树或者均无左右子树
else
return 1 + Math.min(leftheight, rightheight);
}
}
- Eg112.路径总和
//思路:递归解决是否存在路径问题:递归传递剩余差
//如果当前结点是叶子结点,且结点值等于剩余差,即说明有路径
//如果存在路径,即指针传递到空节点,且空节点的父亲是叶子结点
//我们不能仅仅用一个hasPathSum(root, sum)函数来实现递归,
//1)最大根节点为null与子树为null不同。若测试用例为root = null, sum = 0,返回的应该是false;
//2)一定要判断是从根节点到叶子结点,而不能是“存在子路径”。若到了null子结点,且sum=0,但其父结点不是子结点,仍应返回false
//3)要考虑结点值可正可负
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
//思路:递归解决是否存在路径问题:递归传递剩余差
//如果当前结点是叶子结点,且结点值等于剩余差,即说明有路径
//如果存在路径,即指针传递到空节点,且空节点的父亲是叶子结点
if(root == null) return false;
boolean leftflag = false, rightflag = false;
int temp = sum-root.val;
//我们不能仅仅用一个hasPathSum(root, sum)函数来实现递归,
//1)最大根节点为null与子树为null不同。若测试用例为root = null, sum = 0,返回的应该是false;
//2)一定要判断是从根节点到叶子结点,而不能是“存在子路径”。若到了null子结点,且sum=0,但其父结点不是子结点,仍应返回false
//如果左子树有路径
leftflag = hasSubPathSum(root, root.left, sum-root.val);
//如果右子树有路径
rightflag = hasSubPathSum(root, root.right, sum-root.val);
//如果左子树有路径或者右子树有路径,说明该树有路径
return leftflag || rightflag;
}
public static boolean hasSubPathSum(TreeNode parent, TreeNode root, int sum) {
if (root == null) {
if(sum == 0 && parent != null && parent.left == null && parent.right == null)
return true;
else
return false;
}
boolean leftflag = false, rightflag = false;
leftflag = hasSubPathSum(root, root.left, sum-root.val);
rightflag = hasSubPathSum(root, root.right, sum-root.val);
return leftflag || rightflag;
}
}
方法二:
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root == null) return false;
if (root.val == sum) {
if (root.left == null && root.right == null)
return true;
}
boolean leftflag = false, rightflag = false;
leftflag = hasPathSum(root.left, sum-root.val);
rightflag = hasPathSum(root.right, sum-root.val);
return leftflag || rightflag;
}
}
- Eg118 杨辉三角
//①C/Java中function(int[] arr),是引用传递;
//如果想要值传递,可在子函数里面用int[] b =Arrays.cpoyOf(arr,length);
//②Java数组用ArrayList,遍历数组用ArrayList.get()
//③Java ArrayList作为参数传的是值传递,LinkedList才是地址传递;
//④Java求链表的长度用LinkedList.size()
//⑤Java中链表的重新赋值用LinkedList.set(index,value)
class Solution {
public List<List<Integer>> generate(int numRows) {
LinkedList<List<Integer>> lld = new LinkedList<List<Integer>>();
if(numRows == 0) return lld;
List<Integer> l1 = new ArrayList<>();
l1.add(1);
lld.addLast(l1);
if (numRows == 1) return lld;
List<Integer> l2 = new ArrayList<>();
l2.add(1);
l2.add(1);
lld.addLast(l2);
if (numRows == 2) return lld;
LinkedList<Integer> movarr = new LinkedList<>();
movarr.add(1);
movarr.add(1);
for (int circnt = 2; circnt < numRows; circnt++)
generateYang(lld, movarr);
return lld;
}
public static void generateYang (LinkedList<List<Integer>> lld, LinkedList<Integer> movarr) {
LinkedList<Integer> lvl = new LinkedList<>();
lvl.add(1);
for(int i=0, j = 1; j < movarr.size(); i++, j++) {
int temp = movarr.get(i) + movarr.get(j);
lvl.add(temp);
}
lvl.add(1);
lld.add(lvl);
//movarr前半部分重新赋值
for (int i = 0; i < movarr.size(); i++)
movarr.set(i,lvl.get(i));
//movarr后半部分添加
for (int i = movarr.size(); i < lvl.size();i++)
movarr.add(lvl.get(i));
}
}
方法二:
//杨辉三角的性质:①第i行的元素个数为i个;②第i行两端元素为1,中间元素是第i-1行元素的和
//①Java中可扩展数组列表ArrayList(), 不会产生累赘元素;不可扩展数组int[]
//②拷贝ArrayList需要两个参数 pre = Arrays.copyOf(cur,j+1);一个是拷贝对象,一个是拷贝长度
class Solution {
public List<List<Integer>> generate(int numRows) {
//杨辉三角的性质:①第i行的元素个数为i个;②第i行两端元素为1,中间元素是第i-1行元素的和
List<List<Integer>> list = new ArrayList<>();
int[] pre = new int[numRows], cur = new int[numRows];
for (int j = 1; j <= numRows; j ++) {
//①Java中可扩展数组列表ArrayList(), 不会产生累赘元素;不可扩展数组int[]
List<Integer> subList = new ArrayList<Integer>();
for (int i = 0; i < j; i++) {
//两端元素为1
if (i == 0 || i == j)
cur[i] = 1;
else
cur[i] = pre[i-1] + pre[i];
//由于sublist是可扩展的数组,不会产生累赘元素
subList.add(cur[i]);
}
list.add(subList);
pre = Arrays.copyOf(cur,j+1);
}
return list;
}
}
- Eg122.买卖股票的最佳时机II (与最大子串那道题相结合)
动态规划的思想 我还是没有总结出动态规划的特性。
如果要获得的总利润最大,就要让每一步获得的利润最大。而每一步的利润最大就是只要第二天比第一天有收益就售出。乍眼看这会造成短视行为。但是,假设连续三天都涨,第二天售出,立刻第二天买入,第三天售出。这与第一天买入,第三天售出是一样的。
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0 || prices.length == 1)
return 0;
int maxprofit = 0;
for (int i = 0; i < prices.length-1; i++) {
if (prices[i+1] > prices[i])
maxprofit += prices[i+1] - prices[i];
}
return maxprofit;
}
}
- Eg155 最小栈
//思路:栈顶始终为当前栈内最小的元素
//熟悉使用栈Stack的方法
//1)stack.empty();
//2)stack.add();
//3)stack.peek(); 仅获取栈顶元素
//4)stack.get(index); 获取下标为index的元素
//全局变量用修饰词 加个 public static
class MinStack {
public static Stack stack;
/** 构造器 */
public MinStack() {
stack = new Stack<Integer>();
}
public void push(int x) {
if (stack.empty()) {
stack.add(x);
stack.add(x);
}
else {
int topelement = stack.peek();
//int topelement = stack.peek();
//将最新元素压入栈
stack.add(x);
//当前栈内的最小元素压入栈
if (x > topelement)
stack.add(topelement);
else
stack.add(x);
}
}
public void pop() {
stack.pop();
stack.pop();
}
public int top() {
return stack.get(stack.size()-2);
}
public int getMin() {
return stack.get(stack.size()-1);
}
}
- Eg160 相交链表(妙!!!重做!!)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode atr = headA, btr = headB;
int lenA = 0, lenB = 0;
while (atr != null) {
atr = atr.next;
lenA++;
}
atr = headB;
while (btr != null) {
btr = btr.next;
lenB++;
}
btr = headA;
//走同样的步数
if (lenA > lenB)
for (int i = 0 ; i < lenA - lenB; i++)
btr = btr.next;
else
for (int i = 0 ; i < lenB - lenA; i++)
atr = atr.next;
while (atr != null) {
if (atr == btr)
return atr;
else {
atr = atr.next;
btr = btr.next;
}
}
return null;
}
}
- Eg167 两数之和
//解题思路关键:注意到所给的数组numbers是升序排序
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
int left = 0, right = numbers.length-1;
while (left < right) {
if (numbers[left] + numbers[right] == target) {
res[0] = left+1;
res[1] = right+1;
}
//两数之和过大,则要缩小,故右游标左行
if (numbers[left] + numbers[right] > target)
right--;
//两数之和过小,则要放大,故左游标右行
else
left++;
}
return res;
}
}
- Eg168 Excel表列名称
//思路:10进制转26进制,参照10进制转2进制(除2取余法)
//这里1-A … 26-Z; 在26进制中,从0开始无26对应的字符,且此处Excel的26进制表示的是(0,无穷),10进制表示[0,无穷]
//即在每个进制位上(个十百千万) 对于每一个10进制的数,应先将其减1再去数字-字符表找对照
//Eg:2626 + 26 =>step1: 2626 + 25(Z) => 2526 (Z)
//我们不要有个switch…case 来存储数字-字符表,只需要在“A”的基础上添加即可***
//①Java的StringBuilder与String的区别:
//1)String每次赋值都是指向新的对象;2)StringBuilder每次赋值是在自身基础进行修改;
//当程序中需要大量的对某个字符串进行操作时,应该考虑应用StringBuilder类处理该字符串
//②Java的StringBuilder若要转化为String,需要StringBuilder.toString()操作
class Solution {
public String convertToTitle(int n) {
StringBuilder sb = new StringBuilder();
while(n > 0) {
n--;
sb.append((char)(n%26 + 'A'));
n /= 26;
}
String result = sb.reverse().toString();
return result;
}
}
- Eg169 多数元素
=================================================
回顾快速排序算法
//rtr一直往左走,直到所指元素值小于pivot值;跳出循环时,rtr调到ltr上
//关键:一定要是>= 或者 <= (即包括=)时,移动。否则在中途碰到与pivot值相等的元素,游标不会移动
public void QuickSort(int[] nums, int left, int right) {
if (left >= right) return;
int pivot = nums[left], temp;
int ltr = left, rtr = right;
while (ltr < rtr) {
//rtr一直往左走,直到所指元素值小于pivot值;跳出循环时,rtr调到ltr上
while (nums[rtr] > pivot && ltr < rtr)
rtr--;
//ltr一直往右走,直到所指元素值大于pivot值
while (nums[ltr] < pivot && ltr < rtr)
ltr++;
//如果循环并未结束
if (ltr < rtr) {
temp = nums[rtr];
nums[rtr] = nums[ltr];
nums[ltr] = temp;
}
}
//跳出循环时,[left+1, ltr]均为小于pivot值,[rtr+1,right]均为大于pivot的值
nums[left] = nums[rtr];
nums[rtr] = pivot;
QuickSort(nums, left, rtr-1);
QuickSort(nums, rtr+1, right);
}
方法一:先排序
QuickSort(nums, 0, nums.length-1);
//或者使用Java现成的排序api
//Arrays.sort(nums);
//return nums[nums.length / 2];
int count = 1, curmeta = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] == curmeta) {
count++;
if (count > (nums.length/2))
return curmeta;
}
else {
curmeta = nums[i];
count = 1;
}
}
return nums[0];
方法二:哈希表
//Java HashMap类的常用使用
//1)声明变量类型:HashMap<K,V>
//2)添加映射元素(1个映射元素叫entry):HashMap.put(K,V)
//3)判断是否存在某个哈希映射元素:HashMap.containsKey(K)
//4)根据V获取K:HashMap.getKey(V)
//5)根据K获取V:HashMap.getValue(K)
//6)遍历哈希表:用for-each方法
//for(Map.Entry<K,V> entry :hash.entryset())
class Solution {
public int majorityElement(int[] nums) {
HashMap<Integer,Integer> hash = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (!hash.containsKey(nums[i] ))
hash.put(nums[i],1);
else{
int count = hash.get(nums[i]);
hash.put(nums[i],count+1);
}
}
for (Map.Entry<Integer,Integer> entry : hash.entrySet()) {
if (entry.getValue() > (nums.length / 2))
return entry.getKey();
}
return -1;
}
}
- Eg172阶乘后的零(重做)
尝试过先求出阶乘再除10求阶乘后的0,问题是Int只能表示1亿以下的数,即使是long对于130!也是不够装
力扣评论思路:
首先题目的意思是末尾有几个0
比如6! = 【1* 2* 3* 4* 5* 6】
其中只有25末尾才有0,所以就可以抛去其他数据 专门看2 5 以及其倍数 毕竟 4 * 25末尾也是0
比如10! = 【2456810】
其中 4能拆成22 10能拆成25
所以10! = 【2*(22)5(23)(222)(25)】
一个2和一个5配对 就产生一个0 所以10!末尾2个0
转头一想 2肯定比5多 所以只数5的个数就行了
假若N=31 31里能凑10的5为[5, 25, 35, 45, 25, 6*5] 其中 25还能拆为 52
所以 里面的5的个数为 int(31/(51)) + int(31/(52))
所以 只要先找个一个 5x < n 的x的最大数 然后按上面循环加起来
class Solution {
public int trailingZeroes(int n) {
int maxmi = 1, cntOfZeroes = 0;
while(Math.pow(5,maxmi) <= n)
maxmi++;
maxmi--;
for (int i = 1; i <= maxmi; i++)
cntOfZeroes += n / Math.pow(5,i);
return cntOfZeroes;
}
}
- Eg189 旋转数组 (妙)
方法一:时间复杂度O(kn),空间复杂度O(1),即外循环循环k次,内循环循环n-1次,每次外循环结果是数组向右移动1位
方法二:(妙) 重做!!!
1)将所有数组逆转
2)将前面k个元素逆转
3)将后面n-k个元素逆转
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
if (k > 0) {
inverseNumsArray(nums, 0, nums.length-1);
inverseNumsArray(nums, 0, k-1);
inverseNumsArray(nums, k, nums.length-1);
}
}
public void inverseNumsArray(int[] nums, int start, int end) {
int temp;
for (int i = start, j = end ; i < j ; i++, j--) {
temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}