剑指offer(三)

在排序数组中查找数字

(排序数组首先想二分法的各种用法)

class Solution {
    public int search(int[] nums, int target) {
        if(nums.length==0){
            return 0;
        }
        int i=0,j = nums.length-1;
        while(i<=j){
            int mid  =(i+j)/2;
            if(nums[mid]>=target){
                j= mid-1;
            }else{
                i=mid+1;
            }
        }
        int left = j;
        i = 0;
        j = nums.length-1;
        while(i<=j){
           int  mid = (i+j)/2;
            if(nums[mid]>target){
                j=mid-1;
            }else{
                i =mid+1;
            }
        }
        return i-left-1;
    }
}

循环二分: 当 i \leq ji≤j 时循环 (即当闭区间 [i, j][i,j] 为空时跳出) ;
计算中点 m = (i + j) // 2,其中 “” 为向下取整除法;
若 nums[m] < target,则数字 target一定在闭区间 [m + 1, j]中,因此执行 i = m + 1;
若 nums[m] > target ,则数字 target一定在闭区间 [i, m - 1] 中,因此执行 j = m - 1;
若 nums[m] = target,则右边界 right 在闭区间 [m+1, j]中;左边界 leftleft 在闭区间 [i, m-1] 中。因此分为以下两种情况:
若查找 右边界 right,则执行 i = m + 1;(跳出时 ii指向右边界)
若查找 左边界 left,则执行 j = m - 1;(跳出时 j 指向左边界)
返回值: 应用两次二分,分别查找 right 和 left ,最终返回 right - left - 1 即可。

0~n-1中缺失的数字

//二分法   二分法不一定是比较中间大小,还有别的形式,只要是每次取一半。
class Solution {
    public int missingNumber(int[] nums) {
        int i =0;
        int j = nums.length-1;
        while(i<=j){
            int mid = (i+j)/2;
            if(nums[mid] == mid){
                i = mid+1;
            }else{
                j=mid-1;
            }
        }
        return i;
    }
}

二叉搜索树的第K大节点


