剑指offer(一)

=## 数组中的重复数字

  • 方法一
    利用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);
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值