力扣面试100题

321 阅读6分钟

image.png

image.png

/最长公共子序列问题

首先这毫无疑问必然是动态规划,那么首先搞定参数 是两个 是双比较模型, i和j 定义模型的含义,从0到i和0到j 他们有多少个公共子序列,那么参数就会传 两个的长度,这不是从左到右的模型 所以不是固定传0啥的,然后想的就是递推 缩小规模,先想的是basecase 一个字符 或者两个都剩一个字符了,然后讨论情况 是不是以这个i结尾 ,不结尾删了也没事

二维数组雨滴问题

一维的就是双指针 或者找到每个的瓶颈 二维的就是小根堆 跟新最大值 只要是换了最大值就是会了湖泊 其实这个很简单 还是根据算每个点的瓶颈加起来,利用了在没换最大值前 就是还是原来那个湖泊,利用小根堆找到附近能扩到最大的湖泊,一旦更新最大值,头就换了一个,以另一个头部取水 image.png

public int trapRainWater(int[][] heightMap) {
   if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) {
      return 0;
   }
   int[][]water=new int[heightMap.length][heightMap[0].length];
   boolean[][] flag=new boolean[heightMap.length][heightMap[0].length];
   int[][]direction=new int[][]{{-1,0},{1,0},{0,-1},{0,1}};
   int max=Integer.MIN_VALUE;
   PriorityQueue<WaterInfo>queue=new PriorityQueue<>(new Comparator<WaterInfo>() {
      @Override
      public int compare(WaterInfo o1, WaterInfo o2) {
         return o1.value-o2.value;
      }
   });
   for (int i=0;i<heightMap[0].length;i++){
      queue.offer(new WaterInfo(heightMap[0][i],0,i));
      flag[0][i]=true;
   }

   for (int i=1;i<heightMap.length-1;i++){
      queue.offer(new WaterInfo(heightMap[i][heightMap[0].length-1],i,heightMap[0].length-1));
      flag[i][heightMap[0].length-1]=true;
   }
   for (int i=1;i<heightMap.length-1;i++){
      queue.offer(new WaterInfo(heightMap[i][0],i,0));
      flag[i][0]=true;
   }
   for (int i=0;i<heightMap[0].length;i++){
      queue.offer(new WaterInfo(heightMap[heightMap.length-1][i],heightMap.length-1,i));
      flag[heightMap.length-1][i]=true;
   }

   while (!queue.isEmpty()){
      WaterInfo poll = queue.poll();
      max=Math.max(max,poll.value);
   water[poll.row][poll.column]=Math.max(0,max-poll.value);

      for (int[] i:direction){
         int x=poll.row+i[0];
         int y=poll.column+i[1];
         if (x>=0&&y>=0&&x<heightMap.length&&y<heightMap[0].length&&!flag[x][y]){
            queue.offer(new WaterInfo(heightMap[x][y],x,y));
            flag[x][y]=true;
         }
      }

   }

   /**
    * 第一步  先把四周的一圈加入到小更堆
    */
   int sum=0;
   for (int[]i :water){
      for (int j:i){
         sum+=j;
      }
   }
   return sum;

}

遇到要求i前面的最大值 可以前缀数组

如果也要i后的最大值 后缀数组就行了 从右往左

image.png

二叉树的反序化

就是队列+弹出 然后遇到空就返回

连续循环比较 一定是滑动窗口 一直找到违规位置

题目中又尝试操作且让你求最大最小值必是动态规划

矩阵最大值 等问题最小栈解决 象什么连续最大什么的都是一个套路

位的for循环判断是不是1的两种方式

第一种好一点

public static int hammingWeight(int n) {
		int count=0;
     for (int i=0;i<32;i++){
     	if ((n&(1<<i))!=0){
     		count++;
        }
     }
     return count;
	}
public static int hammingWeight(int n) {
   int count=0;
    for (;n!=0;n>>=1){
       if ((n&1)!=0){
          count++;
       }
    }
    return count;
}

能用动态规划做的回溯不一定可用回溯是真对要返回数组的 只是计算长度 不准用回溯法

递归开始前用for循环都是要比较的 因为每一个数都要进递归的

计算矩阵中的最长递增子序列 经典

class Solution {
    public int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int rows, columns;

