剑指Offer(力扣剑前30题)

剑03.数组中重复的数字

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:23 
public class Solution {
    public int findRepeatNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (set.contains(nums[i])) return nums[i];
            else {
                set.add(nums[i]);
            }
        }
        return -1;
    }
}

方法2public int findRepeatNumber(int[] nums) {
        Arrays.sort(nums);
        for (int i = 0; i < nums.length-1; i++) {
            if (nums[i]==nums[i+1]) return nums[i];
        }
        return -1;
    }

剑04.二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。

采用二分查找
public class Solution {
    public static boolean findNumberIn2DArray(int[][] matrix, int target) {
        int n=matrix.length;
        for (int i = 0; i < n; i++) {
            int left=0;
            int right=matrix[i].length-1;
            while (left<=right){
                int mid=(right-left)/2+left;
                if (matrix[i][mid]>target){
                    right=mid-1;
                }else if (matrix[i][mid]<target){
                    left=mid+1;
                }else {
                    return true;
                }
            }
        }
        return false;
    }
}

剑05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = "We are happy."
输出:"We%20are%20happy."
public class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i)==' ') sb.append("%20");
            else sb.append(s.charAt(i));
        }
        return sb.toString();
    }
}

剑06.从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]

栈思想
class Solution {
  public int[] reversePrint(ListNode head) {  
  if (head==null) return new int[]{};
        ListNode cur=head;
        Stack<Integer> stack = new Stack<>();
        while (cur!=null){
            stack.push(cur.val);
            cur=cur.next;
        }
        int []res=new int[stack.size()];
        int count=0;
        for (int i=0;i<res.length;i++){
            res[count++]=stack.pop();
        }
        return res;
    }
}

剑07.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
    3
   / \
  9  20
    /  \
   15   7
class TreeNode {
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode(int x) { val = x; }
  }
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        //4.深度优先结束条件
        if (pre==null||pre.length==0) return null;

        //1.先找到根节点
        TreeNode root=new TreeNode(pre[0]);
        //2.找in中序数组的根节点的位置
        int index=searchRoot(pre,in);
        //3.构建左右子树递归
        //root.left=reConstructBinaryTree(左子树的前序遍历数组,左子树的中序遍历数组)
        //左子树的前序遍历数组:看中序数组:index+1-1=index长度是pre数组要截取的左子树的节点个数
        //左子树的中序遍历数组:看中序数组:首位到根节点的长度
        root.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,index+1),
                Arrays.copyOfRange(in,0,index));

        //同理右子树
        root.right=reConstructBinaryTree(Arrays.copyOfRange(pre,index+1,in.length),
                Arrays.copyOfRange(in,index+1,in.length));

        return root;
    }
    public int searchRoot(int [] pre,int [] in){
        for (int i=0;i<in.length;i++){
            if (pre[0]==in[i]) return i;//返回中序数组的根节点位置
        }
        return 0;
    }
}

剑09. 用两个栈实现队列

* 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,
 * 分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
 * 示例 1* 输入:
 * ["CQueue","appendTail","deleteHead","deleteHead"]
 * [[],[3],[],[]]
 * 输出:[null,null,3,-1]
public class Solution {
    Stack<Integer> stack1;
    Stack<Integer> stack2;
    public Solution() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

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

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

剑10.斐波拉契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 01 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
class Solution {
    public int fib(int n) {
        int dp[]=new int[n+2];
        dp[0]=0;
        dp[1]=1;
        for (int i = 2; i <=n; i++) {
            dp[i]=dp[i-1]+dp[i-2];
            dp[i]%=1000000007;
        }
        return dp[n];
    }
}

剑10.青蛙跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
class Solution {
    public int numWays(int n) {
        int dp[]=new int[n+2];
        if (n==0){return 1;} 
        dp[1]=1;
        dp[2]=2;
        for (int i = 3; i <=n ; i++) {
            dp[i]=dp[i-1]+dp[i-2];
            dp[i]%=1000000007;
        }
        return dp[n];
    }
}

剑10. 变态跳台阶

在这里插入图片描述

public class Solution {
    public int JumpFloorII(int target) {
        int[] dp = new int[target+1];

        for(int i=1;i<=target;i++){
            int sum = 0;
            for(int j=1;j<i;j++){
                sum = sum + dp[j];
            }
            dp[i] = sum+1;
        }
        return dp[target];
    }
}

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

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一个旋转,该数组的最小值为1。  
输入:[3,4,5,1,2]
输出:1
class Solution {
    public int minArray(int[] numbers) {
        MaoPao(numbers);
        return numbers[0];
    }
    public void MaoPao(int[] numbers){
        for(int i=0;i<numbers.length;i++){
            for(int j=0;j<numbers.length-1-i;j++){
                if(numbers[j]>numbers[j+1]){
                    int temp=numbers[j+1];
                    numbers[j+1]=numbers[j];
                    numbers[j]=temp;
                }
            }
        }
    }
}

剑12. 矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
                if (dfs(x,y,matrix,rows,cols,0,str,new boolean[rows][cols])){
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, char[] matrix, int rows, int cols, int k, char[] str, boolean[][] booleans) {
        if (x<0||y<0||x>rows-1||y>cols-1) return false;
        if (booleans[x][y]) return false;
        if (matrix[cols*x+y]!=str[k]) return false;

        if (k==str.length-1) return true;
        booleans[x][y]=true;
        boolean flag=dfs(x-1,y,matrix,rows,cols,k+1,str,booleans)
        ||dfs(x+1,y,matrix,rows,cols,k+1,str,booleans)
        ||dfs(x,y-1,matrix,rows,cols,k+1,str,booleans)
        ||dfs(x,y+1,matrix,rows,cols,k+1,str,booleans);

        if (!flag){
            booleans[x][y]=false;
        }
        return flag;
    }
}

