leetcode-剑指offer

剑指offe题目的题解,为了方便回顾和记忆。答案来自leetcode下面的评论和题解进行整理,算法小白,只能先做抄题记录,

题解算是转载吧,但是转载要投原链接比较多就投的原创。仅作私人笔记用途。

剑指 Offer 09. 用两个栈实现队列

一个入栈,一个出栈。

如果入栈

class CQueue {

    public Stack<Integer> stack_in;
    public Stack<Integer> stack_out;

    public CQueue() {
            stack_in = new Stack<Integer>();
            stack_out = new Stack<Integer>(); 

    }
    
    public void appendTail(int value) {
       stack_in.push(value);

    }
    
    public int deleteHead() {
      if(!stack_out.isEmpty()){
          return stack_out.pop();
      }else{
          while(!stack_in.isEmpty()){
              stack_out.push(stack_in.pop());
          }
          return stack_out.isEmpty()?-1:stack_out.pop();
      }
      
    }
}

剑指 Offer 30. 包含min函数的栈

用两个栈A和B,A中正常存放,B只放当前遇到的最小的值。

pop的时候如果A的顶层元素和B的顶层元素相等的时候B也需要pop

class MinStack {

    Stack<Integer> A,B;
    /** initialize your data structure here. */
    public MinStack() {
        A=new Stack<Integer>();
        B=new Stack<Integer>();
    }
    public void push(int x) {

        A.add(x);
        if(B.empty()||B.peek()>=x){
            B.add(x);
        }
    }
    public void pop() {
        if(A.pop().equals(B.peek()))
        B.pop();
    }
    
    public int top() {
        return A.peek();
    }
    
    public int min() {
            return B.peek();
    }
}

import java.util.Scanner;
public class Main{
    private static String alter(String input){
        StringBuilder strBuilder=new StringBuilder(input);
        int length=input.length();
        for(int i=0;i<length;i++){
            if(i>1&&strBuilder.charAt(i-2)==strBuilder.charAt(i-1)&&strBuilder.charAt(i-1)==strBuilder.charAt(i)){
                strBuilder.deleteCharAt(i);
                i-=1;
                length--;
                continue;
            }
            if(i>2&&strBuilder.charAt(i-3)==strBuilder.charAt(i-2)&&strBuilder.charAt(i-1)==strBuilder.charAt(i)){
                strBuilder.deleteCharAt(i);
                i-=1;
                length--;
                continue;
            }
        }
        return strBuilder.toString();
    }
    
    public static void main(String[] args){
        Scanner scan=new Scanner(System.in);
        int count=Integer.parseInt(scan.nextLine());
        for(int i=0;i<count;i++){
            System.out.println(alter(scan.nextLine()));
        }
    }
}

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int D = sc.nextInt();
        int[] node = new int[N];
        for(int i = 0; i < N; ++i){node[i] = sc.nextInt();}

        long dp = 0;
        int l = 0;
        int r = 2;
        while(l <= r && r < N){
            if(node[r] - node[l] > D)++l;
            else if(r - l < 2)++r;
            else{
                int len = r - l + 1;
                dp += (long)(len - 1) * (long)(len - 2) / 2;
                ++r;
            }
        }

        System.out.println(dp % 99997867);
    }
}

计算某字符出现次数

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char[] chars1 = br.readLine().toLowerCase().toCharArray();
        char[] chars2 = br.readLine().toLowerCase().toCharArray();
        int count = 0;
        for (int i = 0; i < chars1.length; i++) {
            if ((chars1[i] >= 65 || chars1[i] < 90) && (chars1[i] == chars2[0])) {
                count++;
            }
        }
        System.out.println(count);
    }
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char[] chars1 = br.readLine().toLowerCase().toCharArray();
        char[] chars2 = br.readLine().toLowerCase().toCharArray();
        int count = 0;
        for (int i = 0; i < chars1.length; i++) {
            if  (chars1[i] == chars2[0]) {
                count++;
            }
        }
        System.out.println(count);
    }
}
JZ6 从尾到头打印链表
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        if(listNode == null){
            return list;
        }
        list.add(listNode.val);
        while(listNode.next != null){
            listNode = listNode.next;
            list.add(listNode.val);
        }
        ArrayList<Integer> list1 = new ArrayList<>();
        for(int i = list.size() -1; i >= 0; i--){
            list1.add(list.get(i));
        }
        return list1;
    }
}