```java
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {      //中序遍历逆序+提前终止
    int k,res;       
    public int kthLargest(TreeNode root, int k) {
        this.k = k;
        process(root);
        return res;
    }
    public void process(TreeNode root){
        if(root==null){
            return ;
        }
        process(root.right);
        if(--k==0){
            res =  root.val;
            return;
        }
        process(root.left);
    }

}

二叉树的深度

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        int depth = 0;
        return process(root);
    }
    public int process(TreeNode root){
        if(root==null){
            return 0;
        }
        int left = process(root.right);
        int right = process(root.left);
        return Math.max(left,right)+1;
    }
}
//另一种递归
class Solution {
     int maxDepth = 0;
    public int maxDepth(TreeNode root) {
    	if(root==null){
            return 0;
        }
        process(root,1);
        return maxDepth;
    }
    public void  process(TreeNode root, int depth){
        if(root==null){
            return;
        }
        if(depth>maxDepth){
            maxDepth =depth;
        }
        process(root.left,depth+1);
        process(root.right,depth+1);
    }
}

平衡二叉树

/**   //需要判断深度 ,参考上一题
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        return !(process(root)==-1);
    }
    public int process(TreeNode root){
        if(root ==null){
            return 0 ;
        }
        int left = process(root.left);
        if(left==-1)return -1;
        int right = process(root.right);
        if(right==-1) return -1;
        return Math.abs(left-right)<2?Math.max(left,right)+1:-1;
    }
}

数组中数字出现的次数

class Solution {
    public int[] singleNumbers(int[] nums) {
        int[] ans = new int[2];
        int eO = 0,eOhasOne= 0;
        for(int num: nums){
            eO ^= num;
        }
        int right = eO&(~eO+1);  //重要,从右看第一个1;只保留最右边的1.等同n&(-n) 
        for(int cur:nums){
            if((cur&right)!=0){
                eOhasOne ^= cur;
            }
        }
        ans[0] = eOhasOne;
        ans[1] = eO^eOhasOne;
        return ans;
    }
}

位运算技巧: n&(-n) 和 n&(~n+1)
n&(n-1) 把最后1个1 置零,用于统计1 的个数

数组中数字出现的次数Ⅱ

class Solution {    // 题中已知数都大于1,最后不用考虑符号,如果没给,则判断最高位。
//本题还可以真正使用进制,转化为三进制,在转化为十进制返回
    public int singleNumber(int[] nums) {
        int[] count = new int[32];
        for(int num:nums){
            for(int i =0;i<=31;i++){
                count[i]+=num&1;
                num>>=1;
            }
        }
        int res = 0;
        for(int i=1;i<=31;i++){
            res<<=1;
            res|=count[31-i]%3;
        }
        return res;
    }
}

和为s的两个数字

class Solution {  //因为有序,所以双指针
    public int[] twoSum(int[] nums, int target) {
        if(nums.length==1){
            return new int[0];
        }
        int i =0;
        int j = nums.length-1;
        int[] ans = new int[2];  
        while(i<=j){
            if(nums[i]+nums[j]>target){
                j--;
            }
            else if(nums[i]+nums[j]==target){
                ans[0] = nums[i];
                ans[1] = nums[j];
                return ans; 
            }else{
                i++;
            }
        }
        return new int[0];
    }
}

和为s的连续正数序列

class Solution {        //要返回数组,看如何转换
    ArrayList<int[]> ans = new ArrayList<>();
    public int[][] findContinuousSequence(int target) {
        int i =1,j=2;
        int sum = i+j;
        while(i<j&&i<target){
            if(sum>target){
                sum = sum-i;
                i++;
            }else if(sum==target){
               int[] temp = new int[j-i+1];
               int index = 0;
                for(int k = i;k<=j;k++){
                    temp[index] = k;
                    index++;
                }
                ans.add(temp);
                sum = sum-i;
                i++;
            }else{
                j++;
                sum  = sum +j;
            }
        }
        return ans.toArray(new int[ans.size()][]);
    }
}

反转单词顺序

class Solution {    //利用stringbuilder的reverse(),面试时建议不用
    public String reverseWords(String s) {
        if(s==null||s.length()==0){
            return s;
        }
        StringBuilder ans = new StringBuilder();
        String s1 = s.trim();
        if(s1.equals("")){
            return s1;
        }
        String s2 = reverse(s1);
        String[] strs = s2.split(" ");
        for(String str :strs){
            if(!str.equals(" ")&&str.length()!=0){
                ans.append(reverse(str));
                ans.append(" ");
            }
        }
        ans.deleteCharAt(ans.length() -1);
        return ans.toString();
    }
    public String reverse(String s ){
        StringBuilder sb = new StringBuilder(s);
        return sb.reverse().toString();
    }
}
class Solution {    //利用指针
    public String reverseWords(String s) {
        s = s.trim(); // 删除首尾空格
        int j = s.length() - 1, i = j;
        StringBuilder res = new StringBuilder();
        while(i >= 0) {
            while(i >= 0 && s.charAt(i) != ' ') i--; // 搜索首个空格
            res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
            while(i >= 0 && s.charAt(i) == ' ') i--; // 跳过单词间空格
            j = i; // j 指向下个单词的尾字符
        }
        return res.toString().trim(); // 转化为字符串并返回
    }
}

左旋转字符串

class Solution {  //两个s拼接成一个,方便操作
    public String reverseLeftWords(String s, int n) {
        String s1 = s+s;
        int len =s.length();
         n = n%len;
        return s1.substring(n,n+len);
    }
}

方法还有: 字符串切片
列表遍历拼接
字符串遍历拼接

滑动窗口的最大值

最主要的就是获取最大值。 因此可以想到,优先队列,最大堆,单调队列

class Solution {  //使用单调队列
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums==null||nums.length<k||k<1){
            return new int[0];
        }
        int index = 0;
        LinkedList<Integer> maxque = new  LinkedList<>();
        int[] res = new int[nums.length-k+1];
        for(int i =0;i<nums.length;i++){
            while(!maxque.isEmpty()&&nums[maxque.peekLast()]<=nums[i]){
                maxque.pollLast();
            }
            maxque.add(i);
            if(maxque.peekFirst()==(i-k)){
                maxque.pollFirst();
            }
            if(i>=(k-1)){
                res[index++] = nums[maxque.peekFirst()];
            }
        }
        return res;
    }
}

队列的最大值

class MaxQueue {
        Queue<Integer> que ;
        Deque<Integer> deq;
    public MaxQueue() { 
        que = new LinkedList<>();
        deq = new LinkedList<>();
    }
    
    public int max_value() {
        if(!deq.isEmpty()){
            return  deq.peek();
        }
        return -1;

    }
    
    public void push_back(int value) {
        que.add(value);
        while(!deq.isEmpty()&&deq.peekLast()<value){
            deq.pollLast();
        }
        deq.offerLast(value);
    }
    
    public int pop_front() {
        int temp = -1;
        if(!que.isEmpty()){
            temp = que.poll();
        }
        if(!deq.isEmpty()&& temp==deq.peek()){
            deq.poll();
        }
        return temp;
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */ 

n个骰(tou)子的点数

class Solution {    //动态规划
    public double[] twoSum(int n) {
        int dp[][] = new int [n+1][n*6+1];
        for(int i =1;i<=6;i++){
            dp[1][i] = 1;
        }
        for(int i =2;i<=n;i++){
            for(int j =i;j<=6*n;j++){
                for(int cur =1 ;cur<=6;cur++){
                    if(j<=cur){
                        break;
                    }
                    dp[i][j] += dp[i-1][j-cur];
                }
            }
        }
        double all = Math.pow(6,n);
        double[] res = new double[6*n-n+1];
        for(int i =0;i<6*n-n+1;i++){
            res[i] = dp[n][i+n]/all;
        }
        return res;
    }
}

扑克牌中的顺子

class Solution {     //不排序版本,还可以排序,就不需要set了
    public boolean isStraight(int[] nums) {
        Set<Integer> set = new HashSet<>();
        int max= 0;
        int min = 14;
        for(int num:nums){
            if(num==0){
                continue;
            }
            max = Math.max(max,num);
            min = Math.min(min,num);
            if(set.contains(num)){
                return false;
            }
            set.add(num);
        }
        return max-min<5?true:false;
    }
}

求1+2+。。+n

class Solution {       //借助短路与&&判断
    int res;
    public int sumNums(int n) {
        boolean x = (n>0) && sumNums(n-1)>0;
        res+=n;
        return res;
    }
}
//位运算,待做

搜索二叉树的最近公共祖先

说是搜索二叉树,要利用好。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {      //迭代版本
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null){
            return null;
        }   
        while(root!=null){
            if(root.val>p.val&&root.val>q.val){
                root = root.left;
            }else if(root.val<p.val&&root.val<q.val){
                root  = root.right;
            }else{
                break;
            }
        }
        return root;
    }
}
 if(root==null){     //递归版本
            return null;
        }
        if(root.val>p.val&&root.val>q.val){
            return lowestCommonAncestor(root.left,p,q);
        }else if(root.val<p.val&&root.val<q.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;

如果没有说是搜索二叉树,就需要遍历所有的。后序遍历

	 if(head==null||head==p||head==q){
            return head;
        }
        TreeNode left = lowestCommonAncestor(head.left,p,q);
        TreeNode right = lowestCommonAncestor(head.right,p,q);
        if(left!=null&&right!=null){
            return head;
        }
        return left!=null?left:right;

股票最大利润

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length<2){
            return 0;
        }
        int res = 0;
        int cost = Integer.MAX_VALUE;
        
        for(int i =0;i<prices.length;i++){
            cost = Math.min(cost,prices[i]);
            res= Math.max(res,prices[i]-cost);
        }
        return res;
    }
}

系列问题 动态规划通用套路

dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。

for 0 <= i < n:
    for 1 <= k <= K:
        for s in {0, 1}:
            dp[i][k][s] = max(buy, sell, rest)

把字符串转换为整数

class Solution {       
    public int strToInt(String str) {
        str= str.trim();
        if(str==null||str.length()<1){
            return 0;
        }
         char[] chs = str.toCharArray();
         long res= 0;
        int signal = 1;
        int i =1;
        if(chs[0]=='-'){
            signal = -1;
        }else if(chs[0]!='+'){
            i =0;
        }
        for(int k =i;k<chs.length;k++){
            if(chs[k]<'0'||chs[k]>'9'){
                break;
            }
            res = res*10+(chs[k]-'0');
            if(res>Integer.MAX_VALUE){
                return signal==-1?Integer.MIN_VALUE:Integer.MAX_VALUE;
            }
        }
        return signal*(int)res;
    }
}

不用加减乘除做加法

class Solution {     //  a^b 不进位加法, (a&b)<<1  只看进位   循环加到没进位
    public int add(int a, int b) {
        int sum = a;
        while(b!=0){
            sum = a^b;
            b= (a&b)<<1;
            a = sum;
        }
        return sum;
    }
}

减法通用

构建乘积数组

class Solution {   //正反两次遍历,类似 分糖果
    public int[] constructArr(int[] a) {
        if(a.length<=1){
            return new int[0];
        }
        int[] res =  new int[a.length];
        res[0] = 1;
        for(int i = 1;i<a.length;i++){
            res[i] = a[i-1]*res[i-1];
        }
        int temp =1;
        for(int i = a.length-2;i>=0;i--){
            temp = temp*a[i+1] ;
            res[i] =  temp*res[i];
        }
        return res;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值