- 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;
}
}
}