    public int longestIncreasingPath(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }
        rows = matrix.length;
        columns = matrix[0].length;
        int[][] memo = new int[rows][columns];
        int ans = 0;
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < columns; ++j) {
                ans = Math.max(ans, dfs(matrix, i, j, memo));
            }
        }
        return ans;
    }

    public int dfs(int[][] matrix, int row, int column, int[][] memo) {
        if (memo[row][column] != 0) {
            return memo[row][column];
        }
        ++memo[row][column];
        for (int[] dir : dirs) {
            int newRow = row + dir[0], newColumn = column + dir[1];
            if (newRow >= 0 && newRow < rows && newColumn >= 0 && newColumn < columns && matrix[newRow][newColumn] > matrix[row][column]) {
                memo[row][column] = Math.max(memo[row][column], dfs(matrix, newRow, newColumn, memo) + 1);
            }
        }
        return memo[row][column];
    }
}


数组随机条件场 每个子数组的数目随便

因为要遍历数组且里面的数字是不连续的 用滑动窗口有点困难, 且题目是有条件的满足什么样的数才能放进去

image.png

ok 上公式了

这里把这两个加进去是重点  
public static List<List<Integer>> threeSumSmaller(int[] nums, int target) {
   List<Integer>ans=new ArrayList<>();
   List<List<Integer>>res=new ArrayList<>();
   process(nums,target,0,ans,res);
   return res;
}

private static void process(int[] nums, int target, int index, List<Integer> ans, List<List<Integer>> res) {
   if (index==nums.length){
    根据题目条件是否加入  然后看他要什么自己处理下就行
      if (ans.size()==3){
         res.add(new ArrayList<>(ans));
      }
      return;
   }
   if (ans.size()==3){
      /**
       * 加逻辑   根据题目条件是否加
       */
      res.add(new ArrayList<>(ans));
      return;
   }
   for (int i=index;i<nums.length;i++){
      ans.add(nums[i]);
      process(nums,target,i+1,ans,res);
      ans.remove(ans.size()-1);
   }
}

动态规划怎么想

  • 题目中明显是有尝试的
  • 数组有子序列的 暴力递归里面加for循环大概率的 以每一个i开始算结果

脑子里面定义好子问题 利用子问题去算答案. 遇到要遍历某一个位子的应该是要加for循环了,就是尝试

记忆化搜索 第一个for循环算的不是动态规划里面的 是处理规划出来的东西,哪一个最长 递归处理的是

当来到i位置的时候,以他为开头最长是多少,对把 但是最长的不一定是以他开头

 public static int lengthOfLIS(int[] nums) {
		int ans=0;
		for (int i=0;i<nums.length;i++){
			ans=Math.max(ans,process(nums,i));
		}
		return ans;
	}

	private static int process(int[] nums, int i) {
		if (i==nums.length){
			return 0;
		}
		int count=0;
		for (int j=i+1;j<nums.length;j++){
			if (nums[j]>nums[i]){
				count=Math.max(count,process(nums,j));
			}

		}
		return ++count;
	}

动态规划

public static int lengthOfLIS(int[] nums) {
		int[] memory=new int[nums.length];
		for (int i=0;i<memory.length;i++){
			memory[i]=1;
		}
		int ans=0;
		for (int i= nums.length-1;i>=0;i--){
			int count=0;
			for (int j=i+1;j<nums.length;j++){
				if (nums[j]>nums[i]){
					count=Math.max(count,memory[j]);
				}

			}
			memory[i]=++count;
			ans=Math.max(ans,	memory[i]);
		}
		return ans;
		
	}

image.png

看到找数字想二分

题目中是搜索且空间要求低 就查没直接告诉你用二分了

不用 + 怎么算相加

进位加上不进位相加 异或运算本质就是不进位相加 加上while 循环保证没有进位了 可能两个相加还存在进位

public static int getSum(int a, int b) {

   while (b!=0){
      int carry=(a&b)<<1;
      a=a^b;
      b=carry;
   }
   return a;
}

洗牌算法

原地打乱数组 没到一个位置就交换随机一个index下标

class Solution {
	int[]nums;
	int[]help;
   Random random=new Random();

	public Solution(int[] nums) {
		this.nums=nums;
		help=nums.clone();

	}
	public int range(int max,int min){
		return random.nextInt(max-min)+min;
	}