递归版本

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode == null) return new ArrayList<>();
        ArrayList<Integer> res = printListFromTailToHead(listNode.next);
        res.add(listNode.val);
        return res;
    }
}
class Solution {
    public int[] reversePrint(ListNode head) {
        int len = 0;
        ListNode p= head;
        ListNode q= head;
        while (p!= null){
            p=p.next;
            len++;
            
        } 
        int[] ans = new int[len];
        for(int i = len - 1;i >= 0 ; i--){

            ans[i] = q.val;
            q  = q.next;
        }
        return ans;

    }
}
剑指 Offer 24. 反转链表

avatar

avatar

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head, pre = null;
        while(cur != null) {
            ListNode tmp = cur.next; // 暂存后继节点 cur.next
            cur.next = pre;          // 修改 next 引用指向
            pre = cur;               // pre 暂存 cur
            cur = tmp;               // cur 访问下一节点
        }
        return pre;
    }
}
剑指 Offer 35. 复杂链表的复制
class Solution {
    public Node copyRandomList(Node head) {
        if(head==null) return null;
        Node cur =head ;
        Map<Node,Node> map = new HashMap<>();
        while(cur != null){
            map.put(cur,new Node(cur.val)); //建立新的节点 存储在hashmap中
            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);
    }
}
剑指 Offer 05. 替换空格
class Solution {
    public String replaceSpace(String s) {
        StringBuilder str = new StringBuilder();
        int len = s.length();
        for(int i = 0 ;i < len ; i++){
            if(s.charAt(i) != ' '){
                str.append(s.charAt(i));
            }else{
                str.append("%20");
            }
        }
        return str.toString();
    }
}
剑指 Offer 58 - II. 左旋转字符串
class Solution {
    public String reverseLeftWords(String s, int n) {
        //考研408真题,反转字符串
        char[] str = s.toCharArray();
        int len = str.length;
        reverse(str,0,len-1);
        reverse(str,0,len-n-1);
        reverse(str,len-n,len-1);

        return new String(str);
    }
    public void reverse(char[] str,int left,int right){
        while(left < right){
            char temp = str[left];
            str[left] = str[right];
            str[right] = temp ;
            right-- ;
            left++;
        }
    }
}
class Solution {
    public String reverseLeftWords(String s, int n) {
        //Java切片函数 substring截取字符串
        return s.substring(n,s.length())+s.substring(0,n);
    }
}
class Solution {
    public String reverseLeftWords(String s, int n) {
        //求余反转字符串
        StringBuilder res = new StringBuilder();
        int len = s.length();
        for ( int i = n ;i < n+len ; i++){
            res.append(s.charAt(i % len));
        }
        return res.toString();
    }
}
剑指 Offer 03. 数组中重复的数字

因为题目上是0-n-1,原地置换

class Solution {
    public int findRepeatNumber(int[] nums) {     
        int i=0;
        while(i<nums.length){
            if(nums[i]==i){
                i++;
                continue;
            }
            if(nums[i]==nums[nums[i]]) return nums[i];
            int tmp=nums[i];  //注意这里用tmp记录
            nums[i] = nums[tmp];
            nums[tmp]=tmp;
        }
        return -1;
    }
}

如果没有给限定范围,应该考虑hash

class Solution {
    public int findRepeatNumber(int[] nums) {
        int n= nums.length;
        HashSet<Integer> set = new HashSet<>();

        for(int i = 0 ;i < n ;i++){
            if(!set.contains(nums[i])){
                set.add(nums[i]);
            }else{
                return nums[i];
            }
        }
        return -1;
    }
}
剑指 Offer 53 - I. 在排序数组中查找数字 I

练习二分法

class Solution {
    public int search(int[] nums, int target) {
        if(nums.length == 0) return 0;
        return binarySearch(nums,target+1) - binarySearch(nums,target);
    }

    private int binarySearch(int[] nums , int target){
        int left = 0 , right = nums.length;
        while(left < right){
            int mid = left + (right - left) /2;
            if(nums[mid] <target){
                left = mid + 1;
            }else{
                right= mid;
            }
        }
        return right;
    }
}
剑指 Offer 53 - II. 0~n-1中缺失的数字

考点主要还是二分法,题目每个条件都是有用的。所有题都拿来遍历,offer也就遍历到别人那里去了

