=## 数组中的重复数字
- 方法一
利用set集合 或者 hashmap 内存占用大
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
for(int i =0;i<nums.length;i++){
if(set.contains(nums[i])){
return nums[i];
}
set.add(nums[i]);
}
return Integer.MAX_VALUE;
}
}
- 方法二 把数组视为哈希表(有一类问题是这么做的,但是会修改数组)
由于数组元素的值都在指定的范围内,这个范围恰恰好与数组的下标可以一一对应;
因此看到数值,就可以知道它应该放在什么位置,这里数字nums[i] 应该放在下标为 i 的位置上,这就像是我们人为编写了哈希函数,这个哈希函数的规则还特别简单;
而找到重复的数就是发生了哈希冲突;
类似问题还有「力扣」第 41 题: 缺失的第一个正数、「力扣」第 442 题: 数组中重复的数据、「力扣」第 448 题: 找到所有数组中消失的数字 。
分析:这个思路利用到了数组的元素值的范围恰好和数组的长度是一样的,因此数组本身可以当做哈希表来用。遍历一遍就可以找到重复值,但是修改了原始数组。
public class Solution {
public int findRepeatNumber(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
// 如果当前的数 nums[i] 没有在下标为 i 的位置上,就把它交换到下标 i 上
// 交换过来的数还得做相同的操作,因此这里使用 while
// 可以在此处将数组输出打印,观察程序运行流程
// System.out.println(Arrays.toString(nums));
while (nums[i] != i) {
if (nums[i] == nums[nums[i]]) {
// 如果下标为 nums[i] 的数值 nums[nums[i]] 它们二者相等
// 正好找到了重复的元素,将它返回
return nums[i];
}
swap(nums, i, nums[i]);
}
}
throw new IllegalArgumentException("数组中不存在重复数字!");
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
二维数组中的查找
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix==null){
return false;
}
int n = matrix.length;
if(n==0){
return false;
}
int m = matrix[0].length;
if(m==0){
return false;
}
int i = 0;
int j = m-1;
while(i<n&&j>=0){
if(matrix[i][j]>target){
j--;
}else if(matrix[i][j]<target){
i++;
}else{
return true;
}
}
return false;
}
}
替换空格
class Solution {
public String replaceSpace(String s) {
if(s.length()==0){
return "";
}
StringBuilder sb = new StringBuilder();
int n = s.length();
for(int i =0;i<n;i++){
if(s.charAt(i)==' '){
sb.append("%20");
}else{
sb.append(s.charAt(i));
}
}
return sb.toString();
}
}
从尾到头打印链表
- 借助栈
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
if(head==null){
return new int[0];
}
int n =0;
Stack<Integer> stack = new Stack<Integer>();
ListNode node = head;
while(node!=null){
n++;
stack.push(node.val);
node = node.next;
}
int[] ans = new int[n];
int i =0;
while(!stack.isEmpty()){
ans[i] = stack.pop();
i++;
}
return ans;
}
}
- 递归方法
class Solution {
ArrayList<Integer> tmp = new ArrayList<Integer>();
public int[] reversePrint(ListNode head) {
recur(head);
int[] res = new int[tmp.size()];
for(int i = 0; i < res.length; i++)
res[i] = tmp.get(i);
return res;
}
void recur(ListNode head) {
if(head == null) return;
recur(head.next);
tmp.add(head.val);
}
}
重建二叉树(根据前序和中序)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0||inorder.length==0){
return null;
}
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i =0;i< inorder.length;i++){
map.put(inorder[i],i);
}
return helper(preorder,0,preorder.length-1,inorder,0,inorder.length-1,map);
}
public TreeNode helper(int[] preorder,int start,int end,int[] inorder,int start1,int end1,HashMap<Integer,Integer> map){
if(end+1==start){
return null;
}
TreeNode root = new TreeNode(preorder[start]);
root.left = helper(preorder,start+1,start+map.get(preorder[start])-start1,inorder,start1,map.get(preorder[start])-1,map);
root.right = helper(preorder,start+map.get(preorder[start])-start1+1,end,inorder,map.get(preorder[start])+1,end1,map);
return root;
}
}
用两个栈实现队列
最开始想到的
class CQueue {
Stack<Integer> stack1 ;
Stack<Integer> stack2 ;
public CQueue() {
stack1 = new Stack<Integer>();
stack2 = new Stack<Integer>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(stack1.isEmpty()){
return -1;
}
while(!stack1.isEmpty()){
int temp = stack1.pop();
stack2.push(temp);
}
int ans = stack2.pop();
while(!stack2.isEmpty()){
int temp1 = stack2.pop();
stack1.push(temp1);
}
return ans;
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
后来看大佬解法,时间快不少。
class CQueue {
Stack<Integer> stack1 ;
Stack<Integer> stack2 ;
public CQueue() {
stack1 = new Stack<Integer>();
stack2 = new Stack<Integer>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
// if(stack1.isEmpty()){
// return -1;
// }
// while(!stack1.isEmpty()){
// int temp = stack1.pop();
// stack2.push(temp);
// }
// int ans = stack2.pop();
// while(!stack2.isEmpty()){
// int temp1 = stack2.pop();
// stack1.push(temp1);
// }
// return ans;
if(!stack2.isEmpty()){
return stack2.pop();
}
if(stack1.isEmpty()){
return -1;
}
while(!stack1.isEmpty()){
int temp1 = stack1.pop();
stack2.push(temp1);
}
return stack2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
斐波那契数列
class Solution { //超时了 O(2^n)
public int fib(int n) {
if(n==0){
return 0;
}
if(n==1||n==2){
return 1;
}
return (fib(n-1)+fib(n-2))%1000000007;
}
}
public int fib(int n) { //从左到右依次求值,O(N) 注意取余
if(n==0){ //动态规划 ,并且空间优化后的
return 0;
}
if(n==1||n==2){
return 1;
}
int temp =0;
int res =1 ;
int pre = 1;
for(int i = 3;i<=n;i++){
temp = res;
res = (res+pre)%1000000007;
pre = temp;
}
return res;
}
}
矩阵快速幂 O(logn) 但是取模有问题
public int fib(int n) {
if(n==0){
return 0;
}
if(n==1||n==2){
return 1;
}
int[][] base = {{1,1},{1,0}};
int[][] res = matrixPower(base,n-2);
return (res[0][0]+res[1][0])%1000000007;
}
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;
}
int[][] temp = m;
for(;p!=0;p>>=1){
if((p&1)!=0){
res = muliMatrix(res,temp);
}
temp = muliMatrix(temp,temp);
}
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])%1000000007+res[i][j])%1000000007;
}
}
}
return res;
}
青蛙跳台阶问题
和上一个完全一样。
旋转数组的最小数字
本题涉及 4 道「搜索旋转排序数组」题:
LeetCode 33 题:搜索旋转排序数组
LeetCode 81 题:搜索旋转排序数组-ii 允许重复
LeetCode 153 题:寻找旋转排序数组中的最小值
LeetCode 154 题:寻找旋转排序数组中的最小值-ii 允许重复
搜索旋转排序数组 (不重复)
class Solution {
public int search(int[] nums, int target) {
if(nums==null||nums.length==0){
return -1;
}
int start = 0;
int end = nums.length-1;
while(start<=end){
int mid = (start+end)/2;
if(nums[mid]==target){
return mid;
}
if(nums[start]<=nums[mid]){
if(nums[start]<=target&&nums[mid]>target){
end = mid-1;
}else{
start = mid+1;
}
}else{
if(nums[mid]<target&&target<=nums[end]){
start = mid+1;
}else{
end = mid -1;
}
}
}
return -1;
}
}
搜索 重复
class Solution {
public boolean search(int[] nums, int target) {
if(nums==null||nums.length==0){
return false;
}
int start = 0;
int end = nums.length-1;
while(start<=end){
int mid = (start+end)/2;
if(nums[mid]==target){
return true;
}
if(nums[start]==nums[mid]){
start++;
continue;
}
if(nums[start]<nums[mid]){
if(nums[start]<=target&&nums[mid]>target){
end = mid-1;
}else{
start = mid+1;
}
}else{
if(nums[mid]<target&&target<=nums[end]){
start = mid+1;
}else{
end = mid -1;
}
}
}
return false;
}
}
最小值 允许重复
class Solution { // 也可以 比较mid 和right 就不需要比较end和 start 了
public int findMin(int[] nums) {
if(nums==null||nums.length==0){
return -1;
}
int end = nums.length-1;
int start = 0;
while(start<=end){
if(nums[start]<=nums[end]){
return nums[start];
}
int mid = (end+start)/2;
if(nums[mid]>=nums[start]){
start= mid+1; //注意加一
}else {
end = mid; //注意不加一
}
}
return -1;
}
}
最小值 允许重复
//这里用的是mid和right 比较 , 因为要去重,right--,
class Solution {
public int minArray(int[] numbers) {
if(numbers==null||numbers.length==0){
return -1;
}
int end = numbers.length-1;
int start = 0;
while(start<end){
int mid = (end+start)/2;
if(numbers[mid]>numbers[end]){
start= mid+1;
}else if(numbers[mid]<numbers[end]){
end = mid;
}else{
end--;
}
}
return numbers[start];
}
}
- 总结
在旋转排序数组中进行二分查找时,无论是搜索特定值,还是搜索最小值,都需要在左右两个区间里,找到「连续递增」的那个区间。
判断区间是否「连续递增」,只需比较区间边界值:如果 nums[left] <= nums[mid],则区间 [left,mid] 连续递增;反之,区间 [mid,right] 连续递增。但是上述判断仅适用于数组中不含重复元素的情况,如果数组中包含重复元素,那么在 nums[left]==nums[mid] 时将退化为线性查找。
找到「连续递增」的区间后,问题就变得简单了许多:
33 题,查找特定值:只需要判断目标值在「连续递增」区间内还是区间外。比如当区间 [left,mid] 连续递增时,若目标值位于该区间内,则 right = mid-1;若目标值位于该区间外,则 left = mid+1。如果是区间 [mid,right] 连续递增,也可以用类似的方法收缩区间
153 题,查找最小值:只需要排除左侧或者右侧的一段「连续区间」,使得 [left,right] 不连续,就可以找到最小值
矩阵中的路径
class Solution { //回溯的流程 套路固定
private int[][] directions = {{-1,0},{0,1},{1,0},{0,-1}} ;
public boolean exist(char[][] board, String word) {
int m = board.length;
int n = board[0].length;
boolean[][] mark = new boolean[m][n];
for(int i = 0;i<m;i++){
for(int j =0;j<n;j++){
if(dfs(i,j,board,mark,word,0)){
return true;
}
}
}
return false;
}
public boolean dfs(int x,int y,char[][] board,boolean[][] mark,String word,int start){
if(start==word.length()-1){
return word.charAt(word.length()-1)==board[x][y];
}else{
if(word.charAt(start)==board[x][y]){
mark[x][y] = true;
for(int k=0;k<4;k++){
int newX = x+ directions[k][0];
int newY = y+directions[k][1];
if(inArea(newX,newY,board.length,board[0].length)&&!mark[newX][newY]){
if(dfs(newX,newY,board,mark,word,start+1)){
return true;
}
}
}
mark[x][y] = false;
}
return false;
}
}
public boolean inArea(int x,int y ,int m, int n){
return x<m&&y<n&&x>=0&&y>=0;
}
}
机器人的运动范围
class Solution { //做法和上一题类似 ,只不过不用回溯 普通dfs 还可以优化只遍历 右面和下面的
public int movingCount(int m, int n, int k) {
if(m<=0||n<=0||k<0){
return 0;
}
boolean[][] mark = new boolean[m][n];
return dfs(0,0,m,n,mark,k);
}
public int dfs(int x,int y,int m,int n,boolean[][] mark,int k){
int count = 0;
if(check(x,y,m,n,k)&&!mark[x][y]){
mark[x][y] = true;
count = 1 +dfs(x+1,y,m,n,mark,k) +dfs(x-1,y,m,n,mark,k) +dfs(x,y+1,m,n,mark,k)
+dfs(x,y-1,m,n,mark,k);
}
return count;
}
public boolean check(int x,int y,int m,int n,int k ){
boolean result = (sum(x)+sum(y))<=k;
return result&&x<m&&y<n&&x>=0&&y>=0;
}
public int sum(int x){
int res = 0;
while(x>0){
res = res+ x%10;
x= x/10;
}
return res;
}
}
剪绳子
数学推理:
class Solution { //基于两个准则 1:相等时乘积最大
// 2:因数为3时乘积最大
public int integerBreak(int n) {
if(n<=3){
return n-1 ;
}
int a= n/3;
int b = n%3;
if(b==0){
return (int)Math.pow(3,a);
}else if(b==1){
return (int)(Math.pow(3,a-1))*4;
}else{
return (int)(Math.pow(3,a))*2;
}
}
}
动态规划
int[] dp = new int[n+1];
dp[0]=1;
dp[1] = 1;
for(int i =2;i<=n;i++){
for(int j =1;j<i;j++){
dp[i] = max(dp[i],j*dp[i-j],j*(i-j));
}
}
return dp[n];
}
public int max(int a,int b,int c){
return Math.max(a,Math.max(b,c));
}
剪绳子-Ⅱ
这个题和剪绳子I一样的描述,就是数据范围变大了。剪绳子可以用动态规划或者贪心做,这道题对于使用DP难度就增大了一些,因为数据范围变得比较大时,long已经不足以去存储中间结果的状态,但是由于DP做法是枚举各种剪的情况然后取最大值,因此只能通过使用BigInteger的方法去做,这点评论区已经有人给出了解答。
那么这个题范围变大的本意是想让我们使用贪心算法能更好的求解(毕竟BigInteger使用起来麻烦,贪心没有数据溢出的问题,它是找当下的最优解,不需要比较,中间结果可以直接取模)。
用贪心(即数学推理)
if(n == 2) {
return 1;
}
if(n == 3){
return 2;
}
int mod = (int)1e9 + 7;
long res = 1;
while(n > 4) {
res *= 3;
res %= mod;
n -= 3;
}
return (int)(res * n % mod);
dp(也借助了数学推理)
long[] dp = new long[1001];
dp[1] =1;
dp[2] =1;
dp[3] =2;
dp[4] = 4;
dp[5] = 6;
dp[6] = 9;
for(int i=7;i<=n;i++){
dp[i]=(dp[i-3]*3)%1000000007;
}
return (int)dp[n];
二级制中的1的个数
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count =0;
while(n!=0){ //是否等于0;不能用大于0做判断
if((n&1)==1){
count++;
}
n>>>=1; //要用无符号右移
}
return count;
}
}
数值的整数次方
class Solution { //快速幂! 循环形式
public double myPow(double x, int n) {
if(n==0){
return 1.0;
}
double res = 1.0;
long N=n; //重要!
if(N<0){
x=1/x;
N=-N;
}
for(;N>0;N>>=1){
if((N&1)!=0){
res =res*x;
}
x= x*x;
}
return res;
}
}
// 递归形式
if(n==0){
return 1.0;
}
double res = 1.0;
long N=n; //重要!
if(N<0){
x=1/x;
N=-N;
}
return fastPow(x,N);
}
public double fastPow(double x, long n){
if(n==0){
return 1.0;
}
double cur = fastPow(x,n/2);
if(n%2==0){
return cur*cur;
}else{
return cur*cur*x;
}
}
打印从1到最大的n位数
class Solution { //简单做法,可以用快速幂代替Math.pow()
public int[] printNumbers(int n) {
int end = (int)Math.pow(10,n) - 1;
int[] ans = new int[end];
for(int i =0;i<end;i++){
ans[i] = i+1;
}
return ans;
}
}
//考虑大数问题 用字符串数组代替大数
public void print(int n){
if(n<=0){
return;
}
//必须要用字符数组防止大数
char[] c = new char[n];
for(int i=0; i<n; i++){
c[i] = '0';
}
while(!increment(c)){
digitsPrint(c);
}
}
private boolean increment(char[] c){
boolean overflow = false;
//因为是加1,当作从小数进位了1.
int carry = 1;
//从个位开始计算
for(int i=c.length-1; i>=0; i--){
int sum = c[i] - '0' + carry;
//如果进位了
if(sum == 10){
if(i == 0){
overflow = true;
break;
}
carry = 1;
c[i] = '0';
}else{//如果没进位
c[i] = (char)('0' + sum);
break;
}
}
return overflow;
}
private void digitsPrint(char[] c){
int index = 0;
while(c[index] == '0'){
index++;
}
for(int i=index; i<c.length; i++){
System.out.print(c[i]);
}
System.out.println();
}
public void print2(int n){
if(n <= 0)return; //利用回溯,求出全排列
List<String> result = new ArrayList<String>();
String line = "";
//用n控制位数
for(int i=1; i<=n; i++){
find(result, line, 0, n);
}
for(String s : result){
System.out.println(s);
}
}
private void find(List<String> result, String line, int level, int border){
//每一位添加完毕后保存
if(level >= border){
result.add(new String(line));
return;
}
//用户保护原始的line,即保护现场(可百度这个关键词)
String temp = new String(line);
for(int i=0; i<=9; i++){
//第一位不能为0
if(level == 0 && i == 0){
continue;
}else{
//当前位添加
line += i;
//继续添加下一位
find(result, line, level+1, border);
//让line恢复,进入下次for循环
line = temp;
}
}
}
删除链表节点
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head==null){
return head;
}
if(head.val==val){
return head.next;
}
if(head.next==null){
return head;
}
ListNode cur = head;
ListNode pre = new ListNode(-1);
pre.next = head;
while(pre.next!=null&&cur!=null){
if(cur.val==val){
pre.next = cur.next;
return head;
}else{
pre = cur;
cur= cur.next;
}
}
return head;
}
}
正则表达式匹配
//递归的方法 如果没有特殊符号,就依次比较 首字母,如果有*,分俩种情况: 1.x*不匹配,则p截断.2,首字母匹配,则s截断
class Solution {
public boolean isMatch(String s, String p) {
if(p.isEmpty()){
return s.isEmpty();
}
boolean headMatched = !s.isEmpty()&&((s.charAt(0)==p.charAt(0))||(p.charAt(0)=='.'));
if(p.length()>=2&&p.charAt(1)=='*'){
return isMatch(s,p.substring(2))||(headMatched&&isMatch(s.substring(1),p));
} else if(headMatched){
return isMatch(s.substring(1),p.substring(1));
}else{
return false;
}
}
}
//动态规划 ,思路和递归一致
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[0][0] = true;
for(int k=2;k<=p.length();k++){
dp[0][k] = (p.charAt(k-1)=='*')&&dp[0][k-2];
}
for(int i =0;i<s.length();i++){
for(int j = 0;j<p.length();j++){
if(p.charAt(j)=='*'){
dp[i+1][j+1] = dp[i+1][j-1]||(dp[i][j+1]&&headMatched(s,p,i,j-1));
}else{
dp[i+1][j+1] = dp[i][j]&&headMatched(s,p,i,j);
}
}
}
return dp[s.length()][p.length()];
}
public boolean headMatched(String s,String p,int i ,int j){
return (p.charAt(j)=='.')||(s.charAt(i)==p.charAt(j));
}
类似的题: 通配符匹配
//双指针贪心
public boolean isMatch(String s, String p) {
if (p==null||p.isEmpty())return s==null||s.isEmpty();
int i=0,j=0,istart=-1,jstart=-1,slen=s.length(),plen=p.length();
//判断s的所有字符是否匹配
while (i<slen){
//三种匹配成功情况以及匹配失败返回false
if (j<plen&&(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?')){
i++;
j++;
}else if (j<plen&&p.charAt(j)=='*'){
istart=i;
jstart=j++;
}else if (istart>-1){
i=++istart;
j=jstart+1;
}else {
return false;
}
}
//s中的字符都判断完毕,则认为s为空,此时需要p为空或者p中只剩下星号的时候,才能成功匹配。
//如果p中剩余的都是*,则可以移除剩余的*
while (j<plen&&p.charAt(j)=='*')j++;
return j==plen;
}
//动态规划
public boolean isMatch(String s, String p) {
if (p==null||p.isEmpty())return s==null||s.isEmpty();
int slen = s.length(),plen=p.length();
boolean[][] dp=new boolean[slen+1][plen+1];
//初始化dp数组,dp[1][0]~dp[s.length][0]默认值flase不需要显式初始化为false
dp[0][0]=true;
//dp[0][1]~dp[0][p.length]只有p的j字符以及前面所有字符都为'*'才为true
for (int j=1;j<=plen;j++)dp[0][j]=p.charAt(j-1)=='*'&&dp[0][j-1];
//填写dp数组剩余部分
for (int i = 1; i <= slen; i++) {
for (int j = 1; j <= plen; j++) {
char si = s.charAt(i - 1),pj=p.charAt(j-1);
if (si==pj||pj=='?'){
dp[i][j]=dp[i-1][j-1];
}else if (pj=='*'){
dp[i][j]=dp[i-1][j]||dp[i][j-1];
}
}
}
return dp[slen][plen];
}
表示数字的字符串
class Solution {
private int index;
public boolean isNumber(String s) {
String str = s.trim();
if(str.length()==0){
return false;
}
index = 0;
boolean isNum = scanInt(str);
if(index<str.length()&&str.charAt(index)=='.'){
index++;
// 小数点前面有带符号数字 或者 小数点后面有数字 即可
isNum = isNum|scanUnsignedInt(str);
}
if(index<str.length()&&(str.charAt(index)=='e'||str.charAt(index)=='E')){
index++;
//e前面必须有数字
isNum = isNum&&scanInt(str);
}
return (isNum)&&(index==str.length());
}
public boolean scanInt(String s){
if(index<s.length()&&(s.charAt(index)=='+'||s.charAt(index)=='-')){
index++;
}
return scanUnsignedInt(s);
}
public boolean scanUnsignedInt(String s){
boolean flag = false;
while(index<s.length()&&s.charAt(index)>='0'&&s.charAt(index)<='9'){
index++;
flag = true;
}
return flag;
}
}
还可以用DFA
class Solution {
public boolean isNumber(String s) {
//初始状态
int state = 0;
//去除前后的空格
int start = 0,end = s.length()-1;;
while(start<=end && s.charAt(start) == ' '){
start++;
}
while(start<=end && s.charAt(end) == ' '){
end--;
}
s = s.substring(start,end+1);
if(s.length() == 0){
return false;
}
for(int i= 0 ; i<s.length() ; i++){
char c = s.charAt(i);
if(c == ' ' ){
return false;
}
switch(state){
case 0:{
if(c == '+' || c == '-'){
state = 1;
}else if(c>='0' && c<='9'){
state = 2;
}else if(c == '.'){
state = 7;
}else{
return false;
}
break;
}
case 1:{
if(c>='0' && c<='9'){
state = 2;
}else if(c == '.'){
state = 7;
}else{
return false;
}
break;
}
case 2:{
if(c>='0' && c<='9'){
}else if(c == '.'){
state = 4;
}else if(c == 'e'){
state = 3;
}else{
return false;
}
break;
}
case 3:{
if(c == '+' || c == '-'){
state = 6;
}else if(c>='0' && c<='9'){
state = 5;
}else{
return false;
}
break;
}
case 4:{
if(c>='0' && c<='9'){
}else if(c == 'e'){
state =3;
}else{
return false;
}
break;
}
case 5:{
if(c>='0' && c<='9'){
}else{
return false;
}
break;
}
case 6:{
if(c>='0' && c<='9'){
state = 5;
}else{
return false;
}
break;
}
case 7:{
if(c>='0' && c<='9'){
state = 4;
}else{
return false;
}
break;
}
default: break;
}
}
//红色的三个是合法终止状态
if(state == 2 || state == 4 || state == 5){
return true;
}else{
return false;
}
}
}
调整数组顺序使奇数位于偶数前面
类似快排! 双指针。都是分成两部分 ,左面一部分,右面一部分。
class Solution {
public int[] exchange(int[] nums) {
int i =0;
int j = nums.length-1;
while(i<j){
while(i<j&&(nums[i]&1)==1){ //用位运算判断奇偶性;
i++;
}
while(i<j&&(nums[j]&1)==0){
j--;
}
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
}
删除链表中倒数第k个节点
class Solution { //快慢指针
public ListNode getKthFromEnd(ListNode head, int k) {
if(head==null){
return head;
}
ListNode cur = head;
for(int i=0;i<k;i++){
if(cur!=null)cur= cur.next;
else return new ListNode(-1);
}
while(cur!=null){
head = head.next;
cur = cur.next;
}
return head;
}
}
反转链表
// 借助栈
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
Stack<Integer> stack= new Stack<Integer>();
ListNode cur= head;
if(cur==null){
return head;
}
while(cur!=null){
stack.push(cur.val);
cur = cur.next;
}
ListNode ans = new ListNode(stack.pop());
ListNode temp = ans;
while(!stack.isEmpty()){
temp .next = new ListNode(stack.pop());
temp = temp.next;
}
return ans;
}
}
//改变指针指向,头插法
//运用头插法 ,
if(head==null){
return head;
}
ListNode next;
ListNode temp =null;
ListNode cur = head;
while(cur!=null){
next = cur.next;
cur.next = temp;
temp = cur;
cur = next;
}
return temp;
合并两个排序链表
/** //修改指针 借助哑节点
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
int len1 =0;
int len2 = 0;
ListNode l11 =l1;
ListNode l22 = l2;
ListNode dummy = new ListNode(-1);
ListNode cur =dummy;
while(l11!=null){
l11= l11.next;
len1++;
}
while(l22!=null){
l22 = l22.next;
len2 ++;
}
ListNode temp = l1;
l1 = len1>=len2?l1:l2;
l2 = len1>=len2? l2:temp;
while(l1!=null&&l2!=null){
if(l1.val<=l2.val){
cur.next = l1 ;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
if(l1==null){
cur.next = l2;
}
if(l2==null){
cur.next = l1;
}
return dummy.next;
}
}
树的子结构
class Solution { //错误的!!!![1,0,1,-4,-3]
//[1,-4] 测试通不过
// 头节点的遍历和头节点的树的遍历 写到一块了 要分开
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(B ==null||A==null){
return false;
}
System.out.print(A.left.right.val);
return isMatch(A,B);
}
public boolean isMatch(TreeNode a,TreeNode b){
if(b==null ){
return true;
}
if(a==null){
return false;
}
if(a.val==b.val){
return (isMatch(a.left,b.left)&&isMatch(a.right,b.right))||isMatch(a.left,b)||isMatch(a.right,b);
}else{
return isMatch(a.left,b)||isMatch(a.right,b);
}
}
}
//正确答案
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
}
boolean recur(TreeNode A, TreeNode B) {
if(B == null) return true;
if(A == null || A.val != B.val) return false;
return recur(A.left, B.left) && recur(A.right, B.right);
}
}
反转二叉树
//递归方法 dfs
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return null;
}
TreeNode right = invertTree(root.right);
TreeNode left = invertTree(root.left);
root.left = right;
root.right = left;
return root;
}
}
//非递归方法 , bfs
if(root==null){
return null;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode node = queue.remove();
TreeNode left = node.left;
TreeNode right = node.right;
node.left = right;
node.right = left;
if(left!=null){
queue.add(left);
}
if(right!=null){
queue.add(right);
}
}
return root;
按照方法一的思路:翻转一棵树就是将其左右子结点交换位置,且对其左右子树也进行相同的操作。这就相当于说,要将每一个结点的左右子树交换位置。先将子结点进行翻转,就是递归。如果先将根结点的左右子树进行交换,就是迭代了。
这里只要求对每个结点完成交换左右子树,所以进行BFS还是DFS都可以(都是标准写法).
时间复杂度为O(n)O(n),因为遍历了每一个结点。
对称的二叉树
和上题类似,两种方法。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null){
return true;
}
return recur(root.right,root.left);
}
public boolean recur(TreeNode root1,TreeNode root2){
if(root1==null&&root2==null){
return true;
}
if(root1==null||root2==null){
return false;
}
if(root1.val==root2.val){
return recur(root1.left,root2.right)&&recur(root1.right,root2.left);
}else{
return false;
}
}
}
if(root==null){
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root.left);
queue.add(root.right);
while(!queue.isEmpty()){
TreeNode t1 = queue.poll(); //要用poll,poll可以返回null 还有offer(false) add(error)的区别
TreeNode t2 = queue.poll();
if(t1==null&&t2==null){
continue;
}
if(t1==null||t2==null){
return false;
}
if(t1.val!=t2.val){
return false;
}else{
queue.add(t1.left);
queue.add(t2.right);
queue.add(t1.right);
queue.add(t2.left);
}
}
return true;
}
顺时针打印矩阵
class Solution { //按圈处理 , 可以直接用数组,不用链表
ArrayList<Integer> res = new ArrayList<>();
public int[] spiralOrder(int[][] matrix) {
if(matrix==null||matrix.length==0||matrix[0].length==0){
return new int[0];
}
int m = matrix.length-1;
int n = matrix[0].length-1;
int i =0;
int j =0;
while(i<=m&&j<=n){
circle(matrix,i++,j++,m--,n--);
}
int[] ans = new int[res.size()];
for(int q =0;q< res.size();q++){
ans[q] = res.get(q);
}
return ans;
}
public void circle(int[][] matrix,int i,int j,int m,int n){
if(i==m){
for(int a =j;a<=n;a++){
res.add(matrix[i][a]);
}
}
else if(j==n){
for(int a = i;a<=m;a++){
res.add(matrix[a][j]);
}
}else{
int a=j;
while(a!=n){
res.add(matrix[i][a]);
a++;
}
int b = i;
while(b!=m){
res.add(matrix[b][n]);
b++;
}
while(a!=j){
res.add(matrix[m][a]);
a--;
}
while(b!=i){
res.add(matrix[b][j]);
b--;
}
}
}
}
包含min的栈
class MinStack { //辅助栈。
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
stack1.push(x);
if(!stack2.isEmpty()){
if(x<=stack2.peek()){
stack2.push(x);
}
}else{
stack2.push(x);
}
}
public void pop() {
int temp = stack1.pop();
if(temp == stack2.peek()){
stack2.pop();
}
}
public int top() {
return stack1.peek();
}
public int min() {
return stack2.peek();
}
}
栈的压入,弹出序列
class Solution { //借助栈,模拟压入弹出过程。
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();
int j =0;
for(int i =0;i<=pushed.length-1;i++){
stack.push(pushed[i]);
while(!stack.isEmpty()&&j<popped.length&&popped[j]==stack.peek()){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
}
从上到下打印二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution { //BFS
public int[] levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root==null){
return new int[0];
}
ArrayList<Integer> res = new ArrayList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode cur = queue.remove();
res.add(cur.val);
TreeNode left = cur.left;
TreeNode right = cur.right;
if(left!=null){
queue.add(left);
}
if(right!=null){
queue.add(right);
}
}
int[] ans = new int[res.size()];
for(int i =0;i<res.size();i++){
ans[i] = res.get(i);
}
return ans;
}
}
从上到下打印二叉树Ⅱ
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root==null){
return new ArrayList<List<Integer>>();
}
ArrayList<List<Integer>> ans = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
ArrayList<Integer> temp = new ArrayList<>();
for(int i = queue.size();i>0;i--){ //这个循环 ,重点理解
TreeNode cur = queue.poll();
temp.add(cur.val);
if(cur.left!=null){
queue.add(cur.left);
}
if(cur.right!=null){
queue.add(cur.right);
}
}
ans.add(temp);
}
return ans;
}
}
从上到下打印二叉树Ⅲ
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root==null){
return new ArrayList<List<Integer>>();
}
boolean flag = true;
ArrayList<List<Integer>> ans = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
ArrayList<Integer> temp = new ArrayList<>();
for(int i = queue.size();i>0;i--){
TreeNode cur = queue.poll();
temp.add(cur.val);
if(cur.left!=null){
queue.add(cur.left);
}
if(cur.right!=null){
queue.add(cur.right);
}
}
if(flag==false)
Collections.reverse(temp);
ans.add(temp);
flag = !flag;
}
return ans;
}
}
二叉搜索树的后续遍历
class Solution { // 树是否正确&&左子树&&右子树
public boolean verifyPostorder(int[] postorder) {
if(postorder==null){
return false;
}
return recur(postorder,0,postorder.length-1);
}
public boolean recur (int[] postorder,int i,int j){
if(i>=j){ //注意判断条件:>=不能是 ==
return true;
}
int k= i;
while(postorder[k]<postorder[j]){
k++;
}
int m = k;
while(postorder[k]>postorder[j]){
k++;
}
return k==j&&recur(postorder,i,m- 1)&&recur(postorder,m,j-1);
}
}
单调栈方法
待写
二叉树中和为某一值的路径
/** //回溯一般步骤
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if(root==null){
return new ArrayList<List<Integer>>();
}
ArrayList<Integer> res = new ArrayList<>();
int cursum=0;
backtrace(root,res,sum,cursum);
return ans;
}
public void backtrace(TreeNode root, ArrayList<Integer> res,int sum,int cursum){
if(root==null){
return ;
}
// if(sum<cursum){
// return;
// }
res.add(root.val);
if(sum==root.val+cursum&&root.left==null&&root.right==null){
// res.add(root.val);
ArrayList<Integer> temp = new ArrayList<>(res);
ans.add(temp);
}
backtrace(root.left,res,sum,cursum+root.val);
backtrace(root.right,res,sum,cursum+root.val);
res.remove(res.size()-1);
}
}
复杂链表的复制
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution { //借助map
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
HashMap<Node,Node> map = new HashMap<>();
Node cur = head;
while(cur!=null){
map.put(cur,new Node(cur.val));
cur = cur.next;
}
cur = head;
while(cur!=null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur= cur.next;
}
return map.get(head);
}
}
不用哈希表,第二种方式,待写
二叉搜索树与双向链表
/* //队列存储(非就地) 中序遍历
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public Node treeToDoublyList(Node root) {
Queue<Node> queue = new LinkedList<>();
inOrder(root,queue);
if(queue.isEmpty()){
return root;
}
Node head = queue.poll();
Node pre = head;
Node cur = null;
while(!queue.isEmpty()){
cur = queue.poll();
pre.right = cur;
cur.left = pre;
pre = cur;
}
pre.right = head;
head.left = pre;
return head;
}
public void inOrder(Node root,Queue<Node> queue){
if(root==null){
return ;
}
inOrder(root.left,queue);
queue.offer(root);
inOrder(root.right,queue);
}
}
就地版本
class Solution { //不用队列 融入dfs里
Node head,pre;
public Node treeToDoublyList(Node root) {
if(root==null){
return root;
}
dfs(root);
head.left = pre;
pre.right = head;
return head;
}
public void dfs(Node cur){
if(cur==null){
return;
}
dfs(cur.left);
if(pre!=null) pre.right = cur;
else head = cur;
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}