剑13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格
外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机
器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],
因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:
输入:m = 2, n = 3, k = 1
输出:3
public class Solution {
    public int movingCount(int threshold, int rows, int cols)
    {
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
              return dfs(x,y,threshold,rows,cols,new boolean[rows][cols]);
            }
        }
        return 0;
    }

    private int dfs(int x, int y, int threshold, int rows, int cols, boolean[][] booleans) {
       //越界问题
        if (x<0||y<0||x>rows-1||y>cols-1) return 0;
        //递归终止条件,碰壁回退
        if (x/10+x%10+y/10+y%10>threshold) return 0;

        //已经走过的格子不需要再走了
        if (booleans[x][y]) return 0;
        //做出选择
        booleans[x][y]=true;
        //每走一步就+1
        return dfs(x-1,y,threshold,rows,cols,booleans)//向左移动
                +dfs(x+1,y,threshold,rows,cols,booleans)//向右移动
                +dfs(x,y-1,threshold,rows,cols,booleans)//向下移动
                +dfs(x,y+1,threshold,rows,cols,booleans)+1;//向上移动
    }
}

剑14. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为233的三段,此时得到的最大乘积是18。
答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
public class Solution {
    /**
     * /**
     * * 根据数学推导,如果把绳子切成长度为3的多段,乘积最大
     * * *
     * 假如把绳子切成长度为3的多段,那么留下来的最后一段的绳子可能为0、1、2三种情况
     * * 假如余下的绳子长度为2,应该保留不再拆分为2=1+1,因为1*1 < 2
     * * 假如余下的绳子长度为1,那么应该把一个3+1替换成2+2,因为3*1 < 2*2
     * */
    /**
      * * 算法流程:
      * * 当 n≤3 时,即n=2或3时,按照规则应不切分,但由于题目要求必须剪成 m>1 段
      * * 因此必须剪出一段长度为 1 的绳子,2=1+1,即返回 n−1。
      * * *
      * 当 n>3 时,求n/3的整数部分 a 和余数部分b(n=3a+b)
      * * 1、如果b=0, 返回3^a
      * * 2、如果b=1, 返回4*3^(a-1) (3+1->2+2)
      * * 3、如果b=2, 返回2*3^a
      * */
    public int cutRope(int target) {
        if(target<=3) return target-1;
        //a是整数
        int a=target/3;
        //b是余数,除3的余数只能是0,1,2,超过3就不是余数了
        int b=target%3;
        if (b==0) return (int) Math.pow(3,a);
        else if (b==1) return (int) (4*Math.pow(3,(a-1)));
        else  return (int) (2*Math.pow(3,a));
    }
}

剑15. 二进制中1的个数

public class Solution{
    /**输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。*/
    public static void main(String[] args) {

    }
    public int NumberOf1(int n) {
        /*int count=0;
        String string = Integer.toBinaryString(n);
        for (int i = 0; i < string.length(); i++) {
            if(string.charAt(i)=='1'){
                count++;
            }
        }
        return count;*/
        //位运算
        int res=0;
        while (n!=0){
            res+=n&1;
            //无符号右移1位
            n>>>=1;
        }
        return res;
    }
}