class Solution {
    public int missingNumber(int[] nums) {
            int i = 0 , j = nums.length-1;
            while(i<=j){
                int m  = (i + j) /2;
                if(nums[m] == m) i = m+1;
                else j=m-1;
            }
            return i;
    }
}
剑指 Offer 04. 二维数组中的查找

矩阵逆时针旋转 45°,类似于二叉树

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int i = matrix.length - 1, j = 0;
        while(i >= 0 && j < matrix[0].length)
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
}
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int row = 0, column = columns - 1;
        while (row < rows && column >= 0) {
            int num = matrix[row][column];
            if (num == target) {
                return true;
            } else if (num > target) {
                column--;
            } else {
                row++;
            }
        }
        return false;
    }
}

剑指 Offer 11. 旋转数组的最小数字

有序的数组,二分法

class Solution {
    public int minArray(int[] numbers) {
        int i = 0 , j = numbers.length-1;
        while(i < j){
            int m = (i +j) /2;
            if(numbers[m] > numbers[j]) i = m+1;
            else if(numbers[m] < numbers[j]) j=m;
            else j--;

        } 
        return numbers[j];
    }
}
剑指 Offer 50. 第一个只出现一次的字符

map.put(c , !map.containsKey©);

class Solution {
    public char firstUniqChar(String s) {
            HashMap<Character,Boolean> map = new HashMap<>();
            char[] ch = s.toCharArray();
             for(char c : ch)
                map.put(c , !map.containsKey(c)); //很巧妙
            for(char c  : ch) 
                    if(map.get(c)) return c;
        return ' ';
    }
}
剑指 Offer 26. 树的子结构

recur(A, B) 函数:

终止条件:
当节点 BB 为空:说明树 BB 已匹配完成(越过叶子节点),因此返回 truetrue ;
当节点 AA 为空:说明已经越过树 AA 叶子节点,即匹配失败,返回 falsefalse ;
当节点 AA 和 BB 的值不同:说明匹配失败,返回 falsefalse ;
返回值:
判断 AA 和 BB 的左子节点是否相等,即 recur(A.left, B.left) ;
判断 AA 和 BB 的右子节点是否相等,即 recur(A.right, B.right) ;
isSubStructure(A, B) 函数:

特例处理: 当 树 AA 为空 或 树 BB 为空 时,直接返回 falsefalse ;
返回值: 若树 BB 是树 AA 的子结构,则必满足以下三种情况之一,因此用或 || 连接;
以 节点 AA 为根节点的子树 包含树 BB ,对应 recur(A, B);
树 BB 是 树 AA 左子树 的子结构,对应 isSubStructure(A.left, B);
树 BB 是 树 AA 右子树 的子结构,对应 isSubStructure(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);
    }
}
剑指 Offer 27. 二叉树的镜像
  • 根据二叉树镜像的定义,考虑递归遍历(dfs)二叉树,交换每个节点的左 / 右子节点,即可生成二叉树的镜像。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
            if(root == null) return null;
            TreeNode tmp = root .left ;
            root.left  = mirrorTree(root.right);
            root.right = mirrorTree(tmp);
            return root;
    }
}
剑指 Offer 28. 对称的二叉树

对称二叉树

算法流程:
isSymmetric(root) :

特例处理: 若根节点 root 为空,则直接返回 truetrue 。
返回值: 即 recur(root.left, root.right) ;
recur(L, R) :

终止条件:
当 LL 和 RR 同时越过叶节点: 此树从顶至底的节点都对称,因此返回 truetrue ;
当 LL 或 RR 中只有一个越过叶节点: 此树不对称,因此返回 falsefalse ;
当节点 LL 值 \ne

= 节点 RR 值: 此树不对称,因此返回 falsefalse ;
递推工作:
判断两节点 L.leftL.left 和 R.rightR.right 是否对称,即 recur(L.left, R.right) ;
判断两节点 L.rightL.right 和 R.leftR.left 是否对称,即 recur(L.right, R.left) ;
返回值: 两对节点都对称时,才是对称树,因此用与逻辑符 && 连接。

class Solution {
    public boolean isSymmetric(TreeNode root) {
            return root == null ? true : recur (root.left , root.right);
    }
    boolean recur(TreeNode left , TreeNode right){
        if(left == null && right == null) return true;
        if(left == null || right == null || left.val != right.val) return false;
        return recur(left.left,right.right) && recur(left.right,right.left);
    }
}