	/** Resets the array to its original configuration and return it. */
	public int[] reset() {

    return nums;
	}
	public void swap(int[]help ,int i,int j){
		int temp=help[i];
		help[i]=help[j];
		help[j]=temp;
	}

	/** Returns a random shuffling of the array. */
	public int[] shuffle() {
      for (int i=0;i<help.length;i++){
      	swap(help,i,range(help.length,i));
      }
      return help;
	}


}

象什么前k个什么的第一时间想到堆

回溯算法套公式后发现内存溢出 开始改写

public static int longestIncreasingPath(int[][] matrix) {
   int[][] direct={{1,0},{-1,0},{0,-1},{0,1},};
   boolean[][]help=new boolean[matrix.length][matrix[0].length];

       List<Integer> list=new ArrayList<>();
       List<List<Integer>>res=new ArrayList<>();
       for (int i=0;i<matrix.length;i++){
           for (int j=0;j<matrix[0].length;j++){
           dps(list,res,matrix,i,j,help,direct);
        }
       }

       int size=1;
       for (List<Integer> i:res){
           size=Math.max(size,i.size()+1);
       }
       return size;
}

private static void dps(List<Integer> list, List<List<Integer>> res, int[][] matrix, int i, int j, boolean[][] help, int[][] direct) {

   if (!judge(matrix,i,j,help,direct)){
      res.add(new ArrayList<>(list));
      return;
   }




   for (int[] index:direct){
      int newI=i+index[0];
      int newJ=j+index[1];
      if (newI>=0&&newJ>=0&&newI<matrix.length&&newJ<matrix[0].length&&!help[newI][newJ]&&
        matrix[newI][newJ]>matrix[i][j]){
         list.add(matrix[newI][newJ]);
         help[newI][newJ]=true;
         dps(list,res,matrix,newI,newJ,help,direct);
         list.remove(list.size()-1);
         help[newI][newJ]=false;
      }
     }

}
public static boolean judge(int[][] matrix, int i, int j, boolean[][] help, int[][] direct){
   for (int[] index:direct){
      int newI=i+index[0];
      int newJ=j+index[1];
      if (newI>=0&&newJ>=0&&newI<matrix.length&&newJ<matrix[0].length&&!help[newI][newJ]&&
      matrix[newI][newJ]>matrix[i][j]
      ){
         return true;
      }
   }
   return false;
}

第一题

image.png

这一题的暴力方法很好找,就是两个for循环,但是时间都花费再了找第二个数上,如果是数组找数,是有等式的例如相加这种 利用hashmap 非常方便,但是只对两数累加有效果,因为三数累加的时候会改变值,对hanmap改进把数值和value对调 方便拿取 ,做到o(N)对所有值都查一遍,有就找到,没有就放入,无脑。

image.png

第三题

image.png

public static int lengthOfLongestSubstring(String s) {

   int max=0;
   HashMap<Character,Integer>map=new HashMap<>();
   for (int i=0 , j=0;j<s.length();j++){
      if (map.containsKey(s.charAt(j))){
         i=Math.max(i,map.get(s.charAt(j))+1);
      }
      map.put(s.charAt(j),j);
      max=Math.max(max,j-i+1);

   }
   return max;
}

就是利用了hashmap的数组下标计算,两个临近相等字符串长度

 第四题

image.png

中位数的下标可算 不需要去二分

class Solution {
   public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
      int l1=nums1.length;
      int l2=nums2.length;
      boolean flag=false;
      int mid=0;

      int i=0;
      int j=0;
      int z=0;
		double[] res=new double[l2+l1];
      while (i<nums1.length&&j<nums2.length){

      	if (nums1[i]<=nums2[j]){
      		res[z++]=nums1[i];
      		i++;
        }
      	else  {
	        res[z++]=nums2[j];
	        j++;
        }



      }
      while (j<nums2.length){
	      res[z++]=nums2[j++];
      }
		while (i<=nums1.length-1){
			res[z++]=nums1[i++];

		}
		if ((l2+l1)%2==0){
			mid=((l2+l1)/2)-1;
		return (res[mid]+res[mid+1])/2;
		}else {
			mid=((l2+l1)/2);
			return res[mid];
		}


	}

