力扣5.最长回文子串
使用dp[i][j]:以i位置开头,以j位置结尾。
class Solution { public static String longestPalindrome(String s) { int n=s.length(); //dp[i][j]:设置以i位置开头,j位置结尾的dp int len=1; int begin=0; boolean[][]dp=new boolean[n][n]; for(int i=n-1;i>=0;i--){ for(int j=i;j<n;j++){ if(s.charAt(i)==s.charAt(j)){ if(i==j||i+1==j){ dp[i][j]=true; } else{ //中间有字符,接下来看看i+1和j-1这个位置看看 dp[i][j]=dp[i+1][j-1]; } } if(dp[i][j]==true&&j-i+1>len){ len=j-i+1; begin=i; } } } return s.substring(begin,begin+len); } }
力扣1745.分割回文串IV
简单的去判定是不是回文子串用dp已经很常见了,来分析一下比较罕见的分割成三段
class Solution { public boolean checkPartitioning(String s) { int n=s.length(); boolean[][]dp=new boolean[n][n]; for(int i=n-1;i>=0;i--){ for(int j=i;j<n;j++){ if(s.charAt(j)==s.charAt(i)){ if(i+1==j||i==j){ dp[i][j]=true; } else dp[i][j]=dp[i+1][j-1]; } } } for(int i=1;i<n-1;i++){ for(int j=i;j<n-1;j++){ if(dp[0][i-1]&&dp[i][j]&&dp[j+1][n-1]) return true; } } return false; } }
力扣132.分割回文串II
1.dp[i]:s[0,i]区间上最长的子串,最少分割次数
(假如没有这个优化,则会造成越界,因为会有三次循环,判断0-i是否回文,i-j是不是回文,0-j是不是回文串)
2.初始化
dp表内所有的初始值都初始化为无限大
3.填表顺序:
从左往右填表
4.返回值
dp[n-1]
class Solution { public int minCut(String s) { int n=s.length(); boolean[][]isreturn=new boolean[n][n]; for(int i=n-1;i>=0;i--){ for(int j=i;j<n;j++){ if(s.charAt(i)==s.charAt(j)){ if(i==j||i+1==j){ isreturn[i][j]=true; }else{ isreturn[i][j]=isreturn[i+1][j-1]; } } } } int []dp=new int[n]; for(int i=0;i<n;i++)dp[i]=Integer.MAX_VALUE; for(int i=0;i<n;i++){ if(isreturn[0][i]==true) dp[i]=0; else{ for(int j=1;j<=i;j++){ // 1.dp[i]:s[0,i]区间上最长的子串,最少分割次数 if(isreturn[j][i]==true) //初始化无穷大,防止其干扰min求值 dp[i]=Math.min(dp[i],dp[j-1]+1); } } } return dp[n-1]; } }
优先级队列(堆)是什么
默认情况下,堆是小根堆
下面是手动实现小根堆
使用数组和一个计数结构
import java.util.Arrays; public class TestHeap { //自己去写一个堆 public int[] elem; public int usedSize; public TestHeap() { this.elem = new int[10]; } //创建一个大根堆 public void createHeap(int[] array) { for (int i = 0; i < array.length; i++) { elem[i] = array[i]; usedSize++; } //parent=child-1/2 for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) { //向下调整,需要给的数字,当前父亲位置,结束位置 shiftDown(parent, usedSize); } } /** * @param parent 每颗子树根节点 * @param len 每个子树的结束位置 */ //向下调整,已知孩子下标len-1拿到,父亲节点(len-1-1)/2 public void shiftDown(int parent, int len) { int child = parent * 2 + 1; //看他是否有左孩子 //查看左右孩子哪个大,和大的交换,因为要变成大根堆 while (child < len) { if(child+1<len){ if(elem[child]<elem[child+1]){ child=child+1; } } if (elem[child] > elem[parent]) { int tmp = elem[parent]; elem[parent] =elem[ child]; elem[child] = tmp; parent = child; child = parent * 2 + 1; }else{ break; } } } //保证仍然原先是大根堆,还是小根堆(当插入一个元素的时候,先插入到最后一个位置),插入后,直接和根比较 //向上调整 public void push(int val){ //检查是否满了 //2.存数据 if(isFull()){ elem=Arrays.copyOf(elem,2*elem.length); } elem[usedSize]=val; usedSize++; shiftUp(usedSize-1); } public boolean isFull(){ return usedSize==elem.length; } public void shiftUp(int child){ int parent=(child-1)*2; while(child>0){ if(elem[child]>elem[parent]){ int tmp=elem[child]; elem[child]=elem[parent]; elem[parent]=tmp; child=parent; parent=(child-1)/2; }else{ break; } } } //假如只能删除堆顶,还要保持他是一个大根堆的形态(那么就要头和最下面的换,然后去,向下调整) public void poll(){ int tmp =elem[0]; elem[0]=elem[usedSize-1]; //让0下标元素和最下面的元素交换,然后再去向下调整(把size--即可) elem[usedSize-1]=tmp; usedSize--; shiftDown(0,usedSize); } //堆排序 public void headSort(){ int end=usedSize-1; while(end>0){ //本身已经是一个大根堆 int tmp=elem[0]; elem[0]=elem[end]; elem[end]=tmp; shiftDown(0,end); end--; } } }
力扣1046.最后一块石头的重量
class Solution { public static int lastStoneWeight(int[] stones) { //创建一个大根堆 PriorityQueue<Integer>heap=new PriorityQueue<>((a,b)->b-a); //2.把所有的石头放进堆里面 for(int x:stones){ heap.offer(x); } //3.模拟 while(heap.size()>1){ int a=heap.poll(); int b=heap.poll(); //先a再b,然后再大于等于 if(a>b){ heap.offer(a-b); } } //拿出堆顶元素 return heap.isEmpty()?0: heap.peek(); } }