剑指 Offer 10- I. 斐波那契数列

递归法:
原理: 把 f(n)f(n) 问题的计算拆分成 f(n-1)f(n−1) 和 f(n-2)f(n−2) 两个子问题的计算,并递归,以 f(0)f(0) 和 f(1)f(1) 为终止条件。
缺点: 大量重复的递归计算,例如 f(n)f(n) 和 f(n - 1)f(n−1) 两者向下递归需要 各自计算 f(n - 2)f(n−2) 的值。
记忆化递归法:
原理: 在递归法的基础上,新建一个长度为 nn 的数组,用于在递归时存储 f(0)f(0) 至 f(n)f(n) 的数字值,重复遇到某数字则直接从数组取用,避免了重复的递归计算。
缺点: 记忆化存储需要使用 O(N)O(N) 的额外空间。
动态规划:
原理: 以斐波那契数列性质 f(n + 1) = f(n) + f(n - 1)f(n+1)=f(n)+f(n−1) 为转移方程。
从计算效率、空间复杂度上看,动态规划是本题的最佳解法。

class Solution {
    public int fib(int n) {
        int a = 0 ,b = 1 ,sum;
        for(int i = 0 ; i < n ;i++){
            sum = ( a + b ) % 1000000007;
            a = b ;
            b = sum; 
        }
        return a;
    }
}
剑指 Offer 63. 股票的最大利润

状态定义: 设动态规划列表 dpdp ,dp[i]dp[i] 代表以 prices[i]prices[i] 为结尾的子数组的最大利润(以下简称为 前 ii 日的最大利润 )。
转移方程: 由于题目限定 “买卖该股票一次” ,因此前 ii 日最大利润 dp[i]dp[i] 等于前 i - 1i−1 日最大利润 dp[i-1]dp[i−1] 和第 ii 日卖出的最大利润中的最大值。
前 i 日最大利润 = \max(前 (i-1) 日最大利润, 第 i 日价格 - 前 i 日最低价格)
前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格)

dp[i] = max(dp[i - 1], prices[i] - min(prices[0:i]))
dp[i]=max(dp[i−1],prices[i]−min(prices[0:i]))

初始状态: dp[0] = 0dp[0]=0 ,即首日利润为 00 ;
返回值: dp[n - 1]dp[n−1] ,其中 nn 为 dpdp 列表长度。

效率优化:
时间复杂度降低: 前 ii 日的最低价格 \min(prices[0:i])min(prices[0:i]) 时间复杂度为 O(i)O(i) 。而在遍历 pricesprices 时,可以借助一个变量(记为成本 costcost )每日更新最低价格。优化后的转移方程为:
dp[i] = \max(dp[i - 1], prices[i] - \min(cost, prices[i])
dp[i]=max(dp[i−1],prices[i]−min(cost,prices[i])

空间复杂度降低: 由于 dp[i]dp[i] 只与 dp[i - 1]dp[i−1] , prices[i]prices[i] , costcost 相关,因此可使用一个变量(记为利润 profitprofit )代替 dpdp 列表。优化后的转移方程为:
profit = \max(profit, prices[i] - \min(cost, prices[i])
profit=max(profit,prices[i]−min(cost,prices[i])

复杂度分析:
时间复杂度 O(N)O(N) : 其中 NN 为 pricesprices 列表长度,动态规划需遍历 pricesprices 。
空间复杂度 O(1)O(1) : 变量 costcost 和 profitprofit 使用常数大小的额外空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1t7eUbVe-1647962895429)(https://pic.leetcode-cn.com/7ba378da9af7489b616aec9521ac1769b3ea644b3ff18a7e411323f1fb49efd9-Picture5.png)]

class Solution {
    public int maxProfit(int[] prices) {
        int cost = Integer.MAX_VALUE, profit = 0;
        for(int price : prices) {
            cost = Math.min(cost, price);
            profit = Math.max(profit, price - cost);
        }
        return profit;
    }
}

leetcode非官方题解的思路

class Solution {
    public int maxProfit(int[] prices) {
        int  minPrice = Integer.MAX_VALUE;
        int maxProfit = 0 ;
        for(int i = 0 ;  i < prices.length ; i++ ){
            if(prices[i] < minPrice){
                minPrice = prices[i];
            }else if(maxProfit < prices[i] - minPrice){
                maxProfit = prices[i] - minPrice;
            }
        }
        return maxProfit;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值