第五题

image.png

public static String manacher1(String s) {
   if (s == null || s.length() == 0) {
      return null;
   }
   // "12132" -> "#1#2#1#3#2#"
   char[] str = manacherString(s);
   // 回文半径的大小
   int[] pArr = new int[str.length];
   int C = -1;
   // 讲述中:R代表最右的扩成功的位置
   // coding:最右的扩成功位置的,再下一个位置
   int R = -1;
   int max = Integer.MIN_VALUE;
   HashMap<Integer,Integer>map=new HashMap<>();
   for (int i = 0; i < str.length; i++) { // 0 1 2
      // R第一个违规的位置,i>= R
      // i位置扩出来的答案,i位置扩的区域,至少是多大。
      pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
      while (i + pArr[i] < str.length && i - pArr[i] > -1) {
         if (str[i + pArr[i]] == str[i - pArr[i]])
         {
            pArr[i]++;

         }

         else {
            break;
         }
      }
      if (i + pArr[i] > R) {
         R = i + pArr[i];
         C = i;
      }
      max=Math.max(max,pArr[i]);

   }
   int temp=0;
   for (int i=0;i< pArr.length;i++){
      if (pArr[i]==max){
         temp=(i-1)/2;
      }
   }
   int start=0;
   int end=0;
   if ((max-1)%2==0){
      start=(temp-(max-1)/2)+1;
      end=(temp+(max-1)/2);
   }
   else {
      start=(temp-(max-1)/2);
      end=(temp+(max-1)/2);
   }
   String substring = s.substring(start, end+1);


   return substring;
}
 

image.png

第六题

image.png

就是贪心算法 双指针 求最大面级 真是垃圾题目 没意思

image.png

image.png

第七题

image.png

字符串最长前缀和 就比来比去 就行了 。千万别往前缀树那边想

public String longestCommonPrefix(String[] strs) {
   if (strs == null || strs.length == 0) {
      return "";
   }
   String prefix=strs[0];
   for (int i=1;i<strs.length;i++){
      prefix=longPrefix(prefix,strs[i]);
   }
   return prefix;

}

private String longPrefix(String prefix, String str) {
   int i=0;
   int j=0;
   while (i<prefix.length()&&j<str.length()){
      if (prefix.charAt(i)==str.charAt(j)){
         i++;
         j++;
      }else {

         break;
      }
   }
   return prefix.substring(0,i);
}

image.png

第七题

image.png

class Solution {
  public  ListNode removeNthFromEnd(ListNode head, int n) {
     if (n<0||head==null){
     	return new ListNode();
     }
		ListNode slow=head;
		ListNode quick=head;
		for (int i=0;i<n&&quick.next!=null;i++){
			quick=quick.next;
		}

     while (quick.next!=null){
     	quick=quick.next;
     	slow=slow.next;
     }


	 if (slow.next!=null){
		
		if (!distence(slow,quick,n)){
			return head.next;
		}
		else {
			slow.next=slow.next.next;
			return head;
		}
	 }
	 else {
           return null;
	 }





	}
    private  boolean distence(ListNode slow, ListNode quick, int n) {
		int count=0;
		for (int i=0;i<n;i++){
			slow=slow.next;
			count++;
			if (slow==quick){
				break;
			}
		}
		return count==n;


	}
    }

image.png

第八题

image.png

class Solution {
   public  boolean isValid(String s) {
		if (s==null||s.length()<1){
			return false;
		}
		Stack<Character>stack=new Stack<>();
		HashMap<Character,Character>map=new HashMap<>();
	map.put('(',')');
		map.put('{','}');
		map.put('[',']');
		map.put(')','1');
		map.put('}','2');
		map.put(']','3');
		char[] chars = s.toCharArray();
		for (int i=0;i<chars.length;i++){
            if (stack.isEmpty()){
            	stack.push(chars[i]);
            }
           
            else {
            	if (chars[i]==map.get(stack.peek())){
            		stack.pop();
	            }
	            else {
		            stack.push(chars[i]);
	            }
            }
		}
		if (stack.isEmpty()){
			return true;
		}
		return false;
	}

}

image.png

第9题

image.png