剑16. 数值的整数次方

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
class Solution {
    public double myPow(double x, int n) {
        long N=n;
        if (N < 0) {
            return 1/Pow(x,(-N));
        }else {
            return Pow(x,N);
        }
    }
    private double Pow(double x, long n) {
        if (n==0) return 1;
        if (x==1) return 1;
        if (n%2==0){
            double start=Pow(x,n/2);
            return start*start;
        }else {
            double start=Pow(x,(n-1)/2);
            return start*start*x;
        }
    }
}

剑17. 打印1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 123 一直到最大的 3 位数 999。
示例 1:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
class Solution {
    public int[] printNumbers(int n) {
        int length=(int)Math.pow(10,n)-1;
        int []res=new int[length];
        for(int i=0;i<length;i++){
            res[i]=i+1;
        }
        return res;
    }
}

剑18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
public class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if (head==null) return null;
        ListNode dummy=new ListNode(0);
        ListNode cur=dummy;
        dummy.next=head;
        while (cur.next!=null){
            if (cur.next.val==val){
                cur.next=cur.next.next;
                break;
            }
            cur=cur.next;
        }
        return dummy.next;
    }
}

剑18. 删除链表的重复节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,
 * 重复的结点不保留,返回链表头指针。
 * 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
 * 思路:先找头指针不重复的点,之后找剩余的点*/
//删除链表的重复结点
public class Solution2 {
    public ListNode deleteDuplication(ListNode pHead){
        //注意备用头结点,头结点可能被删除
        ListNode dummy =new ListNode(0);
        //双指针 pre cur
        ListNode pre=dummy;
        ListNode cur=pHead;
        dummy.next=pHead;
        while (cur!=null&&cur.next!=null){
            if (cur.val==cur.next.val){//两节点相等
                int temp=cur.val;//记录val用于判断后面节点是否重复
                while (cur!=null&&temp==cur.val){//这一步用于跳过相等的节点,用于删除
                    cur=cur.next;
                }
                pre.next=cur;//删除操作,前节点的next直接等于现在的节点,把中间的节点直接跨过
            }else {
                pre=cur;
                cur=cur.next;
            }
        }
        return dummy.next;
    }
}

剑20. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100""5e2""-123""3.1416""-1E-16""0123"都表示数值,但"12e""1a3.14""1.2.3""+-5""12e+5.4"都不是。
class Solution {
    public boolean isNumber(String s) {
        if (s.endsWith("F")||s.endsWith("f")||s.endsWith("D")||s.endsWith("d")){
            return false;
        }
        try {
            double d=Double.parseDouble(s);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }
}

剑21. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。
public class Solution {
    public int[] exchange(int[] nums) {
        //定义左右指针
        int left=0,right=nums.length-1;
        int temp;
        while (left<right){
            //指针 left 遇到奇数则执行 left = left + 1 跳过,从左到右直到找到偶数;
            while (left<right && nums[left]%2==1) left++;
            //指针 right 遇到偶数则执行 right = right - 1 跳过,从右到左直到找到奇数;
            while (left<right && nums[right]%2==0) right--;
            temp=nums[left];
            nums[left]=nums[right];
            nums[right]=temp;
        }
        return nums;
    }
}

剑22. 链表中的倒数第K个结点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是123456。这个链表的倒数第3个节点是值为4的节点。
public class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //定义快慢指针
        ListNode fast=head;
        ListNode slow=head;
        //先让快指针往前走k步
        for (int i = 0; i < k; i++) {
            fast=fast.next;
        }
        //当快指针走完,直接返回慢指针即可,慢指针的位置距尾结点k-1距离
        while (fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
}

剑23. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev=null;
        ListNode cur=head;
        ListNode temp=null;
        while(cur!=null){
            temp=cur.next;
            cur.next=prev;
            prev=cur;
            cur=temp;
        }
        return prev;
    }
}

剑23. 反转链表2

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
public class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        //定义哑结点
        ListNode dumpy = new ListNode(0);
        dumpy.next=head;
        //定义P1、P2指针
        ListNode P1=dumpy;
        ListNode P2=head;
        //将指针移到转换起始点P2和起始点的前一位P1
        for (int i=1;i<m;i++){
            P1=P1.next;
            P2=P2.next;
        }
        for (int i = 0; i < n - m; i++) {
            ListNode cur=P2.next;
            P2.next=cur.next;
            cur.next=P1.next;
            P1.next=cur;
        }
        return dumpy.next;
    }
}

剑25. 合并两个排序链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //定义空节点
        ListNode dump=new ListNode(0);
        ListNode cur=dump;
        while (list1!=null&&list2!=null){
            if (list1.val<list2.val){
                cur.next=new ListNode(list1.val);
                list1=list1.next;
            }else {
                cur.next=new ListNode(list2.val);
                list2=list2.next;
            }
            cur=cur.next;
        }
        if (list1!=null){
            cur.next=list1;
        }else {
            cur.next=list2;
        }
        return dump.next;
    }
}

