算法專輯
回溯法
正則表達式匹配【分類討論思想】
- 有 * 【首字符匹配(*匹配多個)||首字符不匹配(*匹配0個)】,無 *【首字符匹配&&後續都要匹配】
- 程序終結的條件:【模式串爲空,匹配串不爲空,結果:fasle】,【模式串爲空,匹配串爲空,結果:true】
- 不斷縮小字符串,重複執行以上步驟。
public boolean isMatch(String s, String p){
if (p.isEmpty()) return s.isEmpty();
boolean firstMatch=(!s.isEmpty()&&(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.'));
if (p.length()>1&&p.charAt(1)=='*'){
return (firstMatch&&isMatch(s.substring(1),p))||isMatch(s,p.substring(2));
}else {
return firstMatch&&isMatch(s.substring(1),p.substring(1));
}
}
分割數組的最大值【用棧分析代碼遞歸的執行過程】
排列組合問題【最通用的解題思路】
- 比如給你數組[1,2,3],輸出該數組所有可能的全排列
public class Solution {
boolean[] isused=new boolean[100];
List<Integer>templist=new ArrayList<>();
List<List<Integer>>rst=new ArrayList<>();
public static void main(String[] args) {
int[] a=new int[]{1,2,3};
List<List<Integer>> an=new Solution().arrange(a,0);
System.out.println(an);
}
public List<List<Integer>> arrange(int[] nums,int index) {
if (index==nums.length) {rst.add(new ArrayList<>(templist));}
// //這裏會遍歷nums.length*nums.length次
for (int i = 0; i <nums.length ; i++) {
if(!isused[i]){
isused[i]=true;
templist.add(nums[i]);
arrange(nums,index+1);
templist.remove(templist.size()-1);
isused[i]=false;
}
}
return rst;
}
}
對12345進行全排列
0{
0[
1,{0~4},2,{0~4},3,{4},4
]
1[
0{0~4},1,2,{0~4},2,{0~4},3,{0~4},4{0~4}
]
2[
0,{0~4},1,{0~4}.......
]
}
解釋說明: 01{0~4}對應012,013,014即【123,124,125】
02{0~4}對應【132,134,135】
03{4}對應【145】
- 組合
/**
* 從1,2,3,4,5中選擇3個數,求所有的組合
*/
public class Combination {
static List<List<Integer>>res=new ArrayList<>();
static List<Integer>list=new ArrayList<Integer>();
public static void main(String[] args) {
Combination combination = new Combination();
combination.combination(new int[]{1,2,3,4,5},3,0);
System.out.println(res);
System.out.println(res.size());
}
/**
* 在下一次循環開始之前,直接跳過下次循環,然後回溯狀態,接着循環下去。
* 在循環中,[int i = h/i=0;這條語句在每次遞歸再次進入循環時都會得到執行]
* 【即for(int i = h/i=0; i <n;i++)】,每當滿足條件,就跳過一次循環(拿來用作狀態回退),
*/
void combination(int[] nums, int k, int h) {
if (list.size()==k){res.add(new ArrayList<>(list));return;}
/**
* 通過終止條件,進行剪枝優化,避免無效的遞歸
* c中還剩 k - c.size()個空位,所以[ i ... n]中至少要有k-c.size()個元素
* 所以i最多爲 n - (k - c.size()) + 1
* i <= n - (k - c.size()) + 1;
*/
for (int i = h; i <nums.length ; i++) {
list.add(nums[i]);
/**
*這裏是i+1,不是h+1,否則會出現i=2,h=1,那麼結果中會有【1,3,3】
* 當125添加後。i=nums.length,此時for結束,combination(nums,k,h+1,list);
* 即h+1少了一次執行機會,i就提前比h多加了一個1,i和h就從這裏開始不同步了
*/
combination(nums,k,i+1);
list.remove(list.size()-1);
}
}
}
二進制中1的個數【數字/位運算–與運算】
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
public class Solution {
public static void main(String[] args) {
int a=new Solution().NumberOf1(2);
System.out.println(a);
}
public int NumberOf1(int n) {
int count=0;
while(n!=0){
n=n&(n-1);
count++;
}
return count;
}
}
重建二叉樹(給出前序中序)【遞歸】
/**
* 重建二叉樹(給出前序中序)【遞歸】
*
* 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。
* 假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
* 例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},
* 則重建二叉樹並返回。
*/
public class Main {
public static void main(String[] args) {
int[] a=new int[]{1,2,3,4,5};
int[] b=new int[]{3,2,4,1,5};
Main main = new Main();
TreeNode treeNode=main.reConstructBinaryTree(a,b);
System.out.println(treeNode);
}
public TreeNode reConstructBinaryTree(int [] pre, int [] in) {
if(pre.length==0||in.length==0) return null;
TreeNode treeNodes=new TreeNode(pre[0]);
for (int i = 0; i <in.length ; i++) {
if (in[i]==pre[0]){
treeNodes.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
treeNodes.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
break;
}
}
return treeNodes;
}
}
不重複子字符串的最大長度
lengthOfLongestSubstring
堆排序【堆(優先隊列)】
這其實就是PriotyQueue優先隊列的代碼實現。
半堆化:一顆二叉樹,先將值最大的數交換到根節點,然後對於被交換的節點(沒有被交換的節點,就直接遞歸結束),再使用同樣的(遞歸)方法,進行交換,直到空節點。遞歸結束。(他只能保證二叉樹的一個分叉,符合大頂堆)
完全堆化:從完全二叉樹的最後一個根節點開始,逆向遍歷。每輪循環都進行一次半堆化。
public class HeapSort {
public static void main(String[] args) {
int[] tree = {5, 2, 7, 3, 6, 1, 40};
heapsort(tree);
}
static void heapsort(int[] tree){
for (int i = (tree.length - 2) / 2; i >= 0; i--) {
heapify(tree, tree.length, i);
}
for (int i = tree.length - 1; i >= 0; i--) {
swap(tree, i, 0);
heapify(tree, i, 0);
}
for (int j : tree) System.out.println(j);
}
static void heapify(int[] tree, int n, int index){
if(index>=n) return;
int maxIndex=index;
if(2*index+1<n&&tree[maxIndex]<tree[2*index+1]) maxIndex=2*index+1;
if(2*index+2<n&&tree[maxIndex]<tree[2*index+2]) maxIndex=2*index+2; //找到值最大的元素的下標
if(maxIndex!=index) {swap(tree,maxIndex,index);heapify(tree,n,maxIndex);} //調整被交換的節點爲根的二叉樹【半堆化】
}
static void swap(int[] tree, int maxIndex, int i) {
int temp=tree[maxIndex];
tree[maxIndex]=tree[i];
tree[i]=temp;
}
}
二叉樹前中後遍歷&BFS遍歷&DFS遍歷
更多
斐波那契數列【遞歸/dp】
寫一個函數,輸入 n ,求斐波那契(Fibonacci)數列的第 n 項。斐波那契數列的定義如下:
F(0) = 0
F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
遞歸實現
public class Main {
public static void main(String[] args) {
Main main = new Main();
System.out.println(main.Fibonacci(3));
}
public int Fibonacci(int n) {
if (n==0) return 0;
else if(n==1) return 1;
else {
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
}
dp實現
public int FibonacciDP(int n){
int dp[]=new int[n+1];
dp[0]=0;
dp[1]=1;
for (int i = 2; i <=n ; i++) {
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
dim
輸入n=5,s=4
輸出
[2,1,1,1]
[1,2,1,1]
[1,1,2,1]
[1,1,1,2]
解釋 2+1+1+1=5