就是范围性的尝试,我计算出的第一个点+ 后面计算好的点 而且一定要用next把链表连起来

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
     if (l1==null){
     	return l2;
     }
     else if (l2==null){
     	return l1;
     }
    else if (l1.val<= l2.val){
     	l1.next=mergeTwoLists(l1.next,l2);
     	return l1;
     }
     else {
     	l2.next=mergeTwoLists(l1,l2.next);
     	return l2;
     }
	}

image.png

第10题

image.png 注意等号的位置 要确保能取到边界值 不象以前一直向一边移动,这次可能向两边移动

 public int search(int[] nums, int target) {
		int n = nums.length;
		if (n == 0) {
			return -1;
		}
		if (n == 1) {
			return nums[0] == target ? 0 : -1;
		}

  int i=0;
  int j=n-1;

  while (i<=j){
	  int mid=i+((j-i)>>1);
	  if (nums[mid]==target){
	  	return mid;
	  }
  	if (nums[mid]>=nums[0]){
  		if (nums[0]<=target&&target<nums[mid]){
  			j=mid-1;
	    }else {
  			i=mid+1;
	    }
    }else {
	    if (nums[mid]<target&&target<=nums[n-1]){
		    i=mid+1;

	    }else {
		    j=mid-1;
	    }
    }
  }
  return -1;
	}

image.png

第11题

image.png

这题就是标准的二分找边界值问题 找到了这个值然后向两边扩展,注意边界值取值就行了

 public  int[] searchRange(int[] nums, int target) {
		if (nums==null||nums.length==0){
			return new int[]{-1,-1};
		}
		int n= nums.length;
		int i=0;
		int j=n-1;
		int mid;
		while (i<=j){
			mid=i+((j-i)>>1);
			if (nums[mid]==target){
				return process(nums,target,mid);
			}
			else if (target>=nums[mid]){
				i=mid+1;
			}
			else {
				j=mid-1;
			}


		}

          return new int[]{-1,-1};
	}

	private  int[] process(int[] nums, int target, int mid) {
		int i=mid;
		int j=mid;
		while (i>0&&j< nums.length-1&&nums[--i]==target&&nums[++j]==target){



		}
			while (i>0&&nums[i]==target){
			i--;
		}
		while (j< nums.length-1&&nums[j]==target){
			j++;
		}
      if (i==j||(i==0&&j== nums.length-1&&nums[i]==target&&nums[j]==target)){
			return new int[]{i,j};
		}
		else if (i==0&&nums[j]!=target&&nums[i]==target){
			return new int[]{i,j-1};
		}
		else if (nums[i]!=target&&j== nums.length-1&&nums[j]==target){
			return new int[]{i+1,j};
		}
		else {
			return new int[]{i+1,j-1};
		}

image.png

第12题

image.png

回溯算法的应用,用过的数字不能再用了,for循环的i是必须的变的 不然就重复了,

 	public List<List<Integer>> permute(int[] nums) {

		if (nums==null||nums.length==0){
			return null;
		}
		int n=nums.length;
		List<List<Integer>>res=new ArrayList<>();
		List<Integer>output=new ArrayList<>();
		for (int i:nums){
			output.add(i);
		}
		backtrack(res,output,n,0);
		return res;


	}

	private void backtrack(List<List<Integer>> res, List<Integer> output, int n, int index) {
		if (index==n){
			res.add(new ArrayList<>(output));
		}
		for (int i=index;i<n;i++){
			Collections.swap(output,i,index);
			backtrack(res,output,n,index+1);
			Collections.swap(output,i,index);
		}

	}
}

image.png

第13题 小贪心

image.png

子数组最大和 只要是前累加和是小于0的直接舍弃 重新开始

public  int maxSubArray(int[] nums) {
	if (nums==null||nums.length==0){
		return 0;
	}
	int max=nums[0];
	int pre=0;


	for (int i=0;i<nums.length;i++){
//		pre=Math.max(nums[i],nums[i]+pre );

		if (pre<0){
			pre=nums[i];
		}
	else {
			pre=pre+nums[i];
		}
	max=Math.max(max,pre);

	}
return max;
	}
}

image.png

第14题

image.png