剑26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
     3
    / \
   4   5
  / \
 1   2
给定的树 B:
   4 
  /
 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if (A==null||B==null) return false;
   //判断B是A的子结构,B是A的左子树的子结构,B是A的右子树的子结构          
        return helper(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B);
    }
//helper函数是判断B是否是A的子结构
    private boolean helper(TreeNode rootA, TreeNode rootB) {
        if (rootB==null) return true;
        if (rootA==null) return false;
        return rootA.val==rootB.val&&helper(rootA.left,rootB.left)&&helper(rootA.right,rootB.right);
    }
}

剑27. 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。
     4
   /   \
  2     7
 / \   / \
1   3 6   9
镜像输出:
     4
   /   \
  7     2
 / \   / \
9   6 3   1
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        helper(root);
        return root;
    }
    //helper函数是交换父节点的左右子节点,记住左右子树连着一起调换位置
    public void helper(TreeNode root){
        if(root==null) return;
            TreeNode temp=root.left;
            root.left=root.right;
            root.right=temp;
        helper(root.left);
        helper(root.right);
    }
}

剑28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
    1
   / \
  2   2
 / \ / \
3  4 4  3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
    1
   / \
  2   2
   \   \
   3    3
输入:root = [1,2,2,3,4,4,3]
输出:true
class Solution {
    public boolean isSymmetric(TreeNode root) {
    //递归终止条件
        if (root==null) return true;
        return helper(root.left,root.right);
    }

    private boolean helper(TreeNode root1,TreeNode root2) {
    //递归终止条件
        if (root1==null&&root2==null) return true;
        if (root1==null||root2==null) return false;
        //这个直接看树的结构
        return (root1.val==root2.val)&&helper(root1.left,root2.right)
                &&helper(root1.right,root2.left);
    }
}

剑29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

      1.空值处理: 当 matrix 为空时,直接返回空列表 [] 即可。
     * 2.初始化: 矩阵 左、右、上、下 四个边界 l , r , t , b ,用于打印的结果列表 res 。
     * 3.循环打印: “从左向右、从上向下、从右向左、从下向上” 四个方向循环,每个方向打印中做以下三件事 (各方向的具体信息见下表) ;
     * --(1)根据边界打印,即将元素按顺序添加至列表 res 尾部;
     * --(2)边界向内收缩 l(代表已被打印);
     * --(3)判断是否打印完毕(边界是否相遇),若打印完毕则跳出
class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if (matrix==null||matrix.length==0) return new int[]{};
        int left=0,right=matrix[0].length-1;
        int top=0,bellow=matrix.length-1;
        int count=0;
        int res[]=new int[(bellow+1)*(right+1)];
        while (true){
            for (int i = left; i <=right; i++) {
                res[count++]=matrix[top][i];
            }
            if (++top>bellow)break;
            for (int i = top; i <=bellow; i++) {
                res[count++]=matrix[i][right];
            }
            if (left>--right)break;
            for (int i = right; i >=left; i--) {
                res[count++]=matrix[bellow][i];
            }
            if (top>--bellow)break;
            for (int i = bellow; i >=top; i--) {
                res[count++]=matrix[i][left];
            }
            if (++left>right)break;
        }
        return res;
    }
}

剑30. 包含main函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
实例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.

class MinStack {
    /** initialize your data structure here. */
     Stack<Integer> stack= new Stack<Integer>();
    public MinStack() {

    }

    public void push(int x) {
        stack.push(x);
    }

    public void pop() {
        if (!stack.isEmpty()){
            stack.pop();
        }
    }

    public int top() {
        int peak=0;
        if (!stack.isEmpty()){
            peak=stack.peek();
        }
        return peak;
    }

    public int min() {
        int min=top();
        if (!stack.isEmpty()){
            Iterator<Integer> iterator = stack.iterator();
            while (iterator.hasNext()){
                int temp=iterator.next();
                if (min>temp){
                    min=temp;
                }
            }
        }
        return min;
    }
}

剑31. 栈的压入弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

public class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        ///用一个栈来辅助操作
        Stack<Integer> stack = new Stack<>();
        int i=0;
        for (int num:pushed){
            stack.push(num);
            while (!stack.isEmpty()&&stack.peek()==popped[i]){
                //和popped相等时,就出栈,说明入栈的和弹出的一样
                stack.pop();
                i++;
            }
        }
        //当辅助栈的peek都逐一和popped相等时,
        return stack.isEmpty();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值