class Solution {
    public int climbStairs(int n) {
if (n < 1) {
			return 0;
		}
		if (n == 1 ) {
			return 1;
		}
        if(n==2){
            return 2;
        }
		// [ 1 ,1 ]
		// [ 1, 0 ]
		int[][] base = { 
				{ 1, 1 }, 
				{ 1, 0 } 
				};
		int[][] res = matrixPower(base, n - 1);
		return res[0][0] + res[1][0];
    }
    public  int[][] matrixPower(int[][] m, int p) {
		int[][] res = new int[m.length][m[0].length];
		for (int i = 0; i < res.length; i++) {
			res[i][i] = 1;
		}
		// res = 矩阵中的1
		int[][] t = m;// 矩阵1次方
		for (; p != 0; p >>= 1) {
			if ((p & 1) != 0) {
				res = muliMatrix(res, t);
			}
			t = muliMatrix(t, t);
		}
		return res;
	}

	// 两个矩阵乘完之后的结果返回
	public  int[][] muliMatrix(int[][] m1, int[][] m2) {
		int[][] res = new int[m1.length][m2[0].length];
		for (int i = 0; i < m1.length; i++) {
			for (int j = 0; j < m2[0].length; j++) {
				for (int k = 0; k < m2.length; k++) {
					res[i][j] += m1[i][k] * m2[k][j];
				}
			}
		}
		return res;
	}

}

image.png

第15题 矩阵轨迹问题

image.png

关键在于画点找轨迹 不能执着于点位置的变化

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
		List<Integer> list = new ArrayList<>();
		int lr = 0;
		int lc = 0;
		int rr = matrix.length - 1;
		int rc = matrix[0].length - 1;
		while (lr <= rr && lc <= rc) {
			printCircle(matrix, lr++, lc++, rr--, rc--, list);
		}
		return list;
	}

	private void printCircle(int[][] matrix, int a, int b, int c, int d, List<Integer> list) {
		if (a==c){
			for (int i=b;i<=d;i++){
				list.add(matrix[a][i]);
			}
		}
		else if (b==d){
			for (int i=a;i<=c;i++){
				list.add(matrix[i][b]);
			}
		}
		else {
			int curRow=a;
			int curCol=b;
			while (curCol!=d){
				list.add(matrix[a][curCol++]);
			}
			while (curRow!=c){
				list.add(matrix[curRow++][d]);
			}
			while (curCol!=b){
				list.add(matrix[c][curCol--]);
			}
			while (curRow!=a){
				list.add(matrix[curRow--][b]);  
			}
		}

	}

}

image.png

第16题 回溯法 删不是换

image.png

public static List<List<Integer>> subsets(int[] nums) {
   List<List<Integer>>res=new ArrayList<>();
   List<Integer>list=new ArrayList<>();

        process(nums,0,res,list);
        return res;
}

private  static void process(int[] nums, int i, List<List<Integer>> res, List<Integer> list) {

   if (i== nums.length){
      res.add(new ArrayList<>(list));
      return;
   }
   res.add(new ArrayList<>(list));
   for (int index=i;index<nums.length;index++){

      list.add(nums[index]);

      process(nums,index+1,res,list);
      list.remove(list.size()-1);
   }

}

image.png

回溯算法

找到就break返回 没找到继续上下左右来来回回

image.png

 public boolean exist(char[][] board, String word) {
        int n=board.length;
        int m=board[0].length;
        boolean[][] help=new boolean[n][m];
        for (int i=0;i<n;i++){
        	for (int j=0;j<m;j++){
        		boolean flag= check(board,word,help,0,i,j);
        		if (flag) {
			        return true;
		        }
	        }
        }
        return false;
	}

	private boolean check(char[][] board, String word, boolean[][] help, int length, int i, int j) {
		if (board[i][j]!=word.charAt(length)){
			return false;
		}else if (length==word.length()-1){
			return true;
		}
		int[][] direction={{0,1},{1,0},{-1,0},{0,-1}};
		help[i][j]=true;
		boolean flag=false;
		for (int[] dire:direction){
			int newi=i+dire[0];
			 int newj=j+dire[1];
			if (newi>=0&&newj>=0&&newi< board.length&&newj<board[0].length&&!help[newi][newj]){
				flag=check(board,word,help,length+1,newi,newj);
				if (flag){
				
					break;
				}
			}


		}
		help[i][j]=false;
		return flag;
	}

单挑栈

因为面级最大就是扩展 具体就是到第一个违规的地方 所有找的是两边最小 不是两边最大

image.png

 public int largestRectangleArea(int[] heights) {
		Stack<Integer>stack=new Stack<>();
		int max=0;
		for (int i=0;i<heights.length;i++){
			if (stack.isEmpty()){
				stack.push(i);
			}else {
				while (!stack.isEmpty()&&heights[i]<heights[stack.peek()]){
					Integer pop = stack.pop();
					if (stack.isEmpty()){
					max=Math.max(max,(i)*heights[pop]);
					}
					else {
						max=Math.max(max,(i-stack.peek()-1)*heights[pop]);
					}


				}

				stack.push(i);
			}
		}
        while (!stack.isEmpty()){
			int r=heights.length;
			Integer pop = stack.pop();
			max=stack.isEmpty()?Math.max(max,r*heights[pop]):Math.max(max,(r-stack.peek()-1)*heights[pop]);
		}
		return max;

	}

image.png

字符串中找到出现字母最短串

第一段:用滑动窗口,当出现全部字母时窗口形成 第二段:窗口前缩到最短包含 第三段:窗口放弃一个字母 继续前进搜索下一个字母

image.png

合并区间

用array的特性去sort 用比较器,最后在转化回来 merged.toArray(new int[merged.size()][]);

然后就是看当前区间的左如果在数组中最后一个右端点的右边不可能重合,反之如果重合 去最大值

image.png

二叉树不按照套路的

因为是要向两边要却不一样的信息,没有统一的信息

image.png

public boolean isSymmetric(TreeNode root) {
   return check(root,root);
}

private boolean check(TreeNode root, TreeNode root1) {
   if (root==null&&root1==null){
      return true;
   }
   if (root==null||root1==null){
      return false;
   }
   return root.val==root1.val&&check(root.left,root1.right)&&check(root.right,root1.left);
}

image.png

二叉树按层遍历怎么记录没层的数量

细节就是用当前的queue的size() 提前设置好,当该层扔完了,然后重新计算

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/547238d7c83d4220b631cb1066b990d6~tplv-k3u1fbpfcp-watermark.image)
 public List<List<Integer>> levelOrder(TreeNode root) {
		if (root==null){
			return new ArrayList<>();
		}
		List<List<Integer>>res=new ArrayList<>();
		Queue<TreeNode>queue=new LinkedList<>();
		queue.offer(root);
		while (!queue.isEmpty()){
			int currentSize=queue.size();
			List<Integer>  list=new ArrayList<>();
			for (int i=0;i<currentSize;i++){
				
				TreeNode poll = queue.poll();
				list.add(poll.val);
				if (poll.left!=null){
					queue.offer(poll.left);
				}
				if (poll.right!=null){
					queue.offer(poll.right);
				}
			}
			
			res.add(new ArrayList<>(list));
		}
		return res;

	}

image.png

简单回文新思路

利用stringbuild 的reverse函数直接翻一下对比 ,加上字符的处理函数简直不要太爽 且全部变成小写对比 真是无敌

 public boolean isPalindrome(String s) {
     StringBuilder str=new StringBuilder();
     int n=s.length();
     for (int i=0;i<s.length();i++){
	     char c = s.charAt(i);
	     if (Character.isLetterOrDigit(c)){
	     	str.append(Character.toLowerCase(c));
	     }
     }
		String s1 = str.toString();
     return s1.equals(str.reverse().toString());
	}

二叉树最大路径和问题 套路解决不了

看该点的左右选一个 然后更新所有值 左加右+当前的值,可以去0的

image.png

用hashmap检验环形列表

image.png

   public boolean hasCycle(ListNode head) {
        Set<ListNode> seen = new HashSet<ListNode>();
        while (head != null) {
            if (!seen.add(head)) {
                return true;
            }
            head = head.next;
        }
        return false;
    }

递归生成二叉树

套路都差不多的 先构建根节点 然后 左右节点 往递归里面传

public TreeNode sortedArrayToBST(int[] nums) {
      if (nums==null){
       return null;
      }
      return process(nums,0,nums.length-1);
}

private TreeNode process(int[] nums, int start, int end) {
   if (start>end){
       return null;
   }
   int mid=start+((end-start)>>1);
   TreeNode root=new TreeNode(nums[mid]);
   root.left=process(nums,start,mid-1);
   root.right=process(nums,mid+1,end);
   return root;

}

经典动态规划

关键就是这个决定要的东西需要加上去的不是你参数上加上就好了,

image.png

image.png

 public static int rob(int[] nums) {
        if (nums==null){
        	return 0;
        }
        int sum=0;
        for (int i:nums){
        	sum+=i;
        }
        int[][]dp=new int[nums.length+1][sum+1];
        for (int i=0;i< dp.length;i++){
        	for (int j =0;j<dp[0].length;j++){
        		dp[i][j]=-1;
	        }
        }
        return process(nums,0,0,dp);
	}

	private static int process(int[] nums, int i, int money,int[][]dp) {
		if (i>= nums.length){
			return 0;
		}
		if (dp[i][money]!=-1){
			return dp[i][money];
		}
		int p1 = process(nums, i + 1, money,dp);
		int p2=0;
		if (i+1<=nums.length){
			 int next = process(nums, i + 2, money + nums[i],dp);
			 p2=next+nums[i];
		}
		dp[i][money]=Math.max(p1,p2);
		return dp[i][money];

	}

自己写的反转列表秒杀全部人

想不到retuen啥 就直接把process返回

image.png

class Solution {
  public static ListNode reverseList(ListNode head) {
		ListNode process = process(null, head);
		return process;
	}

	private static ListNode process(ListNode pre,ListNode cur) {
		if (cur==null){
			return pre;
		}
		ListNode temp= cur.next;

		cur.next=pre;
	 return 	process(cur,temp);
	}
}

image.png

简简单单异或

image.png

 public int singleNumber(int[] nums) {
		int a=0;
     for (int i:nums){
     	a^=i;
     }
     return a;
	}

image.png

投票机制

因为题目要求了是大于一半 所有这个数的票数必然是最多的 所有票数多了就换人就好了。

image.png

 public int majorityElement(int[] nums) {
    int count=0;
    int people=nums[0];
    for (int i=0;i<nums.length;i++){
    	if (nums[i]==people){count++;}
    	else {
		    --count;
		    if (count<0&&nums[i]!=people){
			    people=nums[i];
                    count=1;
		    }
		    
	    }
    	
    }
    return people;
	}

image.png

数组交集 什么相交之类的可以先考虑hashmap

用hashmap就行

融合大法 回溯加上动态规划

遍历加上回溯 我要这个或者这个 假设到index我会有很多可能性,都试一下, 看这个是不是回文直接就用动态规划

image.png

List<List<String>>res=new ArrayList<>();
	List<String>ans=new ArrayList<>();
	int n;
	int[][]dp;
	 public List<List<String>> partition(String s) {
        if(s.length()<1){
        	return null;
        }
        dp=new int[s.length()+1][s.length()+1];
        n=s.length();
        dfs(s,0);
return res;
	}

	private void dfs(String s, int i) {
		if (i==n){
			res.add(new ArrayList<>(ans));
			return;
		}

		for (int j=i;j<s.length();j++){
			if (isPalindrome(s,i,j)==1){
				ans.add(s.substring(i,j+1));
				dfs(s,j+1);
				ans.remove(ans.size()-1);
			}

		}
	}

	private int isPalindrome(String s, int i, int j) {

		if (dp[i][j]!=0){
			return dp[i][j];
		}
		if (i>=j){
			dp[i][j]=1;
			return 1;
		}
		else if (s.charAt(i)!=s.charAt(j)){
			dp[i][j]=-1;
			return -1;
		}
		dp[i][j]=	isPalindrome(s,i+1,j-1);
		return dp[i][j];
	}

 矩阵改值但是会影响结果的 用特殊法

用特殊字符或者是 -1 2这中不影响结果的。

会议室问题和衍生问题

解决会议室问题需要用到堆 但需要手动的该值和获取全部的数据用自己写的堆会好一点 参数一般为数组 当遇到,有尝试类型的就直接上递归了没办法了。

多数组求和问题等于0

可用用hashmap来求负数 对半开 遍历完后map不用把数据删除

判断是不是每个数的幂次方

先模 然后除以这个数看是不是等于1 因为有些不是幂次方也能模的