数据结构+算法

数据结构与算法

排序

归并排序(分治算法)

思想,递归拆分数组,将其进行比较替换后 在进行合并

代码实现

package com.xu.fingeroffer.递归;

import java.util.Arrays;

public class 归并排序 {

    public static void main(String[] args) {
        int a[] = {9, 2, 6, 3, 5, 7, 10, 11, 12};
        int[] sort = sort(a);
        System.out.println(Arrays.toString(sort));
        }

    static int[] sort(int[] array) {
        if (array.length != 0) {
            divide(array, 0, array.length - 1);
        }
        return array;
    }

    private static void divide(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }
        int mid = (end+start)/ 2;
        divide(array, start, mid);//左边递归
        divide(array, mid + 1, end);//右边递归
        memerg(array, start, mid, end);//合并两个子列
    }

    private static void memerg(int[] array, int start, int mid, int end) {
        int[] temp = new int[end - start + 1];
        int p1 = start;
        int p2 = mid + 1;
        int k = 0;
        while (p1 <= mid && p2 <= end) {
            if (array[p1] < array[p2]) {
                temp[k++] = array[p1++];
            } else {
                temp[k++] = array[p2++];
            }
        }
        while (p1 <= mid) {
            temp[k++] = array[p1++];
        }
        while (p2 <= end) {
            temp[k++] = array[p2++];
        }
        for (int i = 0; i < temp.length; i++) {
            array[i + start] = temp[i];
        }

    }


}

二叉树

深搜

先序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的先序遍历144 {
    public List<Integer> preorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();

        if (root == null) {
            return list;
        }
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if(root==null){
            return list;
        }
        list.add(root.val);
        list = dfs(root.left,list);
        list = dfs(root.right,list);

        return list;

    }


}

中序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的中序遍历94{
    public List<Integer> inorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();

        if (root == null) {
            return list;
        }
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if(root==null){
            return list;
        }

        if(root.left!=null){
            dfs(root.left,list);
        }
        list.add(root.val);
        if(root.right!=null){
         dfs(root.right,list);
        }
        return list;
    }
}

后序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的后序遍历145 {

    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if (root == null) {
            return list;
        }
        if (root.left != null) {
            list = dfs(root.left, list);
        }
        if(root.right!=null){
            list = dfs(root.right,list);
        }
        list.add(root.val);
        return list;
    }
}

剑指offer

1-10

1、二维数组的查找

题目描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。

给定 target = 3,返回 false。

0 <= array.length <= 500
0 <= array[0].length <= 500
问题分析
要求时间复杂度 O(M + N),空间复杂度 O(1)。其中 M 为行数,N 为 列数。


从二维数组左上角进行查找,若target>array(i)(j)则j+1若小于i-1 若没有返回false
代码实现
package com.xu.fingeroffer.ten;

public class 二维数组的查找1 {
    public static void main(String[] args) {
//        int[][] a = {{1, 2, 8, 9}, {4, 7, 10, 13}};
        int [][] a = new int[2][4];
        System.out.println(Find(7, a));

    }

    public static boolean Find(int target, int[][] array) {
        int m=array.length;
        int n=array[0].length;
        int i=0;
        int j=n-1;
        while(i>=0 && i<m && j>=0 && j<n){
            int mid=array[i][j];
            if(target<mid){
                j--;
            }else if(target>mid){
                i++;
            }else{
                return true;
            }
        }
        return false;
    }
}

2、替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
问题分析
使用stringbuilder工具类 若charAt为空格 则拼接%20即可
代码实现
package com.xu.fingeroffer.ten;

public class 替换空格2 {
    public static void main(String[] args) {
        System.out.println(replaceSpace("a b"));
    }
    public static String replaceSpace (String s) {
        // write code here
        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();
    }
}

3、从尾到头打印链表

题目描述
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:

返回一个数组为[3,2,1]

0 <= 链表长度 <= 1000
示例1
输入:
{1,2,3}
复制
返回值:
[3,2,1]

问题解析
利用用linkedlist特性 实际就是栈 插入后 然后将其放到arraylist中即可
代码实现
package com.xu.fingeroffer.ten;

import com.xu.fingeroffer.node.ListNode;

import java.util.ArrayList;
import java.util.LinkedList;

public class 从尾到头打印链表3 {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        LinkedList<Integer> list1 = new LinkedList<>();
        if(listNode==null){
            return list;
        }
        while (listNode!=null){
            list1.addFirst(listNode.val);
            listNode = listNode.next;
        }
        for (Integer integer : list1) {
            list.add(integer);
        }
        return list;
    }
}

4、重建二叉树

问题描述
描述
给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
题目分析
已知是一个中序遍历和先序遍历数组,先序遍历数组中找到与中序遍历头相等的值,然后对两个数组进行拆分,递归成一个个小问题即可,
注意:两个递归的函数分别为rebuild(pre,pleft+1,pleft+i-vleft,vin,vleft,i-1)
rebuild(pre,pleft+i-vleft+1,pright,vin,i+1,vleft)
代码实现
package com.xu.fingeroffer.ten;

import com.xu.fingeroffer.node.TreeNode;

public class 重建二叉树4 {
    public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
        if (pre.length != vin.length) {
            return null;
        }
        return rebuildBinaryTree(pre, 0, pre.length-1, vin, 0, vin.length-1);
    }

    private TreeNode rebuildBinaryTree(int[] pre, int pleft, int pright, int[] vin, int vleft, int vright) {
        if (pleft > pright || vleft > pright) {
            return null;
        }
        TreeNode root = new TreeNode(pre[pleft]);
        for (int i = pleft; i <= vright; i++) {
            if (pre[pleft] == vin[i]) {
                root.left = rebuildBinaryTree(pre, pleft + 1, pleft+i-vleft, vin, vleft, i - 1);
                root.right = rebuildBinaryTree(pre, pleft+i-vleft+1, pright, vin, i + 1, vright);

            }
        }

        return root;
    }


}

5、用两个栈实现队列

问题描述
描述
用两个栈来实现一个队列,分别完成在队列尾部插入整数(push)和在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。

示例:
输入:
["PSH1","PSH2","POP","POP"]
返回:
1,2
解析:
"PSH1":代表将1插入队列尾部
"PSH2":代表将2插入队列尾部
"POP“:代表删除一个元素,先进先出=>返回1
"POP“:代表删除一个元素,先进先出=>返回2
问题分析
利用两个栈,push的时候push到stack1中,而poll的时候 是将stack1中的所有东西push到stack2中(若stack2不为空则不能push,如果要push到stack2中,则要么把stack1中所有值push,要么将stack2push满),然后 在对stack2进行poll(在poll的时候判断)
代码实现
package com.xu.fingeroffer.ten;

import java.util.Stack;

public class 两个栈实现队列 {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);

    }

    /**
     * 在pop的时候 需要将stack1中的值push到stack2中,然后将stack2中的值pop出
     * 注意:若stack2不为null的时候,不能将stack1中的值push进去,若要push进入到stack2,需要将stack1中的所有值push进去
     * @return
     */
    public int pop() {
        if(stack2.isEmpty()){
            while (!stack1.isEmpty()){
                Integer pop = stack1.pop();
                stack2.push(pop);
            }
        }


        return stack2.pop();
    }
}

6、选择数组最小数字

题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0
问题分析
遍历数组,对每个数字与前一个比较,若大于,则直接返回,若没有返回array[0],若数组长度为0 返回0
代码实现
package com.xu.fingeroffer.ten;

public class 旋转数组最小数字6 {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){
            return 0;
        }
        for (int i = 0; i < array.length-1; i++) {
            if(array[i]>array[i+1]){
                return array[i+1];
            }
        }
        return array[0];
    }
}

7、斐波那契数列

问题描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n\leq 39n≤39
题目分析
斐波那契数列,最好不要用递归,浪费资源,用循环即可
代码实现

8、跳台阶

问题描述
描述一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。示例1输入:2复制返回值:2复制示例2输入:7复制返回值:21复制关联企业
题目分析
若只能跳1次和两次,其实就是个递归的问题,跳一个格子只有一次,跳第二个格子有两种方法,跳第三个格子的时候,就可以选择在第一个格子跳或者在第二个格子 是第一次跳的方法加第二次跳的方法和,即为斐波那契数列问题
代码实现

9、跳台阶拓展

问题描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
题目分析
可以使用回溯算法 0到1 为1次 0到2为 0-1-1 或者0-2 0-为 0-1-1-1 0-1-2 0-2-1,0-3 结果值为2的n次方减一
代码实现

10、矩形覆盖

问题描述:
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,从同一个方向看总共有多少种不同的方法?比如n=3时,2*3的矩形块有3种不同的覆盖方法(从同一个方向看):
问题解析:
当n=1 的时候值为1,n=2有两种解法,n=3的时候,有三种,n=4的时候,为 f2+f3 这是个斐波那契数列问题
代码实现:
package com.xu.fingeroffer.ten;public class 矩形覆盖10 {    public int rectCover(int target) {        if (target==0||target == 1 || target == 2 || target == 3) {            return target;        }        int f1 = 2;        int f2 = 3;        for (int i = 4; i <= target; i++) {            int num = f1;            f1 = f2;            f2 = f1+num;        }        return f2;    }}

11-20

11、二进制中1的个数

题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。示例1
问题分析
代码实现

12、数值的整数次方

题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数。
问题分析
遍历乘 若exponent<0 返回分之一即可
代码实现

13、调整数组顺序使奇数位于偶数前面

问题描述
 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 
问题解析
利用两个数组,遍历数组,将奇数放到1数组,偶数放到2数组中,最后将1,2数组连接即可
代码实现
package com.xu.fingeroffer.twenty;import java.util.LinkedList;public class 调整数组顺序使奇数位于偶数前面13 {    /**     * 队列     * @param array     * @return     */    public int[] reOrderArray (int[] array) {        LinkedList<Integer> l1 = new LinkedList<>();        LinkedList<Integer> l2 = new LinkedList<>();        for (int i = 0; i < array.length; i++) {            if(array[i]%2==1){                l1.add(array[i]);            }else {                l2.add(array[i]);            }        }        l1.addAll(l2);        int []result = new int [array.length];        for (int i = 0; i < array.length; i++) {            result[i]=l1.removeFirst();        }        return result;    }    /**     * 数组     */    public  int[] reOrderArray1 (int[] array) {        int []a = new int [array.length];        int []b = new int [array.length];        int j = 0;        int k = 0;        for (int i = 0; i < array.length; i++) {            if(array[i]%2==1){                a[j] = array[i];                j++;            }else {                b[k]=array[i];                k++;            }        }        for (int i = 0; i < k ;i++) {            a[j]=b[i];            j++;        }        return a;    }}

14、链表中倒数k个节点

题目描述
输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。如果该链表长度小于k,请返回一个长度为 0 的链表。
问题解析
设置一个标志位index,每次遍历一个节点,标志位+1,若k>index,则return null,对k进行遍历,最终输出结果应该是从栈中pull的节点 
代码实现
package com.xu.fingeroffer.twenty;

import com.xu.fingeroffer.node.ListNode;

import java.util.HashMap;
import java.util.Stack;

public class 链表中倒数最后k个结点14 {
    public static void main(String[] args) {
        ListNode node = new ListNode(1);
        node.next = new ListNode(2);
        node.next.next= new ListNode(3);
        ListNode listNode = FindKthToTail(node, 1);
    }
    public static ListNode FindKthToTail (ListNode pHead, int k) {
        int index = 0;
        // write code here
        if(k==0||pHead==null){
            return null;
        }
        // write code here
        Stack<ListNode> stack = new Stack<>();
        while (pHead!=null){
            stack.push(pHead);
            index++;
            pHead=pHead.next;
        }
        if(index<k)
            return null;
        for (int i = 1; i < k; i++) {
            stack.pop();

        }

        return stack.pop();
    }
}

15、反转链表

问题描述
 输入一个链表,反转链表后,输出新链表的表头。 
题目解析
将链表中的所有数据放到一个list集合中,利用头插法,最终得出反转后的链表
代码实现
 package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.ListNode;public class 反转链表15 {    public ListNode ReverseList(ListNode head) {        if(head==null||head.next==null){            return head;        }        ListNode first = null;        while (head!=null){            ListNode second = head;            head=head.next;            second.next=first;            first=second;        }        return first;    }}
16、合并两个排序的链表
问题描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则

题目解析

通过两个链表: 构建一个新链表 head(Interger.MIN) cur = head 然后对两个链表进行遍历比较 将较小的值放入cur中,若一个遍历完,将cur的下一个指针指向另外那个链表

代码实现

package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.listnode.ListNode;/** * 题目解析: * 创建一个新的链表 * 两个链表的值进行比较 小的插入到新链表的下一个节点 */public class 合并两个排序的链表 {    public ListNode Merge(ListNode list1, ListNode list2) {        //进行判断 如果两个都空 直接返回null        //如果两个其中一个不为空 则返回不为空的那个        if (list1 == null) {            if (list2 != null)                return list2;            else return null;        }        if (list2 == null) {            if (list1 != null) {                return list1;            }        }        //创建一个新的链表        ListNode head = new ListNode(Integer.MIN_VALUE);        ListNode cur = head;        //开始比较两个数组        while (list1 != null && list2 != null) {            //进行判断 将小的放入到cur链表中            if (list1.val < list2.val) {                cur.next = list1;                cur = cur.next;//指针后移                list1 = list1.next;            } else {                cur.next = list2;                cur = cur.next;                list2 = list2.next;            }        }        //如果一个链表遍历完了而另外一个没有遍历完        if (list1 != null) {            cur.next = list1;        } else {            cur.next = list2;        }        return head.next;    }}

17、树的子结构

问题描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
题目分析
对r1和r2进行值比较,若相等,执行子树比较方法,若不相等,则对r1的左右子树和r2的值比较
代码实现
package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.TreeNode;/** * 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) */public class 树的子结构17 {    public boolean HasSubtree(TreeNode root1, TreeNode root2) {        //遍历大树        if(root1==null||root2==null){            return false;        }        if(root1.val==root2.val){            if(judge(root1,root2)){                return true;            }        }        return HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);    }    private boolean judge(TreeNode root1, TreeNode root2) {        if(root2==null){            return true;        }        if(root1==null){            return false;        }        if(root1.val==root2.val){            return (judge(root1.left,root2.left)&&judge(root1.right,root2.right));        }        return false;    }}

18、二叉树镜像:

问题描述:

给一个二叉树 构建他的镜像二叉树

比如:    源二叉树             8           /  \          6   10         / \  / \        5  7 9 11        镜像二叉树            8           /  \          10   6         / \  / \        11 9 7  5

输入:

{8,6,10,5,7,9,11}

复制

返回值:

{8,10,6,11,9,7,5}

问题解析:

递归调用 执行替换即可:若有左右子节点则替换左右子节点,

并且对左右子节点实现镜像方法

代码实现

package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.TreeNode;/** * 操作给定的二叉树,将其变为源二叉树的镜像 */public class 二叉树的镜像18 {    /**     * 问题解析:运用递归 如果二叉树的next不为null 则替换两个子节点     */    public TreeNode Mirror(TreeNode pRoot) {        // write code here        if (pRoot == null) {            return null;        } else            rootMirror(pRoot);        return pRoot;    }    public void rootMirror(TreeNode pRoot) {        //如果不为null 则替换二叉树的两个子节点        if (pRoot != null) {            TreeNode temp = pRoot.left;            pRoot.left = pRoot.right;            pRoot.right = temp;        }        //如果左子节点不为null 则递归向下遍历 执行替换        if (pRoot.left != null) {           rootMirror(pRoot.left);        }        //如果右子节点不为null 则递归向下遍历 执行替换        if (pRoot.right != null) {            rootMirror(pRoot.right);        }    }}

19、顺时针打印矩阵

问题描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]则依次打印出数字[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]示例1
题目分析
设四个参数分别代表上下左右,每次打印最外圈的,然后将矩阵缩小打印内圈即可
代码实现
package com.xu.fingeroffer.twenty;import java.util.ArrayList;import java.util.Arrays;public class 顺时针打印矩阵19 {    public static void main(String[] args) {        int[][] a = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};        ArrayList<Integer> list = printMatrix(a);        System.out.println(Arrays.toString(list.toArray()));    }    public static ArrayList<Integer> printMatrix(int[][] matrix) {        ArrayList<Integer> list = new ArrayList<>();        int up = 0;        int down = matrix.length - 1;        int left = 0;        int right = matrix[0].length - 1;        while (left < right && up < down) {            for (int i = left; i <= right; i++) {                list.add(matrix[up][i]);            }            for (int i = up + 1; i <= down; i++) {                list.add(matrix[i][right]);            }            for (int i = right - 1; i >= left; i--) {                list.add(matrix[down][i]);            }            for (int i = down - 1; i >= up + 1; i--) {                list.add(matrix[i][left]);            }            up++;            right--;            down--;            left++;        }        return list;    }}

20、包含min函数的栈

问题描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数,并且调用 min函数、push函数 及 pop函数 的时间复杂度都是 O(1)push(value):将value压入栈中pop():弹出栈顶元素top():获取栈顶元素min():获取栈中最小元素示例:输入:  ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]输出:  -1,2,1,-1解析:"PSH-1"表示将-1压入栈中,栈中元素为-1"PSH2"表示将2压入栈中,栈中元素为2,-1“MIN”表示获取此时栈中最小元素==>返回-1"TOP"表示获取栈顶元素==>返回2"POP"表示弹出栈顶元素,弹出2,栈中元素为-1"PSH-1"表示将1压入栈中,栈中元素为1,-1"TOP"表示获取栈顶元素==>返回1“MIN”表示获取此时栈中最小元素==>返回-1

题目分析

构建一个栈 ,用一个辅助栈进行存取最小值,若遇到 更小的,将小的放入辅助栈中,出栈的时候一并出
代码实现
package com.xu.fingeroffer.twenty;import java.util.Stack;/** * 构建一个栈 要求包含min函数 */public class 包含min函数的栈20 {    Stack<Integer> stack1 = new Stack();    Stack<Integer> stack2 = new Stack<Integer>();    public void push(int node) {        stack1.push(node);        if (stack2.isEmpty()) {            stack2.push(node);        }        if (stack2.peek() >= node) {            stack2.push(node);        }    }    public void pop() {        Integer pop = stack1.pop();        if (stack2.peek() == pop) {            stack2.pop();        }    }    public int top() {        return stack1.peek();    }    public int min() {        return stack2.peek();    }}

20-30

21:栈的压入弹出顺序

题目描述:
输入两个整数序列,第一个是压入顺序,第二个是弹出顺序,假设所有数字不等,判断第二个是否为第一个的弹出序列 
问题解析
     * 题目解析:     * 添加b标志位index     * 添加一个辅助栈,将a数组遍历压入栈中     * 判断栈顶元素是否等于b中数组下标的元素,若相等弹出栈顶元素,index加一,若栈为null或者不等,则进行下一次循环
代码实现
package com.xu.fingeroffer.thirty;import java.util.Stack;/** * 题目描述: * 输入两个整数序列,第一个是压入顺序,第二个是弹出顺序,假设所有数字不等,判断第二个是否为第一个的弹出序列 */public class 栈的压入弹出序列21 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5};        int[] b = {5, 4, 3, 2, 1};        System.out.println(Solution(a, b));    }    /**     * 题目解析:     * 添加b标志位index     * 添加一个辅助栈,将a数组遍历压入栈中     * 判断栈顶元素是否等于b中数组下标的元素,若相等弹出栈顶元素,index加一,若栈为null或者不等,则进行下一次循环     */    public static boolean Solution(int[] a, int[] b) {        if (a.length != b.length) {            return false;        }        int index = 0;        Stack<Integer> stack = new Stack<Integer>();        for (int i = 0; i < a.length; i++) {            //栈中添加a中的元素            stack.push(a[i]);            //判断栈中元素是否为null 或者b数组下标位置是否等于栈顶元素            while (!stack.isEmpty() && stack.peek() == b[index]) {                stack.pop();                index++;            }        }        return stack.isEmpty();    }}

22、从上到下打印二叉树

题目描述
描述从上往下打印出二叉树的每个节点,同层节点从左至右打印。示例1输入:{5,4,#,3,#,2,#,1}复制返回值:[5,4,3,2,1]
问题分析
创建一个list链表《TreeNode》 将root放进去一直遍历 当list 为空跳出循环将头节点取出来,若左子树不为null 将其放入,若右子树不为空 将其放入,把值放到list中
代码实现
package com.xu.fingeroffer.thirty;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;import java.util.LinkedList;public class 从上到下打印二叉树22 {    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {        ArrayList<Integer> list = new ArrayList<Integer>();        if (root == null) {            return list;        }        LinkedList<TreeNode> treelist = new LinkedList<>();        treelist.add(root);                while (!treelist.isEmpty()){            TreeNode node = treelist.removeFirst();            if(node.left!=null){                treelist.add(node.left);            }            if(node.right!=null){                treelist.add(node.right);            }            list.add(node.val);        }         return list;    }}

23:二叉树的后续遍历

题目描述
描述输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。(ps:我们约定空树不是二叉搜索树)示例1输入:[4,8,6,12,16,14,10]返回值:true
问题分析
这道题采用递归的思想,数组的最后一位则是树的根节点,然后在数组中找出第一个比他大的数字的下标,保证其后的所有数字都比数组最后一位数字大即可,否则 返回false
代码实现
package com.xu.fingeroffer.thirty;

import javax.sound.midi.Soundbank;

public class 二叉搜索树的后序遍历 {
    public static void main(String[] args) {
        int[] a = {4, 8, 6, 12, 16, 14, 10};
        System.out.println(VerifySquenceOfBST(a));
    }

    public static boolean VerifySquenceOfBST(int[] sequence) {
        if (sequence.length == 0 || sequence == null) {
            return false;
        }
        return check(sequence, 0, sequence.length - 1);
    }

    private static boolean check(int[] sequence, int start, int end) {
        if (start > end) {
            return true;
        }
        int tmp = sequence[end];
        int index = start;
        while (index < end && sequence[index] < sequence[end]) {
            index++;
        }
        for (int i = index; i < end; i++) {
            if (tmp >= sequence[i]) {
                return false;
            }
        }
        return check(sequence, start, index - 1) && check(sequence, index, end - 1);

    }
}

24、二叉树中和为某一值的路径

问题描述
描述
输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
题目分析
对二叉树的的节点向下遍历,递归调用target-root.val ,如果判断最终为二叉树的根节点,并且值等于target,则将其打印出来
代码实现
package com.xu.fingeroffer.thirty;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;public class 二叉树和为某一值的路径24 {    ArrayList<Integer> list = new ArrayList<>();    ArrayList<ArrayList<Integer>> lists = new ArrayList<>();    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {        if (root == null) {            return lists;        }        list.add(root.val);        target -= root.val;        if (target == 0 && root.left == null && root.right == null) {            lists.add(new ArrayList<Integer>(list));        }        FindPath(root.left, target);        FindPath(root.right, target);        list.remove(list.size() - 1);        return lists;    }}

25、复杂链表的复制

问题描述
描述输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。示例:输入:{1,2,3,4,5,3,5,#,2,#}输出:{1,2,3,4,5,3,5,#,2,#}解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5后半部分,3,5,#,2,#分别的表示为1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null如下图:
题目分析
代码实现

26、二叉搜索数与双向链表

题目描述
注意:1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继2.返回链表中的第一个节点的指针3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构4.你不用输出或者处理,示例中输出里面的英文,比如"From left to right are:"这样的,程序会根据你的返回值自动打印输出示例:输入: {10,6,14,4,8,12,16}输出:From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;解析:输入就是一棵二叉树,如上图,输出的时候会将这个双向链表从左到右输出,以及从右到左输出,确保答案的正确
问题分析
代码实现

27、字符串的排列

题目描述
描述输入一个字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。示例1输入:"ab"复制返回值:["ab","ba"]复制说明:返回["ba","ab"]也是正确的  
问题分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7WJD8taZ-1629185489440)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210817101444650.png)]

将字符串转换为数组,然后递归替换值
代码实现
package com.xu.fingeroffer.thirty;import java.util.ArrayList;import java.util.Collections;import java.util.List;public class 字符串排列27 {    public ArrayList<String> Permutation(String str) {        List<String> list = new ArrayList<>();        if (str.length() == 0) {            return (ArrayList) list;        }        fun(str.toCharArray(), list, 0);        Collections.sort(list);        return (ArrayList<String>) list;    }    private void fun(char[] ch, List<String> list, int i) {        if (i == ch.length - 1) {            //判断一下是否重复            if (!list.contains(new String(ch))) {                list.add(new String(ch));                return;            }        } else {            for (int j = i; j < ch.length; j++) {                swap(ch, i, j);                fun(ch, list, i + 1);                swap(ch, i, j);            }        }    }    private void swap(char[] str, int i, int j) {        if (i != j) {            char t = str[i];            str[i] = str[j];            str[j] = t;        }    }}

28:数组中超过一半的数字

题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000
问题分析
将数组遍历 保存在一个map中,判断map中的value  若其中某个值大于数组长度的一半 ,则返回
代码实现
 package com.xu.fingeroffer.thirty;import java.util.HashMap;/** * 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000 */public class 数组中超过一半的数字 {    public static int MoreThanHalfNum_Solution(int [] array) {        if(array.length==0){            return 0;        }        HashMap<Integer, Integer> map = new HashMap<>();        for (int i : array) {            if(map.containsKey(i)){                map.put(i,map.get(i)+1);            }else {                map.put(i,1);            }            if(map.get(i)>array.length/2){                return i;            }        }        return 0;    }}

29:最小的k个数

题目描述:
给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。- 0 <= k <= input.length <= 10000- 0 <= input[i] <= 10000
问题解析
将数组排序,然后将其加入到集合当中
代码实现
package com.xu.fingeroffer.thirty;import java.util.ArrayList;import java.util.Arrays;/** * 给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。 * 0 <= k <= input.length <= 10000 * 0 <= input[i] <= 10000 */public class 最小的k个数29 {    public static void main(String[] args) {        int[] a = {0, 1, 2, 1, 2};        System.out.println(Arrays.toString(GetLeastNumbers_Solution(a, 3).toArray()));        System.out.println(Arrays.toString(a));    }    public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {        ArrayList<Integer> list = new ArrayList<>();        for (int i = 0; i < input.length; i++) {            for (int j = input.length - 1; j > i; j--) {                if (input[i] > input[j]) {                    int temp;                    temp = input[i];                    input[i] = input[j];                    input[j] = temp;                }            }        }        for (int i = 0; i < k; i++) {            list.add(input[i]);        }        return list;    }}

30:连续子数组的最大和

题目描述
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).
问题解析
遍历数组,设定一个max值和变量值,变量值是从前往后遍历添加的 ,若数组为null 则max = 0,一次遍历,若变量值>max 则将变量值赋给max ,若变量小于0,则给该变量重新赋值
代码实现
package com.xu.fingeroffer.thirty;import java.util.Arrays;import java.util.LinkedList;import java.util.ListIterator;/** * 给一个数组 ,求该数组中子数组的最大值 */public class 连续子数组的最大和30 {    public static void main(String[] args) {        int[] a = {1,-2,3,10,-4,7,2,-5};        System.out.println(FindGreatestSumOfSubArray(a));    }    public static int FindGreatestSumOfSubArray(int[] array) {        if (array.length == 0) {            return 0;        }        LinkedList<Integer> list = new LinkedList<>();        int temp = 0;        int max = array[0];        for (int i = 0; i < array.length; i++) {            temp = temp + array[i];            list.add(array[i]);            if (max < temp) {                max = temp;            }            if (temp < 0) {                temp = 0;                list.clear();            }        }        System.out.println(Arrays.toString(list.toArray()));        return max;    }}

30-40

31:整数中1出现的字数

问题描述
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数例如,1~13中包含1的数字有1、10、11、12、13因此共出现6
题目分析
不会算法,乖乖遍历吧,int count =0 ;然后从1遍历到n ,对每次循环获得的值进行判断

代码实现

package com.xu.fingeroffer.forty;public class 整数中1出现的字数 {    public int NumberOf1Between1AndN_Solution(int n) {        int num = 0;        for (int i = 1; i <= n; i++) {            int tmp = i;            while (tmp != 0) {                if (tmp % 10 == 1) {                    num++;                }                tmp /= 10;            }        }        return num;    }}

32把数组排成最小的数

题目描述
描述输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。示例1输入:[3,32,321]复制返回值:"321323
问题分析
代码实现

33、丑数

问题描述
描述把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数
题目分析
代码实现

34:第一个只出现一次的字符

问题描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
题目分析
遍历字符串,将其放到map集合中,若有重复,则value+1,然后遍历该字符串,取第一个value值为1 的下标位置解法2:遍历s 将字符放入数组中,再次遍历找值为1的
代码实现
package com.xu.fingeroffer.forty;import java.util.HashMap;/** * 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数 */public class 第一个只出现一次的字符34 {    public static void main(String[] args) {        System.out.println(FirstNotRepeatingChar("google"));    }    public static int FirstNotRepeatingChar(String str) {        if (str == null || str.length() == 0) {            return -1;        }        int index = -1;        HashMap<Character, Integer> map = new HashMap<>();        for (int i = 0; i < str.length(); i++) {            if (map.get(str.charAt(i)) == null) {                map.put(str.charAt(i), 1);            } else {                map.put(str.charAt(i), map.get(str.charAt(i)) + 1);            }        }        for (int i = 0; i < str.length(); i++) {            if (map.get(str.charAt(i)) == 1) {                index = i;                return index;            }        }        return index;    }    public char firstUniqChar(String s) {        char[] c1 = new char[128];        for (int i = 0; i < s.length(); i++) {            c1[s.charAt(i)]++;        }        for (int i = 0; i < s.length(); i++) {            if(c1[s.charAt(i)]==1){                return s.charAt(i);            }        }        return ' ';    }}

35:数组中的逆序对

问题描述
 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
问题解析
通过归并排序的思想,在进行排序替换的时候,count增加的值应该是后一个mid-p1+1,即第一个数组后面的值
代码实现
package com.xu.fingeroffer.forty;public class 数组中的逆序对35 {    /**     * 采用分治算法,即归并排序进行排序,在替换我位置的时候使得count变化     *     * @param array     * @return     */    static int count = 0;    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5, 6, 7, 0};        System.out.println(InversePairs(a));    }    public static int InversePairs(int[] array) {        if (array.length != 0) {            divide(array, 0, array.length - 1);        }        return count;    }    private static void divide(int[] array, int start, int end) {        if (start >= end) {            return;        }        int mid = (end+start)/ 2;        divide(array, start, mid);//左边递归        divide(array, mid + 1, end);//右边递归        merge(array, start, mid, end);//合并两个子列    }    private static void merge(int[] array, int start, int mid, int end) {        //创建一个辅助数组,长度为end-star+1        int[] temp = new int[end - start + 1];        int p1 = start;        int p2 = mid + 1;        int k = 0;        //增加一个标志位,将数组数据加入辅助数组中,若一个数组全部添加完毕,则将另外一个数组的剩余数据添加到辅助数组中        while (p1 <= mid && p2 <= end) {            if (array[p1] < array[p2]) {                temp[k++] = array[p1++];            } else {                temp[k++] = array[p2++];                count = (count+(mid-p1+1))%1000000007;            }        }        while (p1 <= mid) {            temp[k++] = array[p1++];        }        while (p2 <= end) {            temp[k++] = array[p2++];        }        //将辅助数组的值返回到原数组中        for (int i = 0; i < temp.length; i++) {            array[i + start] = temp[i];        }    }}

36:两个链表的第一个公共结点

问题描述:
输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
题目解析:
给两个 无环的单链表,求两个链表集合中 存在共同的数 ,遍历两个链表即可得出
代码实现:
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.ListNode;/** * 输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的) */public class 两个链表的第一个公共节点36 {        //解法1    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {        if(pHead1==null||pHead2==null){            return null;        }        ListNode p1 = pHead1;        ListNode p2 = pHead2;        while (p1 != p2) {            p1 = p1.next;            p2 = p2.next;            if (p1 == p2) {                return p1;            }            if (p1 == null) {                p1 = pHead1;            }            if (p2 == null) {                p2 = pHead2;            }        }        return p1;    }    /**     * 解法2     * @param pHead1     * @param pHead2     * @return     */    public ListNode FindFirstCommonNode2(ListNode pHead1, ListNode pHead2) {        if(pHead1==null||pHead2==null){            return null;        }        ListNode p1 = pHead1;        ListNode p2 = pHead2;        while (p1!=p2){            p1=p1.next;            if(p1==p2){                return p1;            }            if(p1==null){                p1=pHead1;                p2=p2.next;            }        }        return p1;    }}

37、数字在排序数组中出现的次数

问题描述
描述统计一个数字在升序数组中出现的次数。
题目分析
二分查找对应的数字,然后找到他的上下界即可
代码实现
package com.xu.fingeroffer.forty;

public class 数字在升序数组中出现的次数37 {
    public static void main(String[] args) {
        int[] a = {1,3,3,3,3,4,5};
        System.out.println(GetNumberOfK1(a, 6));

    }

    /**
     * 直接遍历
     *
     * @param array
     * @param k
     * @return
     */
    public static int GetNumberOfK(int[] array, int k) {
        int num = 0;
        for (int i = 0; i < array.length; i++) {
            if (k == array[i]) {
                num++;
                if (i == array.length - 1) {
                    return num;
                }
                if (array[i + 1] != k) {
                    return num;
                }
            }
        }
        return num;

    }

    /**
     * 二分查找
     */
    public static int GetNumberOfK1(int[] array, int k) {
        int num = 0;
        int index = doubleSort(array, 0, array.length-1, k);
        if (index == -1) {
            return 0;
        }
        int m = index;
        int n = index;
        while (array[m] == k) {
            num += 1;
            m--;
            if (m < 0) {
                break;
            }
        }
        while (array[n] == k) {
            n++;
            num++;

            if (n == array.length) {
                break;
            }
        }

        return num - 1;
    }

    public static int doubleSort(int[] array, int low, int high, int k) {
        if(low>high){
            return -1;
        }
        int mid = low + ((high-low) / 2);
        if (array[mid] == k) {
            return mid;
        }
        if (array[mid] < k) {
            return doubleSort(array, mid + 1, high, k);
        } else if (array[mid] > k) {
            return doubleSort(array, low, mid - 1, k);
        } else {
            return -1;
        }
    }


}

38:二叉树的深度

题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
问题分析
递归比较 左右子节点的深度
代码实现
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.TreeNode;public class 二叉树的深度38 {    public int TreeDepth(TreeNode root) {        if(root==null){            return 0;        }        return  Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;    }}

39平衡二叉树

问题描述:
输入一棵二叉树,判断该二叉树是否是平衡二叉树。在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树**平衡二叉树**(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。注:我们约定空树是平衡二叉树。
题目分析:
计算二叉树左右子节点的最大深度,然后进行比较,若相差小于1则继续比较其子树,直到所有都为平衡二叉树的时候才为真
代码实现:
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.TreeNode;public class 二叉树的深度38 {    public int TreeDepth(TreeNode root) {        if(root==null){            return 0;        }        return  Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;    }}

40、数组中出现一次两个的数字

问题描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
题目分析
利用hashMap的特性,查找出哪个在map中的值为1即可
代码实现
 	package com.xu.fingeroffer.forty;import java.util.Arrays;import java.util.HashMap;public class 数组中只出现一次的两个数字40 {    public static void main(String[] args) {        int []a ={1,4,1,6};        System.out.println(Arrays.toString(FindNumsAppearOnce(a)));    }    public static int[] FindNumsAppearOnce (int[] array) {        // write code here        int []a =new int[2];        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < array.length; i++) {            if(map.get(array[i])==null){                map.put(array[i],1);            }else {                map.put(array[i], map.get(array[i])+1);            }        }        int index = 0;        int j = 0;        while (index!=array.length){            if(map.get(array[index])==1){                a[j] = array[index];                j++;            }            index++;        }        return  a;    }}

40-50

41:何为s的连续整数

题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck! 
问题解析
运用滑动窗口的方法 设定一个i j 若满足 若小于sum 则j+1 若大于sum 则i+1 ,直到j为sum的一半+1即可 
代码实现
package com.xu.fingeroffer.fifty;import java.util.ArrayList;import java.util.Arrays;import java.util.Map;/** * 求和为s的连续整数 */public class 和为S的连续整数41 {    public static void main(String[] args) {        ArrayList<ArrayList<Integer>> arrayLists = FindContinuousSequence(9);        System.out.println(Arrays.toString(arrayLists.toArray()));    }    public static ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {        ArrayList<ArrayList<Integer>> list1 = new ArrayList<>();        int i = 1;        int j = 1;        if(sum==1){            return list1;        }        while (j <= (sum / 2 + 1)) {            int num = 0;            for (int i1 = i; i1 <= j; i1++) {                num += i1;            }            if (num < sum) {                j++;            } else if (num > sum) {                i++;            } else if (num == sum) {                ArrayList<Integer> list = new ArrayList<>();                for (int i1 = i; i1 <= j; i1++) {                    list.add(i1);                }                j++;                list1.add(list);            }        }        return list1;    }}

42:和为s的两个数字

题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,返回两个数的乘积最小的,如果无法找出这样的数字,返回一个空数组即可。
问题解析
题解1: 使用hashMap,将值作为key存在hashMap中,遍历数组,当k=i时查找有没有key=s-i的值 若有则说明存在题解2:使用双指针,一个从前往后,一个从后往前,若两者相加=s 则i,和j下标对应的值刚刚好,若a[i]+a[j]>s,则j--,否则i++,直到i=j
代码实现

双指针法

package com.xu.fingeroffer.fifty;import java.util.ArrayList;import java.util.Arrays;public class 和为s的两个数字42 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};        ArrayList<Integer> list = FindNumbersWithSum(a, 21);        System.out.println(Arrays.toString(list.toArray()));    }    /**     * 双指针解法     *     * @param array 传入数组     * @param sum   需要计算的值     * @return     */    public static ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {        int m = 0;        int n = array.length - 1;        ArrayList<Integer> list = new ArrayList<>(2);        if (sum == 0 || array.length == 0) {            return list;        }        while (m != n) {            if (array[m] + array[n] == sum) {                list.add(0, array[m]);                list.add(1, array[n]);                return list;            }            if (array[m] + array[n] < sum) {                m++;            }            if (array[m] + array[n] > sum) {                n--;            }        }        return list;    }}

HashMap法

 public static ArrayList<Integer> FindNumbersWithSum2(int[] array, int sum) {        HashMap map = new HashMap(16);        ArrayList<Integer> a = new ArrayList<Integer>();        for (int i = 0; i <= array.length - 1; i++) {            if (map.get(sum - array[i]) != null) {                while (!a.isEmpty()) {                    a.remove(0);                }                a.add(sum - array[i]);                a.add(array[i]);            }            map.put(array[i], i);        }        return a;    }

43:左旋转字符串

问题描述
 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出(保证 K 小于等于 S 的长度)。例如,字符序列S=”abcXYZdef”,要求输出循环左移 3 位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它! 
问题分析
遍历,然后用stringbuffer拼接即可
代码实现
package com.xu.fingeroffer.fifty;public class 左旋转字符串43 {    public static void main(String[] args) {        String str = "abcXYZdef";        System.out.println(LeftRotateString(str, 3));    }    public static String LeftRotateString(String str,int n) {        if(n>str.length()){            return str;        }        return str.substring(n)+str.substring(0,n);    }}

44、翻转单词序列

题目描述
描述牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?示例1输入:"nowcoder. a am I"复制返回值:"I am a nowcoder."复制
问题解析
判断,每次在空格的时候,将前面的字符穿 插到总字符串的前面
代码实现
package com.xu.fingeroffer.forty;import java.util.Arrays;import java.util.HashMap;public class 数组中只出现一次的两个数字40 {    public static void main(String[] args) {        int []a ={1,4,1,6};        System.out.println(Arrays.toString(FindNumsAppearOnce(a)));    }    public static int[] FindNumsAppearOnce (int[] array) {        // write code here        int []a =new int[2];        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < array.length; i++) {            if(map.get(array[i])==null){                map.put(array[i],1);            }else {                map.put(array[i], map.get(array[i])+1);            }        }        int index = 0;        int j = 0;        while (index!=array.length){            if(map.get(array[index])==1){                a[j] = array[index];                j++;            }            index++;        }        return  a;    }}

45:扑克牌顺子

问题描述:
现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。有如下规则:\1. A为1,J为11,Q为12,K为13,A不能视为14\2. 大、小王为 0,0可以看作任意牌\3. 如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。例如:给出数据[6,0,2,0,4]中间的两个0一个看作3,一个看作5 。即:[6,3,2,5,4]这样这五张牌在[2,6]区间连续,输出true数据保证每组5个数字,每组最多含有4个零,数组的数取值为 [0, 13]
问题分析:
给数组进行排序,初始赋值num = 0;然后进行遍历,若为0,则跳出本次循环进入下一次循环,若array[i]=array[i+1] 返回false 否则num= num+array[i+1]-array[i];最后 对num的值进行判断,只要num的值小于4 则返回为真 否则 为假
代码实现
package com.xu.fingeroffer.fifry;import java.util.Arrays;public class 扑克牌顺子 {    public boolean IsContinuous(int[] numbers) {        //将传入的数组进行排序        Arrays.sort(numbers);        int num = 0;        //遍历数组        for (int i = 0; i < numbers.length-1; i++) {            //如果是0 则跳出循环            if (numbers[i] == 0) {                continue;            }            //如果有重复的数,返回false            else if (numbers[i] == numbers[i + 1]) {                return false;            } else {                num = num + (numbers[i + 1] - numbers[i]);            }        }        if (num <= 4) {            return true;        } else        {            return false;        }            }}

46、孩子们的游戏(约瑟夫环问题)

题目描述
描述每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)如果没有小朋友,请返回-1示例1输入:5,3复制返回值:
问题解析
利用循环链表的特性,每执行m次都会删除一个节点,若到队尾,则放到队头即可,知道队列大小为1
代码实现
package com.xu.fingeroffer.fifty;import java.util.Arrays;import java.util.LinkedList;/** * 类似约瑟夫环 * 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) * <p> * 如果没有小朋友,请返回-1 */public class 孩子们中的游戏约瑟夫环46 {    public static void main(String[] args) {        System.out.println(LastRemaining_Solution(5, 3));    }    public static int LastRemaining_Solution(int n, int m) {        if (n<=1||m==0) {            return -1;        }        LinkedList<Integer> list = new LinkedList<>();        for (int i = 0; i < n; i++) {            list.add(i);        }        int k  = 0;        while (list.size()!=1){            k = k + m;            // 第m人的索引位置            k = k % (list.size()) - 1;            // 判断是否到队尾            if (k < 0) {                System.out.println(list.get(list.size() - 1));                list.remove(list.size() - 1);                k = 0;            } else {                System.out.println(list.get(k));                list.remove(k);            }        }        return list.remove();    }}

48:不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
问题解析
使用位运算,异或 左移 
代码实现
package com.xu.fingeroffer.fifry;public class 位运算加48 {    public int Add(int num1,int num2) {        while (num2!=0){            int temp = num1^num2;            num2 = (num1&num2)<<1;            num1 = temp;        }        return num1;    }}

49、把字符串转换为整数

题目描述
描述将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0输入描述:输入一个字符串,包括数字字母符号,可以为空返回值描述:如果是合法的数值表达则返回该数字,否则返回0示例1输入:"+2147483647"复制返回值:2147483647复制示例2输入:"1a33"复制返回值:0
问题解析
从1号位置往后遍历,sum=sum*10+str.CharAt(i)-'0'若不是数字返回零,最后判断,若第一位为正,直接返回,为负,返回负数,为数字,则给sum*10的length-1次方乘以第一个数字+sum
代码实现
package com.xu.fingeroffer.fifty;public class 把字符串转换为数字49 {    public static void main(String[] args) {        System.out.println(StrToInt("1110"));    }    public static int StrToInt(String str) {        if(str==null||str.length()==0){            return 0;        }        int sum = 0;        int i = 0;        for (int j = 1; j < str.length(); j++) {            if((int)str.charAt(j)-'0'<0||(int)str.charAt(j)-'9'>0){                return 0;            }            sum=sum*10+(str.charAt(j)-'0');        }        if(str.charAt(0)=='+'){            return sum;        }else if(str.charAt(0)=='-'){            return -sum;        }else if(str.charAt(0)-'0'>=0&&(int)str.charAt(0)-'9'<=9){            int pow =(int) Math.pow(10, str.length()-1);            sum = sum + (str.charAt(0)-'0')*pow;        }                return sum;    }}

50-60

50:数组中重复的数字

题目描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXlzdMib-1629185489443)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210813172645364.png)]

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
问题解析
### 解法1:如题可知 数组中的数字大小不会超过数组的长度,所以可以用数组来实现,创建一个空数组与原数组长度相同,遍历元数组,使得新数组中原数组中值对应的下标位置的值+1;当对应下表值为2的时候 ,返回该值 若没有 返回-1### ==最优解法==: 对数组进行遍历,当num[i]!=i的时候,将i的值和num[i]的值进行替换,找到一个即可!!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qG7qmpVd-1629185489444)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210813173557770.png)]

代码实现
package com.xu.fingeroffer.fifty;import java.util.HashMap;public class 数组中重复的数字50 {    public static void main(String[] args) {        int []a = {2, 3, 1, 0, 2, 5};        System.out.println(duplicate3(a));    }    public static int duplicate (int[] numbers) {        if(numbers==null||numbers.length==0){            return -1;        }        // write code here        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < numbers.length; i++) {            if(map.get(numbers[i])==null){                map.put(numbers[i],1);            }else {                return numbers[i];            }        }        return -1;    }    /**     * 解法二     * @param numbers     * @return     */        public static int duplicate2 (int[] numbers) {            int []test = new int [numbers.length];            for (int i = 0; i < numbers.length; i++) {                test[numbers[i]]++;                if(test[numbers[i]]==2){                    return numbers[i];                }            }            return -1;        }    /**     * 解法3     * @param numbers     * @return     */    public static int duplicate3 (int[] numbers) {        for (int i = 0; i < numbers.length; i++) {            while (numbers[i]!=i){                if(numbers[i] == numbers[numbers[i]]){                    return numbers[i];                }                swap(numbers,i,numbers[i]);            };        }        return -1;    }    private static void swap(int[] numbers, int i, int j) {        int tmp = numbers[i];        numbers[i] = numbers[j];        numbers[j] = tmp;    }}

51、构建乘积数组

问题描述
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在
题目分析
进行双重循环,若遇到下标等于自身下标,则跳过
代码实现
package com.xu.fingeroffer.six;

import java.util.Arrays;

public class 构建乘积数组51 {
    public static void main(String[] args) {
        int []a = {1,2,3,4,5};
        System.out.println(Arrays.toString(multiply1(a)));
    }
    public static int[] multiply(int[] A) {
        int []a = new  int [A.length];
        for (int i = 0; i < a.length; i++) {
            int sum=1;
            for (int i1 = 0; i1 < A.length; i1++) {
                if(i1!=i){
                    sum=sum*A[i1];
                }
            }
            a[i]=sum;
        }
        return a;

    }
    public static int[] multiply1(int[] A) {
        int [] array = new int [A.length];
        int sum = 1;
        for (int i = 0; i < A.length; i++) {
            sum*=A[i];
        }
        for (int i = 0; i < array.length; i++) {
            array[i] = sum/A[i];
        }

        return array;
    }
}

52、正则表达式匹配

问题描述
描述
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
题目分析
代码实现

53、表示数值的字符串

问题描述
描述请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。科学计数法的数字(按顺序)可以分成以下几个部分:1.若干空格2.一个整数或者小数3.(可选)一个 'e' 或 'E' ,后面跟着一个整数(可正可负)4.若干空格小数(按顺序)可以分成以下几个部分:1.若干空格2.(可选)一个符号字符('+' 或 '-')3. 可能是以下描述格式之一:3.1 至少一位数字,后面跟着一个点 '.'3.2 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字3.3 一个点 '.' ,后面跟着至少一位数字4.若干空格整数(按顺序)可以分成以下几个部分:1.若干空格2.(可选)一个符号字符('+' 或 '-')3. 至少一位数字4.若干空格例如,字符串["+100","5e2","-123","3.1416","-1E-16"]都表示数值。但是["12e","1a3.14","1.2.3","+-5","12e+4.3"]都不是数值。
题目分析

代码实现

54:字符流中第一个不重复的数

题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。输入:"google"复制返回值:"ggg#ll"
问题解析

创建一个链表,插入字符的时候,链表中没有这个字符,则给链表最后一个节点插入这个字符。如果有,则将删除这个字符

代码实现
package com.xu.fingeroffer.six;import java.util.LinkedList;public class 字符流中的第一个不重复的数54 {    LinkedList<Character> list = new LinkedList<Character>();    int [] time = new int [128];    public void Insert(char ch)    {        if(time[ch]==0){            list.addLast(ch);        }else {            list.remove(new Character(ch));        }        time[ch]++;    }    public char FirstAppearingOnce()    {        if(list.isEmpty()){            return '#';        }else {            return list.getFirst();        }    }}

55、链表中的入口节点

问题描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

题目分析

将每次链表中的值放入到map中,一直遍历链表,若有存在的节点则返回

代码实现
package com.xu.fingeroffer.six;import java.util.Arrays;public class 构建乘积数组51 {    public static void main(String[] args) {        int []a = {1,2,3,4,5};        System.out.println(Arrays.toString(multiply1(a)));    }    public static int[] multiply(int[] A) {        int []a = new  int [A.length];        for (int i = 0; i < a.length; i++) {            int sum=1;            for (int i1 = 0; i1 < A.length; i1++) {                if(i1!=i){                    sum=sum*A[i1];                }            }            a[i]=sum;        }        return a;    }    public static int[] multiply1(int[] A) {        int [] array = new int [A.length];        int sum = 1;        for (int i = 0; i < A.length; i++) {            sum*=A[i];        }        for (int i = 0; i < array.length; i++) {            array[i] = sum/A[i];        }        return array;    }}

56、删除链表中重复的节点

问题描述
描述在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
题目分析

代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.ListNode;import java.util.Arrays;import java.util.LinkedList;public class 删除链表中重复的节点56 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 3, 4, 4, 5};        ListNode node = new ListNode(a);        deleteDuplication(node);    }    private static ListNode deleteDuplication(ListNode pHead) {        if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回            return pHead;        }        if (pHead.val == pHead.next.val) { // 当前结点是重复结点            ListNode pNode = pHead.next;            while (pNode != null && pNode.val == pHead.val) {                // 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点                pNode = pNode.next;            }            return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归        } else { // 当前结点不是重复结点            pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归            return pHead;        }    }}

57、二叉树的下一个结点

问题描述
描述给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针。下图为一棵有9个节点的二叉树。树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的用虚线表示
题目分析
给一个节点,先判断他有没有右子节点,如果有,则找该节点的右子树的最左节点如果没有右子树,则判断是不是父节点的左子节点,如果是,则返回父节点如果没有右子树,且是父节点的右节点,则回溯到父节点,从父节点开始查
代码实现
public TreeLinkNode GetNext(TreeLinkNode pNode) {        //如果当前节点有右子树,则查找右子树的最左节点        if (pNode.right != null) {            pNode = pNode.right;            while (pNode.left != null) {                pNode = pNode.left;            }            return pNode;        }        //若当前节点没有左右孩子,而且是父节点的左子树,则返回父节点        //防止空指针异常(必须先判断父节点不为null)        if (pNode.right == null && pNode.next != null && pNode == pNode.next.left) {            return pNode.next;        }        //若当前节点没有右孩子,而且是父节点的右子树,则沿着父节点回溯        //防止空指针异常(必须先判断父节点不为null)        if (pNode.right == null && pNode.next != null && pNode == pNode.next.right) {            pNode = pNode.next;            while (pNode.next != null && pNode != pNode.next.left) {                pNode = pNode.next;            }            return pNode.next;        }        return null;    }	    

58、对称的二叉树

问题描述
描述请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
题目分析
对根节点的的左右子树的子节点进行判断,然后判断其的左右是否对称,即是他的右子树的左节点,左子树的右节点 即判断左子树的右节点和右子树的左节点是否一致
代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.TreeNode;public class 对称的二叉树58 {    boolean isSymmetrical(TreeNode pRoot) {        if (pRoot == null) {            return true;        }        return isSymmetrical(pRoot.left, pRoot.right);    }    private boolean isSymmetrical(TreeNode left, TreeNode right) {        if (left == null && right == null) {            return true;        }        if (left == null || right == null) {            return false;        }        return left.val == right.val                && isSymmetrical(left.right, right.left)                && isSymmetrical(left.left, right.right);    }}

59、按之字顺序打印二叉树

问题描述
描述给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)例如:给定的二叉树是{1,2,3,#,#,4,5}该二叉树之字形层序遍历的结果是[[1],[3,2],[4,5]]示例1
题目分析
用一个辅助数链表,将根节点存入栈中,取出根节点的值,将其数值放入集合,将其左右子树放入到栈中,判断栈空即可返回
代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;        import java.util.Stack;public class 按之字顺序打印二叉树59 {    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {        ArrayList<ArrayList<Integer>> printResult = new ArrayList<>();        if (pRoot == null) {            return printResult;        }        //碰到逆序的时候要想到栈!!!        Stack<TreeNode> floorNode = new Stack<>();        floorNode.push(pRoot);        Print(1, printResult,floorNode);        return printResult;    }    private void Print(int floor, ArrayList<ArrayList<Integer>> printResult, Stack<TreeNode> floorNode) {        if (floorNode.size() == 0) {            return;        }        //将上一层存进结果中        ArrayList<Integer> temp = new ArrayList<>();        for (TreeNode treeNode : floorNode) {            temp.add(treeNode.val);        }        printResult.add(temp);        Stack<TreeNode> nextFloorNode = new Stack<>();        while (!floorNode.empty()){            TreeNode node = floorNode.pop();            if (floor % 2 == 0) {                if (node.left!=null) {                    nextFloorNode.push(node.left);                }                if (node.right!=null) {                    nextFloorNode.push(node.right);                }            } else {                if (node.right!=null) {                    nextFloorNode.push(node.right);                }                if (node.left!=null) {                    nextFloorNode.push(node.left);                }            }        }        Print(floor + 1, printResult, nextFloorNode);    }}

60-66

60、把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。例如:给定的二叉树是{1,2,3,#,#,4,5}该二叉树多行打印层序遍历的结果是[[1],[2,3],[4,5]]
示例1输入:{1,2,3,#,#,4,5}复制返回值:[[1],[2,3],[4,5]]
问题解析
创建一个放Integer集合的的集合,创建一个放树的队列,先将根节点放入队列中,当队列的大小不为空的时候,递归,将节点值放入集合中,判断节点是否有子节点,若有,将子节点放入队列中
代码实现
package com.xu.fingeroffer.seventy;

import com.xu.fingeroffer.node.TreeNode;

import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import java.util.ArrayList;
import java.util.LinkedList;

public class 把二叉树打印成多行60 {
    ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        if (pRoot == null) {
            return null;
        }
        LinkedList<TreeNode> list = new LinkedList<>();
        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
        list.add(pRoot);
        while (!list.isEmpty()){
            ArrayList<Integer> integers = new ArrayList<>();
            int size = list.size();
            while (size !=0){
                TreeNode remove = list.remove(0);
                size--;
                integers.add(remove.val);
                if(remove.left!=null){
                    list.add(remove.left);
                }
                if(remove.right!=null){
                    list.add(remove.right);
                }

            }
            lists.add(integers);
        }
        return lists;
    }

}

61、序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,可以根据层序遍历并特定标志空结点的方案序列化,也可以根据满二叉树结点位置的标号规律来序列化,还可以根据先序遍历和中序遍历的结果来序列化。

假如一棵树共有 2 个结点, 其根结点为 1 ,根结点右子结点为 2 ,没有其他结点。按照上面第一种说法可以序列化为“1,#,2,#,#”,按照上面第二种说法可以序列化为“{0:1,2:2}”,按照上面第三种说法可以序列化为“1,2;2,1”,这三种序列化的结果都包含足以构建一棵与原二叉树完全相同的二叉树的信息。

不对序列化之后的字符串进行约束,所以欢迎各种奇思妙想。
问题分析
将二叉树层序遍历 即可转为一个字符串,根据字符串,提取数字 重组为二叉树
代码实现
 String Serialize(TreeNode root) {
        List<String> list = new ArrayList<>();
        if(root == null) {
            return "";
        }
        Deque<TreeNode> que = new LinkedList<>();
        TreeNode p = root;
        que.offer(p);
        while(!que.isEmpty()) {
            int len = que.size();
            for(int i = 0; i < len; i++) {
                p = que.poll();
                if(p == null) {
                    list.add("#");
                } else {
                    list.add(String.valueOf(p.val));
                    que.offer(p.left);
                    que.offer(p.right);
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        for(int i = 0; i < list.size(); i++) {
            sb.append(list.get(i));
            if(i < list.size()-1) {
                sb.append(",");
            }
        }
        sb.append("}");
        return sb.toString();
    }

    TreeNode Deserialize(String str) {
        if(str == null || str.length() == 0) {
            return null;
        }
        if(str.equals("{}")) {
            return null;
        }
        String subStr = str.substring(1, str.length()-1);
        String[] nodes = subStr.split(",");
        TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
        TreeNode p = root;
        Deque<TreeNode> que = new LinkedList<>();
        que.offer(p);
        int idx = 1;
        while(!que.isEmpty()) {
            int len = que.size();
            for(int i = 0; i < len; i++) {
                p = que.poll();
                if("#".equals(nodes[idx])) {
                    p.left = null;
                } else {
                    p.left = new TreeNode(Integer.parseInt(nodes[idx]));
                    que.offer(p.left);
                }
                idx++;
                if("#".equals(nodes[idx])) {
                    p.right = null;
                } else {
                    p.right = new TreeNode(Integer.parseInt(nodes[idx]));
                    que.offer(p.right);
                }
                idx++;
            }
        }
        return root;
    }

62、二叉搜索树的第k个节点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。
问题分析
第k小即中序遍历的第k个数值
代码实现
package com.xu.fingeroffer.seventy;

import com.xu.fingeroffer.node.TreeNode;

import java.util.ArrayList;

public class 二叉搜索时的第k个节点61 {
    int i = 0;
    TreeNode KthNode(TreeNode pRoot, int k) {
        TreeNode node = getK(pRoot, k);
        return node;
    }

    TreeNode getK(TreeNode pRoot, int k) {
        TreeNode node = null;
        if (pRoot.left != null) {
            node = getK(pRoot.left, k);
        }
        i++;
        if (i == k) {
            node = pRoot;
        }
        if (pRoot == null && pRoot.right == null) {
            node = getK(pRoot.right, k);
        }
        return node;
    }
}

63、数据流中的中位数

问题描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

输入:
[5,2,3,4,1,6,7,0,8]

返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "

说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...  
题目分析
利用arraylist的特性,在插入的时候插入排序一下,然后在求中位数的时候,如果list的size为奇数则返回最中间的那个,如果为偶数,则中间两个数取平均值
代码实现
package com.xu.fingeroffer.seventy;

import java.util.ArrayList;
import java.util.LinkedList;

public class 数据流中的中位数63 {
    ArrayList<Integer> list = new ArrayList<>();

    public void Insert(Integer num) {
        if (list.size() == 0) {
            list.add(num);
            return;
        }
        int start = 0;
        int end = list.size();
        int mid = 0;
        while (start < end) {
            mid = (end + start) / 2;
            Integer integer = list.get(mid);
            if (num > integer) {
                start = mid + 1;
            } else if (num < integer) {
                end = mid - 1;
            } else {
                list.add(mid, num);
                return;
            }
        }
        list.add(mid, num);
    }

    public Double GetMedian() {
        if ((list.size() & 1) == 1) {
            return Double.valueOf(list.get(list.size()/2));
        }else {
            return(Double.valueOf(list.get(list.size()/2))+Double.valueOf(list.get(list.size()/2-1)));
        }

    }
}
代码实现

64、滑动窗口最大值

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
问题分析
利用双指针的思想,窗口大小固定,只需要在遍历数组的时候求出该窗口的最大值即可
代码实现
package com.xu.fingeroffer.seventy;

import sun.nio.cs.SingleByte;

import java.util.ArrayList;
import java.util.Arrays;

public class 滑动窗口的最大值64 {
    public static void main(String[] args) {
        int []a = {2,3,4,2,6,2,5,1};
        ArrayList<Integer> windows = maxInWindows(a, 3);
        System.out.println(Arrays.toString(windows.toArray()));
    }
    public static ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> list = new ArrayList<>();
        if(size==0){
            return list;
        }
        if(size==1){
            for (int i = 0; i < num.length; i++) {
                list.add(num[i]);
            }
            return list;
        }
        int i = 0 ;
        int j = i+size-1;
        while (j<num.length){
            for (int i1 = 0; i1 < num.length-size+1; i1++) {
                int max = num[i] ;
                for (int k = i+1; k <= j; k++) {
                    if(num[k]>max) {
                        max=num[k];
                    }
                }
                list.add(max);
                j++;
                i++;
            }
        }

        return list;
    }
}

65、矩阵中的路径

问题描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
题目分析
遍历数组,找到数组的头,根据数组的头,找后面的,若都能找到返回true否则返回false
代码实现
package com.xu.fingeroffer.seventy;

import sun.misc.VM;

public class 矩阵中的路径 {
    public static void main(String[] args) {
        char[][] a = {{'a', 'b', 'c', 'e'}, {'s', 'f', 'c', 's'}, {'a', 'd', 'e', 'e'}};
        System.out.println(hasPath(a, "abcced"));

    }

    public static boolean hasPath(char[][] matrix, String word) {
        boolean flag = false;
        // 遍历找初始位置
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (word.charAt(0) == matrix[i][j]) {
                    if( dfs(matrix, word, i, j, 1)){
                        flag = true;
                        return flag;
                    };
                }
            }
        }
        return flag;
    }

    private static boolean dfs(char[][] matrix, String word, int i, int j, int index) {
        if (index > word.length() - 1) {
            return true;
        }
        char c = word.charAt(index);
        if (i + 1 < matrix.length&&c == matrix[i + 1][j]  ) {
            return dfs(matrix, word, i + 1, j, index + 1);
        } else if (i > 0 && c == matrix[i - 1][j]) {
            return dfs(matrix, word, i - 1, j, index + 1);
        } else if (j + 1 < matrix[0].length && c == matrix[i][j + 1]) {
            return dfs(matrix, word, i, j + 1, index + 1);
        } else if (j>0&&c == matrix[i][j - 1] ) {
            return dfs(matrix, word, i, j - 1, index + 1);
        } else {
            return false;
        }

    }
}

66、机器人的运动范围

题目描述
地上有一个rows行和cols列的方格。坐标从 [0,0][rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
问题解析
由题意可得,机器人运动的步数应该为threshold,每次运动都是上下左右运动,不能进入行坐标+列坐标之和大于threshold的数,进行dfs算法,将row和cols的列相加和threshold进行比较,如果小于threshold,则count++,否则,直接返回,然后对其的上下左右进行遍历
代码实现
package com.xu.fingeroffer.seventy;public class 机器人的运动范围 {    private int[][] range = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};    private boolean[][] flag;    private int count;    public int movingCount(int threshold, int rows, int cols) {        flag = new boolean[rows][cols];        count = 0;        //从0,0开始计数        dfs(0, 0, rows, cols, threshold);        return count;    }    /**     * dfs算法     *     * @param i         当前纵坐标     * @param j         当前横坐标     * @param rows      数组长度     * @param cols      数组宽度     * @param threshold 临界值     */    private void dfs(int i, int j, int rows, int cols, int threshold) {        //判断,如果不符合临界值条件,直接返回 不在数组中,或者被遍历过         if (i < 0 || i >= rows || j < 0 || j > cols || flag[i][j]) {            return;        }        flag[i][j] = true;        //根据i j 坐标 判断他是否符合条件        int sum = 0, m = i, n = j;                while (m > 0) {            sum += (m % 10);            m /= 10;        }        while (n > 0) {            sum += (n % 10);            n /= 10;        }        if (sum <= threshold) {            count++;        } else {            return;        }        for (int i1 = 0; i1 < range.length; i1++) {            dfs(i + range[i1][0], j + range[i1][1], rows, cols, threshold);        }    }}

67、剪绳子问题

题目描述
描述地上有一个rows行和cols列的方格。坐标从 [0,0] 到 [rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
问题解析
将绳子分为n个3,每三个见一次,如果到最后,绳子长度为4,则最后为切为22若
代码实现
package com.xu.fingeroffer.seventy;

public class 剪绳子67 {
    public static void main(String[] args) {
        System.out.println(cutRope(8));
    }

    public static int cutRope(int target) {
        if (target == 1 || target == 2 || target == 3) {
            return target;
        }

        int sum = 1;
        while (target > 0) {
            if (target <= 4) {
                sum = target * sum;
                target = 0;
            } else {
                target = target - 3;
                sum = sum * 3;
            }

        }
        return sum;
    }
}

力扣

双指针问题

977、有序数组的平方

题目描述
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

问题解析
利用双指针,建立一个辅助数组,对数组的头与数组尾部进行比较,然后将较大的值插入到新的数组最末端
代码实现
public static int[] sortedSquares(int[] nums) {
    int left = 0;
    int right = nums.length-1;
    int[] assist = new int[nums.length];
    int index = nums.length - 1;
    while (left <= right) {
        if (nums[left] * nums[left] > nums[right] * nums[right]) {
            assist[index--] = nums[left] * nums[left];
            left++;
        } else {
            assist[index--] = nums[right] * nums[right];
            right--;
        }
    }


    return assist;
}

189、旋转数组

题目描述
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

 输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
问题解析

利用循环队列,将数组的值加入到新的数组中,返回新的数组

代码实现
    public static void rotate(int[] nums, int k) {
        if(nums.length==1){
            return;
        }
            k=k%nums.length;

        int[] array = new int[nums.length];
        for (int i = 0; i < array.length; i++) {
            if(k==0){
                k=nums.length;
            }
            array[i] = nums[nums.length-k];
            k--;
        }
        for (int i = 0; i < array.length; i++) {
            nums[i] = array[i];
        }

    }

283、移动零

题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。示例:输入: [0,1,0,3,12]输出: [1,3,12,0,0]说明:必须在原数组上操作,不能拷贝额外的数组。尽量减少操作次数。
问题分析
利用双指针的思想,若右指针不为0,则指针右移,若右指针为0,则与左指针交换位置,左指针右移,直到右指针移出即可
代码实现
package com.xu.lecode.算法.双指针;

import java.util.Arrays;

public class 移动零283 {
    public static void main(String[] args) {
        int[] a = {0, 1, 0, 3, 12};
        moveZeroes(a);
        System.out.println(Arrays.toString(a));
    }

    public static void moveZeroes(int[] nums) {
        int left = 0;
        int right = 0;
        while (right < nums.length) {
            if (nums[right] != 0) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
                left++;
            }
            right++;
        }
    }
}

167、两数之和

题目描述
给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
问题分析
利用双指针,一个从前往后,一个从后往前,使两数之和和target进行比较,若大于则右指针左移,若小于,左指针右移,若等于,则返回
代码实现
package com.xu.lecode.算法.双指针;import java.util.Arrays;public class 两数之和167 {    public static void main(String[] args) {        int []a ={2,7,11,15};        System.out.println(Arrays.toString(twoSum(a, 9)));    }    public static int[] twoSum(int[] numbers, int target) {        int[] a = new int[2];        int m = 0;        int n = numbers.length - 1;        while (m < n) {            if(numbers[m]+numbers[n]>target){                n--;            }else if(numbers[m]+numbers[n]<target){                m++;            }else {                a[0] = m+1;                a[1] = n+1;                return a;            }        }        return a;    }}

344、反转字符串单词

题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

 

示例 1:

输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
问题解析
利用双指针的思想,进行替换即可
代码实现
package com.xu.lecode.数据结构.数组;

import java.util.Arrays;

public class 反转字符串344 {
    public static void main(String[] args) {
        char[] a = {'h', 'e', 'l', 'l', 'o'};
        reverseString(a);
        System.out.println(Arrays.toString(a));
    }

    public static void reverseString(char[] s) {
        int m = 0;
        int n = s.length - 1;
        while (m < n) {
            char tmp = s[m];
            s[m] = s[n];
            s[n] = tmp;
            m++;
            n--;
        }
    }
}

557、反转字符串中的单词

题目描述
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 示例:输入:"Let's take LeetCode contest"输出:"s'teL ekat edoCteeL tsetnoc"
问题解析
将字符串分为字符数组,遍历,遇到空格就从start到i-1替换,如果遍历到数组尾部,则从start到i进行替换
代码实现
package com.xu.lecode.数据结构.数组;

import java.util.Arrays;

public class 反转字符串344 {
    public static void main(String[] args) {
        char[] a = {'h', 'e', 'l', 'l', 'o'};
        reverseString(a);
        System.out.println(Arrays.toString(a));
    }

    public static void reverseString(char[] s) {
        int m = 0;
        int n = s.length - 1;
        while (m < n) {
            char tmp = s[m];
            s[m] = s[n];
            s[n] = tmp;
            m++;
            n--;
        }
    }
}

876、链表的中间节点

题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

 

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
问题解析
求中间值的问题,可以使用快慢指针来解决这一问题,当快指针到末尾的时候,满指针所在的节点,即为中间节点(注意快指针空指针异常问题)
代码实现
package com.xu.lecode.算法.双指针;import com.xu.fingeroffer.node.ListNode;public class 链表的中间节点876 {    public ListNode middleNode(ListNode head) {        if (head == null || head.next == null) {            return head;        }        ListNode node1 = head;        ListNode node2 = head;        while (node1 != null) {            if (node1.next != null) {                node1 = node1.next.next;                node2 = node2.next;            } else {                node1 = node1.next;            }        }        return node2;    }}

19、链表的倒数第N个结点

问题描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。进阶:你能尝试使用一趟扫描实现吗?
题目分析
使用快慢指针,解决这个问题,链表中,不new对象使用的是同一个对象,所以 ,slow 和fast用的都是同一个对象,而修改,
代码实现
package com.xu.lecode.算法.双指针;

import com.xu.fingeroffer.node.ListNode;

public class 删除链表的倒数第n个节点19 {
    public static void main(String[] args) {
    }

    public static ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        //快指针
        ListNode first = head;
        //满指针
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;

    }

}

3、无重复的最长字符串

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
问题分析
滑动窗口,m=0,n=1,如果m和n直接没有存在相同的字符,则n++,size++;如果m-n中存在相同的字符则将size和max比较,较大的给max,当n走到末尾的时候,比较一次size和max 最终返回较大的值
代码实现
int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;

深度优先广度优先

567、字符串的排列

题目描述
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,s1 的排列之一是 s2 的 子串 。
问题解析
将s1 的字符串中的所有值,存入到一个数组中,然后对s2利用滑动窗口,窗口长度为n进行遍历,如果数组的所有值都一一对应,则为真,否则为假
代码实现
package com.xu.lecode.算法.双指针;

import java.util.HashMap;
import java.util.Map;

public class 无重复字符的最长字串3 {
    public static void main(String[] args) {
        String s = "pwwkew";
        System.out.println(lengthOfLongestSubstring(s));
    }

    public static int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;


    }
}

733、图像渲染

题目描述
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。

给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。

为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。

最后返回经过上色渲染后的图像。

问题解析
深度优先算法计算
代码实现
package com.xu.lecode.算法.深度优先或广度优先;

public class 图像渲染733 {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        int currColor = image[sr][sc];
        if (currColor != newColor) {
            dfsFill(image, sr, sc, currColor, newColor);
        }
        return image;
    }

    public void dfsFill(int[][] grid, int i, int j, int currColor, int newColor) {
        if (i < 0 || i >= grid.length) {
            return;
        }
        if (j < 0 || j >= grid[0].length) {
            return;
        }
        if (grid[i][j] == currColor) {
            grid[i][j] = newColor;
            dfsFill(grid, i - 1, j, currColor, newColor);
            dfsFill(grid, i, j + 1, currColor, newColor);
            dfsFill(grid, i + 1, j, currColor, newColor);
            dfsFill(grid, i, j - 1, currColor, newColor);
        }
    }


}

695、岛屿中的最大面积

题目描述
给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
问题解析d
采用bfs算法,遍历数组,对数组中的每个数据都采用bfs算法,即遍历数组,进行查找,如果为1,则找他的周围的一圈,使得num+1,最后找出最大的num
代码实现
package com.xu.lecode.算法.深度优先或广度优先;

import java.util.Deque;
import java.util.LinkedList;

public class 岛屿的最大面积695 {
    public int maxAreaOfIsland(int[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == 1) {
                    res = Math.max(res, dfs(i, j, grid));
                }
            }
        }
        return res;
    }

    private int dfs(int i, int j, int[][] grid) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] == 0) {
            return 0;
        }
        grid[i][j] = 0;
        int num = 1;
        num += dfs(i + 1, j, grid);
        num += dfs(i - 1, j, grid);
        num += dfs(i, j + 1, grid);
        num += dfs(i, j - 1, grid);
        return num;

    }

}



617合并二叉树

题目描述
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

问题解析
深搜,先分别判断r1 r2是否为null,若为null 返回另外一个,然后new 一个新的节点,递归调用,返回新的节点
代码实现
package com.xu.lecode.算法.深度优先或广度优先;import com.xu.fingeroffer.node.TreeNode;import com.xu.lecode.算法.小飞.机器人碰撞问题.Robot;public class 合并二叉树617 {    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {        if (root1 == null) {            return root2;        }        if (root2 == null) {            return root1;        }        TreeNode node = new TreeNode(root1.val + root2.val);        node.left = mergeTrees(root1.left, root2.left);        node.right = mergeTrees(root1.right, root2.right);        return node;    }}

数据结构与算法

排序

归并排序(分治算法)

思想,递归拆分数组,将其进行比较替换后 在进行合并

代码实现

package com.xu.fingeroffer.递归;

import java.util.Arrays;

public class 归并排序 {

    public static void main(String[] args) {
        int a[] = {9, 2, 6, 3, 5, 7, 10, 11, 12};
        int[] sort = sort(a);
        System.out.println(Arrays.toString(sort));
        }

    static int[] sort(int[] array) {
        if (array.length != 0) {
            divide(array, 0, array.length - 1);
        }
        return array;
    }

    private static void divide(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }
        int mid = (end+start)/ 2;
        divide(array, start, mid);//左边递归
        divide(array, mid + 1, end);//右边递归
        memerg(array, start, mid, end);//合并两个子列
    }

    private static void memerg(int[] array, int start, int mid, int end) {
        int[] temp = new int[end - start + 1];
        int p1 = start;
        int p2 = mid + 1;
        int k = 0;
        while (p1 <= mid && p2 <= end) {
            if (array[p1] < array[p2]) {
                temp[k++] = array[p1++];
            } else {
                temp[k++] = array[p2++];
            }
        }
        while (p1 <= mid) {
            temp[k++] = array[p1++];
        }
        while (p2 <= end) {
            temp[k++] = array[p2++];
        }
        for (int i = 0; i < temp.length; i++) {
            array[i + start] = temp[i];
        }

    }


}

二叉树

深搜

先序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的先序遍历144 {
    public List<Integer> preorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();

        if (root == null) {
            return list;
        }
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if(root==null){
            return list;
        }
        list.add(root.val);
        list = dfs(root.left,list);
        list = dfs(root.right,list);

        return list;

    }


}

中序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的中序遍历94{
    public List<Integer> inorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();

        if (root == null) {
            return list;
        }
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if(root==null){
            return list;
        }

        if(root.left!=null){
            dfs(root.left,list);
        }
        list.add(root.val);
        if(root.right!=null){
         dfs(root.right,list);
        }
        return list;
    }
}

后序遍历

package com.xu.lecode.数据结构.二叉树;

import com.xu.node.TreeNode;

import java.util.LinkedList;
import java.util.List;

public class 二叉树的后序遍历145 {

    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<>();
        list = dfs(root, list);
        return list;

    }

    private LinkedList<Integer> dfs(TreeNode root, LinkedList<Integer> list) {
        if (root == null) {
            return list;
        }
        if (root.left != null) {
            list = dfs(root.left, list);
        }
        if(root.right!=null){
            list = dfs(root.right,list);
        }
        list.add(root.val);
        return list;
    }
}

剑指offer

1-10

1、二维数组的查找

题目描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。

给定 target = 3,返回 false。

0 <= array.length <= 500
0 <= array[0].length <= 500
问题分析
要求时间复杂度 O(M + N),空间复杂度 O(1)。其中 M 为行数,N 为 列数。


从二维数组左上角进行查找,若target>array(i)(j)则j+1若小于i-1 若没有返回false
代码实现
package com.xu.fingeroffer.ten;

public class 二维数组的查找1 {
    public static void main(String[] args) {
//        int[][] a = {{1, 2, 8, 9}, {4, 7, 10, 13}};
        int [][] a = new int[2][4];
        System.out.println(Find(7, a));

    }

    public static boolean Find(int target, int[][] array) {
        int m=array.length;
        int n=array[0].length;
        int i=0;
        int j=n-1;
        while(i>=0 && i<m && j>=0 && j<n){
            int mid=array[i][j];
            if(target<mid){
                j--;
            }else if(target>mid){
                i++;
            }else{
                return true;
            }
        }
        return false;
    }
}

2、替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
问题分析
使用stringbuilder工具类 若charAt为空格 则拼接%20即可
代码实现
package com.xu.fingeroffer.ten;

public class 替换空格2 {
    public static void main(String[] args) {
        System.out.println(replaceSpace("a b"));
    }
    public static String replaceSpace (String s) {
        // write code here
        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();
    }
}

3、从尾到头打印链表

题目描述
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:

返回一个数组为[3,2,1]

0 <= 链表长度 <= 1000
示例1
输入:
{1,2,3}
复制
返回值:
[3,2,1]

问题解析
利用用linkedlist特性 实际就是栈 插入后 然后将其放到arraylist中即可
代码实现
package com.xu.fingeroffer.ten;

import com.xu.fingeroffer.node.ListNode;

import java.util.ArrayList;
import java.util.LinkedList;

public class 从尾到头打印链表3 {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        LinkedList<Integer> list1 = new LinkedList<>();
        if(listNode==null){
            return list;
        }
        while (listNode!=null){
            list1.addFirst(listNode.val);
            listNode = listNode.next;
        }
        for (Integer integer : list1) {
            list.add(integer);
        }
        return list;
    }
}

4、重建二叉树

问题描述
描述
给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
题目分析
已知是一个中序遍历和先序遍历数组,先序遍历数组中找到与中序遍历头相等的值,然后对两个数组进行拆分,递归成一个个小问题即可,
注意:两个递归的函数分别为rebuild(pre,pleft+1,pleft+i-vleft,vin,vleft,i-1)
rebuild(pre,pleft+i-vleft+1,pright,vin,i+1,vleft)
代码实现
package com.xu.fingeroffer.ten;

import com.xu.fingeroffer.node.TreeNode;

public class 重建二叉树4 {
    public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
        if (pre.length != vin.length) {
            return null;
        }
        return rebuildBinaryTree(pre, 0, pre.length-1, vin, 0, vin.length-1);
    }

    private TreeNode rebuildBinaryTree(int[] pre, int pleft, int pright, int[] vin, int vleft, int vright) {
        if (pleft > pright || vleft > pright) {
            return null;
        }
        TreeNode root = new TreeNode(pre[pleft]);
        for (int i = pleft; i <= vright; i++) {
            if (pre[pleft] == vin[i]) {
                root.left = rebuildBinaryTree(pre, pleft + 1, pleft+i-vleft, vin, vleft, i - 1);
                root.right = rebuildBinaryTree(pre, pleft+i-vleft+1, pright, vin, i + 1, vright);

            }
        }

        return root;
    }


}

5、用两个栈实现队列

问题描述
描述
用两个栈来实现一个队列,分别完成在队列尾部插入整数(push)和在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。

示例:
输入:
["PSH1","PSH2","POP","POP"]
返回:
1,2
解析:
"PSH1":代表将1插入队列尾部
"PSH2":代表将2插入队列尾部
"POP“:代表删除一个元素,先进先出=>返回1
"POP“:代表删除一个元素,先进先出=>返回2
问题分析
利用两个栈,push的时候push到stack1中,而poll的时候 是将stack1中的所有东西push到stack2中(若stack2不为空则不能push,如果要push到stack2中,则要么把stack1中所有值push,要么将stack2push满),然后 在对stack2进行poll(在poll的时候判断)
代码实现
package com.xu.fingeroffer.ten;

import java.util.Stack;

public class 两个栈实现队列 {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);

    }

    /**
     * 在pop的时候 需要将stack1中的值push到stack2中,然后将stack2中的值pop出
     * 注意:若stack2不为null的时候,不能将stack1中的值push进去,若要push进入到stack2,需要将stack1中的所有值push进去
     * @return
     */
    public int pop() {
        if(stack2.isEmpty()){
            while (!stack1.isEmpty()){
                Integer pop = stack1.pop();
                stack2.push(pop);
            }
        }


        return stack2.pop();
    }
}

6、选择数组最小数字

题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0
问题分析
遍历数组,对每个数字与前一个比较,若大于,则直接返回,若没有返回array[0],若数组长度为0 返回0
代码实现
package com.xu.fingeroffer.ten;

public class 旋转数组最小数字6 {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){
            return 0;
        }
        for (int i = 0; i < array.length-1; i++) {
            if(array[i]>array[i+1]){
                return array[i+1];
            }
        }
        return array[0];
    }
}

7、斐波那契数列

问题描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n\leq 39n≤39
题目分析
斐波那契数列,最好不要用递归,浪费资源,用循环即可
代码实现

8、跳台阶

问题描述
描述一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。示例1输入:2复制返回值:2复制示例2输入:7复制返回值:21复制关联企业
题目分析
若只能跳1次和两次,其实就是个递归的问题,跳一个格子只有一次,跳第二个格子有两种方法,跳第三个格子的时候,就可以选择在第一个格子跳或者在第二个格子 是第一次跳的方法加第二次跳的方法和,即为斐波那契数列问题
代码实现

9、跳台阶拓展

问题描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
题目分析
可以使用回溯算法 0到1 为1次 0到2为 0-1-1 或者0-2 0-为 0-1-1-1 0-1-2 0-2-1,0-3 结果值为2的n次方减一
代码实现

10、矩形覆盖

问题描述:
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,从同一个方向看总共有多少种不同的方法?比如n=3时,2*3的矩形块有3种不同的覆盖方法(从同一个方向看):
问题解析:
当n=1 的时候值为1,n=2有两种解法,n=3的时候,有三种,n=4的时候,为 f2+f3 这是个斐波那契数列问题
代码实现:
package com.xu.fingeroffer.ten;public class 矩形覆盖10 {    public int rectCover(int target) {        if (target==0||target == 1 || target == 2 || target == 3) {            return target;        }        int f1 = 2;        int f2 = 3;        for (int i = 4; i <= target; i++) {            int num = f1;            f1 = f2;            f2 = f1+num;        }        return f2;    }}

11-20

11、二进制中1的个数

题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。示例1
问题分析
代码实现

12、数值的整数次方

题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数。
问题分析
遍历乘 若exponent<0 返回分之一即可
代码实现

13、调整数组顺序使奇数位于偶数前面

问题描述
 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 
问题解析
利用两个数组,遍历数组,将奇数放到1数组,偶数放到2数组中,最后将1,2数组连接即可
代码实现
package com.xu.fingeroffer.twenty;import java.util.LinkedList;public class 调整数组顺序使奇数位于偶数前面13 {    /**     * 队列     * @param array     * @return     */    public int[] reOrderArray (int[] array) {        LinkedList<Integer> l1 = new LinkedList<>();        LinkedList<Integer> l2 = new LinkedList<>();        for (int i = 0; i < array.length; i++) {            if(array[i]%2==1){                l1.add(array[i]);            }else {                l2.add(array[i]);            }        }        l1.addAll(l2);        int []result = new int [array.length];        for (int i = 0; i < array.length; i++) {            result[i]=l1.removeFirst();        }        return result;    }    /**     * 数组     */    public  int[] reOrderArray1 (int[] array) {        int []a = new int [array.length];        int []b = new int [array.length];        int j = 0;        int k = 0;        for (int i = 0; i < array.length; i++) {            if(array[i]%2==1){                a[j] = array[i];                j++;            }else {                b[k]=array[i];                k++;            }        }        for (int i = 0; i < k ;i++) {            a[j]=b[i];            j++;        }        return a;    }}

14、链表中倒数k个节点

题目描述
输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。如果该链表长度小于k,请返回一个长度为 0 的链表。
问题解析
设置一个标志位index,每次遍历一个节点,标志位+1,若k>index,则return null,对k进行遍历,最终输出结果应该是从栈中pull的节点 
代码实现
package com.xu.fingeroffer.twenty;

import com.xu.fingeroffer.node.ListNode;

import java.util.HashMap;
import java.util.Stack;

public class 链表中倒数最后k个结点14 {
    public static void main(String[] args) {
        ListNode node = new ListNode(1);
        node.next = new ListNode(2);
        node.next.next= new ListNode(3);
        ListNode listNode = FindKthToTail(node, 1);
    }
    public static ListNode FindKthToTail (ListNode pHead, int k) {
        int index = 0;
        // write code here
        if(k==0||pHead==null){
            return null;
        }
        // write code here
        Stack<ListNode> stack = new Stack<>();
        while (pHead!=null){
            stack.push(pHead);
            index++;
            pHead=pHead.next;
        }
        if(index<k)
            return null;
        for (int i = 1; i < k; i++) {
            stack.pop();

        }

        return stack.pop();
    }
}

15、反转链表

问题描述
 输入一个链表,反转链表后,输出新链表的表头。 
题目解析
将链表中的所有数据放到一个list集合中,利用头插法,最终得出反转后的链表
代码实现
 package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.ListNode;public class 反转链表15 {    public ListNode ReverseList(ListNode head) {        if(head==null||head.next==null){            return head;        }        ListNode first = null;        while (head!=null){            ListNode second = head;            head=head.next;            second.next=first;            first=second;        }        return first;    }}
16、合并两个排序的链表
问题描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则

题目解析

通过两个链表: 构建一个新链表 head(Interger.MIN) cur = head 然后对两个链表进行遍历比较 将较小的值放入cur中,若一个遍历完,将cur的下一个指针指向另外那个链表

代码实现

package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.listnode.ListNode;/** * 题目解析: * 创建一个新的链表 * 两个链表的值进行比较 小的插入到新链表的下一个节点 */public class 合并两个排序的链表 {    public ListNode Merge(ListNode list1, ListNode list2) {        //进行判断 如果两个都空 直接返回null        //如果两个其中一个不为空 则返回不为空的那个        if (list1 == null) {            if (list2 != null)                return list2;            else return null;        }        if (list2 == null) {            if (list1 != null) {                return list1;            }        }        //创建一个新的链表        ListNode head = new ListNode(Integer.MIN_VALUE);        ListNode cur = head;        //开始比较两个数组        while (list1 != null && list2 != null) {            //进行判断 将小的放入到cur链表中            if (list1.val < list2.val) {                cur.next = list1;                cur = cur.next;//指针后移                list1 = list1.next;            } else {                cur.next = list2;                cur = cur.next;                list2 = list2.next;            }        }        //如果一个链表遍历完了而另外一个没有遍历完        if (list1 != null) {            cur.next = list1;        } else {            cur.next = list2;        }        return head.next;    }}

17、树的子结构

问题描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
题目分析
对r1和r2进行值比较,若相等,执行子树比较方法,若不相等,则对r1的左右子树和r2的值比较
代码实现
package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.TreeNode;/** * 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) */public class 树的子结构17 {    public boolean HasSubtree(TreeNode root1, TreeNode root2) {        //遍历大树        if(root1==null||root2==null){            return false;        }        if(root1.val==root2.val){            if(judge(root1,root2)){                return true;            }        }        return HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);    }    private boolean judge(TreeNode root1, TreeNode root2) {        if(root2==null){            return true;        }        if(root1==null){            return false;        }        if(root1.val==root2.val){            return (judge(root1.left,root2.left)&&judge(root1.right,root2.right));        }        return false;    }}

18、二叉树镜像:

问题描述:

给一个二叉树 构建他的镜像二叉树

比如:    源二叉树             8           /  \          6   10         / \  / \        5  7 9 11        镜像二叉树            8           /  \          10   6         / \  / \        11 9 7  5

输入:

{8,6,10,5,7,9,11}

复制

返回值:

{8,10,6,11,9,7,5}

问题解析:

递归调用 执行替换即可:若有左右子节点则替换左右子节点,

并且对左右子节点实现镜像方法

代码实现

package com.xu.fingeroffer.twenty;import com.xu.fingeroffer.node.TreeNode;/** * 操作给定的二叉树,将其变为源二叉树的镜像 */public class 二叉树的镜像18 {    /**     * 问题解析:运用递归 如果二叉树的next不为null 则替换两个子节点     */    public TreeNode Mirror(TreeNode pRoot) {        // write code here        if (pRoot == null) {            return null;        } else            rootMirror(pRoot);        return pRoot;    }    public void rootMirror(TreeNode pRoot) {        //如果不为null 则替换二叉树的两个子节点        if (pRoot != null) {            TreeNode temp = pRoot.left;            pRoot.left = pRoot.right;            pRoot.right = temp;        }        //如果左子节点不为null 则递归向下遍历 执行替换        if (pRoot.left != null) {           rootMirror(pRoot.left);        }        //如果右子节点不为null 则递归向下遍历 执行替换        if (pRoot.right != null) {            rootMirror(pRoot.right);        }    }}

19、顺时针打印矩阵

问题描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]则依次打印出数字[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]示例1
题目分析
设四个参数分别代表上下左右,每次打印最外圈的,然后将矩阵缩小打印内圈即可
代码实现
package com.xu.fingeroffer.twenty;import java.util.ArrayList;import java.util.Arrays;public class 顺时针打印矩阵19 {    public static void main(String[] args) {        int[][] a = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};        ArrayList<Integer> list = printMatrix(a);        System.out.println(Arrays.toString(list.toArray()));    }    public static ArrayList<Integer> printMatrix(int[][] matrix) {        ArrayList<Integer> list = new ArrayList<>();        int up = 0;        int down = matrix.length - 1;        int left = 0;        int right = matrix[0].length - 1;        while (left < right && up < down) {            for (int i = left; i <= right; i++) {                list.add(matrix[up][i]);            }            for (int i = up + 1; i <= down; i++) {                list.add(matrix[i][right]);            }            for (int i = right - 1; i >= left; i--) {                list.add(matrix[down][i]);            }            for (int i = down - 1; i >= up + 1; i--) {                list.add(matrix[i][left]);            }            up++;            right--;            down--;            left++;        }        return list;    }}

20、包含min函数的栈

问题描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数,并且调用 min函数、push函数 及 pop函数 的时间复杂度都是 O(1)push(value):将value压入栈中pop():弹出栈顶元素top():获取栈顶元素min():获取栈中最小元素示例:输入:  ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]输出:  -1,2,1,-1解析:"PSH-1"表示将-1压入栈中,栈中元素为-1"PSH2"表示将2压入栈中,栈中元素为2,-1“MIN”表示获取此时栈中最小元素==>返回-1"TOP"表示获取栈顶元素==>返回2"POP"表示弹出栈顶元素,弹出2,栈中元素为-1"PSH-1"表示将1压入栈中,栈中元素为1,-1"TOP"表示获取栈顶元素==>返回1“MIN”表示获取此时栈中最小元素==>返回-1

题目分析

构建一个栈 ,用一个辅助栈进行存取最小值,若遇到 更小的,将小的放入辅助栈中,出栈的时候一并出
代码实现
package com.xu.fingeroffer.twenty;import java.util.Stack;/** * 构建一个栈 要求包含min函数 */public class 包含min函数的栈20 {    Stack<Integer> stack1 = new Stack();    Stack<Integer> stack2 = new Stack<Integer>();    public void push(int node) {        stack1.push(node);        if (stack2.isEmpty()) {            stack2.push(node);        }        if (stack2.peek() >= node) {            stack2.push(node);        }    }    public void pop() {        Integer pop = stack1.pop();        if (stack2.peek() == pop) {            stack2.pop();        }    }    public int top() {        return stack1.peek();    }    public int min() {        return stack2.peek();    }}

20-30

21:栈的压入弹出顺序

题目描述:
输入两个整数序列,第一个是压入顺序,第二个是弹出顺序,假设所有数字不等,判断第二个是否为第一个的弹出序列 
问题解析
     * 题目解析:     * 添加b标志位index     * 添加一个辅助栈,将a数组遍历压入栈中     * 判断栈顶元素是否等于b中数组下标的元素,若相等弹出栈顶元素,index加一,若栈为null或者不等,则进行下一次循环
代码实现
package com.xu.fingeroffer.thirty;import java.util.Stack;/** * 题目描述: * 输入两个整数序列,第一个是压入顺序,第二个是弹出顺序,假设所有数字不等,判断第二个是否为第一个的弹出序列 */public class 栈的压入弹出序列21 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5};        int[] b = {5, 4, 3, 2, 1};        System.out.println(Solution(a, b));    }    /**     * 题目解析:     * 添加b标志位index     * 添加一个辅助栈,将a数组遍历压入栈中     * 判断栈顶元素是否等于b中数组下标的元素,若相等弹出栈顶元素,index加一,若栈为null或者不等,则进行下一次循环     */    public static boolean Solution(int[] a, int[] b) {        if (a.length != b.length) {            return false;        }        int index = 0;        Stack<Integer> stack = new Stack<Integer>();        for (int i = 0; i < a.length; i++) {            //栈中添加a中的元素            stack.push(a[i]);            //判断栈中元素是否为null 或者b数组下标位置是否等于栈顶元素            while (!stack.isEmpty() && stack.peek() == b[index]) {                stack.pop();                index++;            }        }        return stack.isEmpty();    }}

22、从上到下打印二叉树

题目描述
描述从上往下打印出二叉树的每个节点,同层节点从左至右打印。示例1输入:{5,4,#,3,#,2,#,1}复制返回值:[5,4,3,2,1]
问题分析
创建一个list链表《TreeNode》 将root放进去一直遍历 当list 为空跳出循环将头节点取出来,若左子树不为null 将其放入,若右子树不为空 将其放入,把值放到list中
代码实现
package com.xu.fingeroffer.thirty;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;import java.util.LinkedList;public class 从上到下打印二叉树22 {    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {        ArrayList<Integer> list = new ArrayList<Integer>();        if (root == null) {            return list;        }        LinkedList<TreeNode> treelist = new LinkedList<>();        treelist.add(root);                while (!treelist.isEmpty()){            TreeNode node = treelist.removeFirst();            if(node.left!=null){                treelist.add(node.left);            }            if(node.right!=null){                treelist.add(node.right);            }            list.add(node.val);        }         return list;    }}

23:二叉树的后续遍历

题目描述
描述输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。(ps:我们约定空树不是二叉搜索树)示例1输入:[4,8,6,12,16,14,10]返回值:true
问题分析
这道题采用递归的思想,数组的最后一位则是树的根节点,然后在数组中找出第一个比他大的数字的下标,保证其后的所有数字都比数组最后一位数字大即可,否则 返回false
代码实现
package com.xu.fingeroffer.thirty;

import javax.sound.midi.Soundbank;

public class 二叉搜索树的后序遍历 {
    public static void main(String[] args) {
        int[] a = {4, 8, 6, 12, 16, 14, 10};
        System.out.println(VerifySquenceOfBST(a));
    }

    public static boolean VerifySquenceOfBST(int[] sequence) {
        if (sequence.length == 0 || sequence == null) {
            return false;
        }
        return check(sequence, 0, sequence.length - 1);
    }

    private static boolean check(int[] sequence, int start, int end) {
        if (start > end) {
            return true;
        }
        int tmp = sequence[end];
        int index = start;
        while (index < end && sequence[index] < sequence[end]) {
            index++;
        }
        for (int i = index; i < end; i++) {
            if (tmp >= sequence[i]) {
                return false;
            }
        }
        return check(sequence, start, index - 1) && check(sequence, index, end - 1);

    }
}

24、二叉树中和为某一值的路径

问题描述
描述
输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
题目分析
对二叉树的的节点向下遍历,递归调用target-root.val ,如果判断最终为二叉树的根节点,并且值等于target,则将其打印出来
代码实现
package com.xu.fingeroffer.thirty;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;public class 二叉树和为某一值的路径24 {    ArrayList<Integer> list = new ArrayList<>();    ArrayList<ArrayList<Integer>> lists = new ArrayList<>();    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {        if (root == null) {            return lists;        }        list.add(root.val);        target -= root.val;        if (target == 0 && root.left == null && root.right == null) {            lists.add(new ArrayList<Integer>(list));        }        FindPath(root.left, target);        FindPath(root.right, target);        list.remove(list.size() - 1);        return lists;    }}

25、复杂链表的复制

问题描述
描述输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。示例:输入:{1,2,3,4,5,3,5,#,2,#}输出:{1,2,3,4,5,3,5,#,2,#}解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5后半部分,3,5,#,2,#分别的表示为1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null如下图:
题目分析
代码实现

26、二叉搜索数与双向链表

题目描述
注意:1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继2.返回链表中的第一个节点的指针3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构4.你不用输出或者处理,示例中输出里面的英文,比如"From left to right are:"这样的,程序会根据你的返回值自动打印输出示例:输入: {10,6,14,4,8,12,16}输出:From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;解析:输入就是一棵二叉树,如上图,输出的时候会将这个双向链表从左到右输出,以及从右到左输出,确保答案的正确
问题分析
代码实现

27、字符串的排列

题目描述
描述输入一个字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。示例1输入:"ab"复制返回值:["ab","ba"]复制说明:返回["ba","ab"]也是正确的  
问题分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7fZ4mBc-1629185491750)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210817101444650.png)]

将字符串转换为数组,然后递归替换值
代码实现
package com.xu.fingeroffer.thirty;import java.util.ArrayList;import java.util.Collections;import java.util.List;public class 字符串排列27 {    public ArrayList<String> Permutation(String str) {        List<String> list = new ArrayList<>();        if (str.length() == 0) {            return (ArrayList) list;        }        fun(str.toCharArray(), list, 0);        Collections.sort(list);        return (ArrayList<String>) list;    }    private void fun(char[] ch, List<String> list, int i) {        if (i == ch.length - 1) {            //判断一下是否重复            if (!list.contains(new String(ch))) {                list.add(new String(ch));                return;            }        } else {            for (int j = i; j < ch.length; j++) {                swap(ch, i, j);                fun(ch, list, i + 1);                swap(ch, i, j);            }        }    }    private void swap(char[] str, int i, int j) {        if (i != j) {            char t = str[i];            str[i] = str[j];            str[j] = t;        }    }}

28:数组中超过一半的数字

题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000
问题分析
将数组遍历 保存在一个map中,判断map中的value  若其中某个值大于数组长度的一半 ,则返回
代码实现
 package com.xu.fingeroffer.thirty;import java.util.HashMap;/** * 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000 */public class 数组中超过一半的数字 {    public static int MoreThanHalfNum_Solution(int [] array) {        if(array.length==0){            return 0;        }        HashMap<Integer, Integer> map = new HashMap<>();        for (int i : array) {            if(map.containsKey(i)){                map.put(i,map.get(i)+1);            }else {                map.put(i,1);            }            if(map.get(i)>array.length/2){                return i;            }        }        return 0;    }}

29:最小的k个数

题目描述:
给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。- 0 <= k <= input.length <= 10000- 0 <= input[i] <= 10000
问题解析
将数组排序,然后将其加入到集合当中
代码实现
package com.xu.fingeroffer.thirty;import java.util.ArrayList;import java.util.Arrays;/** * 给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。 * 0 <= k <= input.length <= 10000 * 0 <= input[i] <= 10000 */public class 最小的k个数29 {    public static void main(String[] args) {        int[] a = {0, 1, 2, 1, 2};        System.out.println(Arrays.toString(GetLeastNumbers_Solution(a, 3).toArray()));        System.out.println(Arrays.toString(a));    }    public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {        ArrayList<Integer> list = new ArrayList<>();        for (int i = 0; i < input.length; i++) {            for (int j = input.length - 1; j > i; j--) {                if (input[i] > input[j]) {                    int temp;                    temp = input[i];                    input[i] = input[j];                    input[j] = temp;                }            }        }        for (int i = 0; i < k; i++) {            list.add(input[i]);        }        return list;    }}

30:连续子数组的最大和

题目描述
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).
问题解析
遍历数组,设定一个max值和变量值,变量值是从前往后遍历添加的 ,若数组为null 则max = 0,一次遍历,若变量值>max 则将变量值赋给max ,若变量小于0,则给该变量重新赋值
代码实现
package com.xu.fingeroffer.thirty;import java.util.Arrays;import java.util.LinkedList;import java.util.ListIterator;/** * 给一个数组 ,求该数组中子数组的最大值 */public class 连续子数组的最大和30 {    public static void main(String[] args) {        int[] a = {1,-2,3,10,-4,7,2,-5};        System.out.println(FindGreatestSumOfSubArray(a));    }    public static int FindGreatestSumOfSubArray(int[] array) {        if (array.length == 0) {            return 0;        }        LinkedList<Integer> list = new LinkedList<>();        int temp = 0;        int max = array[0];        for (int i = 0; i < array.length; i++) {            temp = temp + array[i];            list.add(array[i]);            if (max < temp) {                max = temp;            }            if (temp < 0) {                temp = 0;                list.clear();            }        }        System.out.println(Arrays.toString(list.toArray()));        return max;    }}

30-40

31:整数中1出现的字数

问题描述
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数例如,1~13中包含1的数字有1、10、11、12、13因此共出现6
题目分析
不会算法,乖乖遍历吧,int count =0 ;然后从1遍历到n ,对每次循环获得的值进行判断

代码实现

package com.xu.fingeroffer.forty;public class 整数中1出现的字数 {    public int NumberOf1Between1AndN_Solution(int n) {        int num = 0;        for (int i = 1; i <= n; i++) {            int tmp = i;            while (tmp != 0) {                if (tmp % 10 == 1) {                    num++;                }                tmp /= 10;            }        }        return num;    }}

32把数组排成最小的数

题目描述
描述输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。示例1输入:[3,32,321]复制返回值:"321323
问题分析
代码实现

33、丑数

问题描述
描述把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数
题目分析
代码实现

34:第一个只出现一次的字符

问题描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
题目分析
遍历字符串,将其放到map集合中,若有重复,则value+1,然后遍历该字符串,取第一个value值为1 的下标位置解法2:遍历s 将字符放入数组中,再次遍历找值为1的
代码实现
package com.xu.fingeroffer.forty;import java.util.HashMap;/** * 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数 */public class 第一个只出现一次的字符34 {    public static void main(String[] args) {        System.out.println(FirstNotRepeatingChar("google"));    }    public static int FirstNotRepeatingChar(String str) {        if (str == null || str.length() == 0) {            return -1;        }        int index = -1;        HashMap<Character, Integer> map = new HashMap<>();        for (int i = 0; i < str.length(); i++) {            if (map.get(str.charAt(i)) == null) {                map.put(str.charAt(i), 1);            } else {                map.put(str.charAt(i), map.get(str.charAt(i)) + 1);            }        }        for (int i = 0; i < str.length(); i++) {            if (map.get(str.charAt(i)) == 1) {                index = i;                return index;            }        }        return index;    }    public char firstUniqChar(String s) {        char[] c1 = new char[128];        for (int i = 0; i < s.length(); i++) {            c1[s.charAt(i)]++;        }        for (int i = 0; i < s.length(); i++) {            if(c1[s.charAt(i)]==1){                return s.charAt(i);            }        }        return ' ';    }}

35:数组中的逆序对

问题描述
 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
问题解析
通过归并排序的思想,在进行排序替换的时候,count增加的值应该是后一个mid-p1+1,即第一个数组后面的值
代码实现
package com.xu.fingeroffer.forty;public class 数组中的逆序对35 {    /**     * 采用分治算法,即归并排序进行排序,在替换我位置的时候使得count变化     *     * @param array     * @return     */    static int count = 0;    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5, 6, 7, 0};        System.out.println(InversePairs(a));    }    public static int InversePairs(int[] array) {        if (array.length != 0) {            divide(array, 0, array.length - 1);        }        return count;    }    private static void divide(int[] array, int start, int end) {        if (start >= end) {            return;        }        int mid = (end+start)/ 2;        divide(array, start, mid);//左边递归        divide(array, mid + 1, end);//右边递归        merge(array, start, mid, end);//合并两个子列    }    private static void merge(int[] array, int start, int mid, int end) {        //创建一个辅助数组,长度为end-star+1        int[] temp = new int[end - start + 1];        int p1 = start;        int p2 = mid + 1;        int k = 0;        //增加一个标志位,将数组数据加入辅助数组中,若一个数组全部添加完毕,则将另外一个数组的剩余数据添加到辅助数组中        while (p1 <= mid && p2 <= end) {            if (array[p1] < array[p2]) {                temp[k++] = array[p1++];            } else {                temp[k++] = array[p2++];                count = (count+(mid-p1+1))%1000000007;            }        }        while (p1 <= mid) {            temp[k++] = array[p1++];        }        while (p2 <= end) {            temp[k++] = array[p2++];        }        //将辅助数组的值返回到原数组中        for (int i = 0; i < temp.length; i++) {            array[i + start] = temp[i];        }    }}

36:两个链表的第一个公共结点

问题描述:
输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
题目解析:
给两个 无环的单链表,求两个链表集合中 存在共同的数 ,遍历两个链表即可得出
代码实现:
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.ListNode;/** * 输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的) */public class 两个链表的第一个公共节点36 {        //解法1    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {        if(pHead1==null||pHead2==null){            return null;        }        ListNode p1 = pHead1;        ListNode p2 = pHead2;        while (p1 != p2) {            p1 = p1.next;            p2 = p2.next;            if (p1 == p2) {                return p1;            }            if (p1 == null) {                p1 = pHead1;            }            if (p2 == null) {                p2 = pHead2;            }        }        return p1;    }    /**     * 解法2     * @param pHead1     * @param pHead2     * @return     */    public ListNode FindFirstCommonNode2(ListNode pHead1, ListNode pHead2) {        if(pHead1==null||pHead2==null){            return null;        }        ListNode p1 = pHead1;        ListNode p2 = pHead2;        while (p1!=p2){            p1=p1.next;            if(p1==p2){                return p1;            }            if(p1==null){                p1=pHead1;                p2=p2.next;            }        }        return p1;    }}

37、数字在排序数组中出现的次数

问题描述
描述统计一个数字在升序数组中出现的次数。
题目分析
二分查找对应的数字,然后找到他的上下界即可
代码实现
package com.xu.fingeroffer.forty;

public class 数字在升序数组中出现的次数37 {
    public static void main(String[] args) {
        int[] a = {1,3,3,3,3,4,5};
        System.out.println(GetNumberOfK1(a, 6));

    }

    /**
     * 直接遍历
     *
     * @param array
     * @param k
     * @return
     */
    public static int GetNumberOfK(int[] array, int k) {
        int num = 0;
        for (int i = 0; i < array.length; i++) {
            if (k == array[i]) {
                num++;
                if (i == array.length - 1) {
                    return num;
                }
                if (array[i + 1] != k) {
                    return num;
                }
            }
        }
        return num;

    }

    /**
     * 二分查找
     */
    public static int GetNumberOfK1(int[] array, int k) {
        int num = 0;
        int index = doubleSort(array, 0, array.length-1, k);
        if (index == -1) {
            return 0;
        }
        int m = index;
        int n = index;
        while (array[m] == k) {
            num += 1;
            m--;
            if (m < 0) {
                break;
            }
        }
        while (array[n] == k) {
            n++;
            num++;

            if (n == array.length) {
                break;
            }
        }

        return num - 1;
    }

    public static int doubleSort(int[] array, int low, int high, int k) {
        if(low>high){
            return -1;
        }
        int mid = low + ((high-low) / 2);
        if (array[mid] == k) {
            return mid;
        }
        if (array[mid] < k) {
            return doubleSort(array, mid + 1, high, k);
        } else if (array[mid] > k) {
            return doubleSort(array, low, mid - 1, k);
        } else {
            return -1;
        }
    }


}

38:二叉树的深度

题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
问题分析
递归比较 左右子节点的深度
代码实现
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.TreeNode;public class 二叉树的深度38 {    public int TreeDepth(TreeNode root) {        if(root==null){            return 0;        }        return  Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;    }}

39平衡二叉树

问题描述:
输入一棵二叉树,判断该二叉树是否是平衡二叉树。在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树**平衡二叉树**(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。注:我们约定空树是平衡二叉树。
题目分析:
计算二叉树左右子节点的最大深度,然后进行比较,若相差小于1则继续比较其子树,直到所有都为平衡二叉树的时候才为真
代码实现:
package com.xu.fingeroffer.forty;import com.xu.fingeroffer.node.TreeNode;public class 二叉树的深度38 {    public int TreeDepth(TreeNode root) {        if(root==null){            return 0;        }        return  Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;    }}

40、数组中出现一次两个的数字

问题描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
题目分析
利用hashMap的特性,查找出哪个在map中的值为1即可
代码实现
 	package com.xu.fingeroffer.forty;import java.util.Arrays;import java.util.HashMap;public class 数组中只出现一次的两个数字40 {    public static void main(String[] args) {        int []a ={1,4,1,6};        System.out.println(Arrays.toString(FindNumsAppearOnce(a)));    }    public static int[] FindNumsAppearOnce (int[] array) {        // write code here        int []a =new int[2];        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < array.length; i++) {            if(map.get(array[i])==null){                map.put(array[i],1);            }else {                map.put(array[i], map.get(array[i])+1);            }        }        int index = 0;        int j = 0;        while (index!=array.length){            if(map.get(array[index])==1){                a[j] = array[index];                j++;            }            index++;        }        return  a;    }}

40-50

41:何为s的连续整数

题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck! 
问题解析
运用滑动窗口的方法 设定一个i j 若满足 若小于sum 则j+1 若大于sum 则i+1 ,直到j为sum的一半+1即可 
代码实现
package com.xu.fingeroffer.fifty;import java.util.ArrayList;import java.util.Arrays;import java.util.Map;/** * 求和为s的连续整数 */public class 和为S的连续整数41 {    public static void main(String[] args) {        ArrayList<ArrayList<Integer>> arrayLists = FindContinuousSequence(9);        System.out.println(Arrays.toString(arrayLists.toArray()));    }    public static ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {        ArrayList<ArrayList<Integer>> list1 = new ArrayList<>();        int i = 1;        int j = 1;        if(sum==1){            return list1;        }        while (j <= (sum / 2 + 1)) {            int num = 0;            for (int i1 = i; i1 <= j; i1++) {                num += i1;            }            if (num < sum) {                j++;            } else if (num > sum) {                i++;            } else if (num == sum) {                ArrayList<Integer> list = new ArrayList<>();                for (int i1 = i; i1 <= j; i1++) {                    list.add(i1);                }                j++;                list1.add(list);            }        }        return list1;    }}

42:和为s的两个数字

题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,返回两个数的乘积最小的,如果无法找出这样的数字,返回一个空数组即可。
问题解析
题解1: 使用hashMap,将值作为key存在hashMap中,遍历数组,当k=i时查找有没有key=s-i的值 若有则说明存在题解2:使用双指针,一个从前往后,一个从后往前,若两者相加=s 则i,和j下标对应的值刚刚好,若a[i]+a[j]>s,则j--,否则i++,直到i=j
代码实现

双指针法

package com.xu.fingeroffer.fifty;import java.util.ArrayList;import java.util.Arrays;public class 和为s的两个数字42 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};        ArrayList<Integer> list = FindNumbersWithSum(a, 21);        System.out.println(Arrays.toString(list.toArray()));    }    /**     * 双指针解法     *     * @param array 传入数组     * @param sum   需要计算的值     * @return     */    public static ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {        int m = 0;        int n = array.length - 1;        ArrayList<Integer> list = new ArrayList<>(2);        if (sum == 0 || array.length == 0) {            return list;        }        while (m != n) {            if (array[m] + array[n] == sum) {                list.add(0, array[m]);                list.add(1, array[n]);                return list;            }            if (array[m] + array[n] < sum) {                m++;            }            if (array[m] + array[n] > sum) {                n--;            }        }        return list;    }}

HashMap法

 public static ArrayList<Integer> FindNumbersWithSum2(int[] array, int sum) {        HashMap map = new HashMap(16);        ArrayList<Integer> a = new ArrayList<Integer>();        for (int i = 0; i <= array.length - 1; i++) {            if (map.get(sum - array[i]) != null) {                while (!a.isEmpty()) {                    a.remove(0);                }                a.add(sum - array[i]);                a.add(array[i]);            }            map.put(array[i], i);        }        return a;    }

43:左旋转字符串

问题描述
 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出(保证 K 小于等于 S 的长度)。例如,字符序列S=”abcXYZdef”,要求输出循环左移 3 位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它! 
问题分析
遍历,然后用stringbuffer拼接即可
代码实现
package com.xu.fingeroffer.fifty;public class 左旋转字符串43 {    public static void main(String[] args) {        String str = "abcXYZdef";        System.out.println(LeftRotateString(str, 3));    }    public static String LeftRotateString(String str,int n) {        if(n>str.length()){            return str;        }        return str.substring(n)+str.substring(0,n);    }}

44、翻转单词序列

题目描述
描述牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?示例1输入:"nowcoder. a am I"复制返回值:"I am a nowcoder."复制
问题解析
判断,每次在空格的时候,将前面的字符穿 插到总字符串的前面
代码实现
package com.xu.fingeroffer.forty;import java.util.Arrays;import java.util.HashMap;public class 数组中只出现一次的两个数字40 {    public static void main(String[] args) {        int []a ={1,4,1,6};        System.out.println(Arrays.toString(FindNumsAppearOnce(a)));    }    public static int[] FindNumsAppearOnce (int[] array) {        // write code here        int []a =new int[2];        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < array.length; i++) {            if(map.get(array[i])==null){                map.put(array[i],1);            }else {                map.put(array[i], map.get(array[i])+1);            }        }        int index = 0;        int j = 0;        while (index!=array.length){            if(map.get(array[index])==1){                a[j] = array[index];                j++;            }            index++;        }        return  a;    }}

45:扑克牌顺子

问题描述:
现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。有如下规则:\1. A为1,J为11,Q为12,K为13,A不能视为14\2. 大、小王为 0,0可以看作任意牌\3. 如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。例如:给出数据[6,0,2,0,4]中间的两个0一个看作3,一个看作5 。即:[6,3,2,5,4]这样这五张牌在[2,6]区间连续,输出true数据保证每组5个数字,每组最多含有4个零,数组的数取值为 [0, 13]
问题分析:
给数组进行排序,初始赋值num = 0;然后进行遍历,若为0,则跳出本次循环进入下一次循环,若array[i]=array[i+1] 返回false 否则num= num+array[i+1]-array[i];最后 对num的值进行判断,只要num的值小于4 则返回为真 否则 为假
代码实现
package com.xu.fingeroffer.fifry;import java.util.Arrays;public class 扑克牌顺子 {    public boolean IsContinuous(int[] numbers) {        //将传入的数组进行排序        Arrays.sort(numbers);        int num = 0;        //遍历数组        for (int i = 0; i < numbers.length-1; i++) {            //如果是0 则跳出循环            if (numbers[i] == 0) {                continue;            }            //如果有重复的数,返回false            else if (numbers[i] == numbers[i + 1]) {                return false;            } else {                num = num + (numbers[i + 1] - numbers[i]);            }        }        if (num <= 4) {            return true;        } else        {            return false;        }            }}

46、孩子们的游戏(约瑟夫环问题)

题目描述
描述每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)如果没有小朋友,请返回-1示例1输入:5,3复制返回值:
问题解析
利用循环链表的特性,每执行m次都会删除一个节点,若到队尾,则放到队头即可,知道队列大小为1
代码实现
package com.xu.fingeroffer.fifty;import java.util.Arrays;import java.util.LinkedList;/** * 类似约瑟夫环 * 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) * <p> * 如果没有小朋友,请返回-1 */public class 孩子们中的游戏约瑟夫环46 {    public static void main(String[] args) {        System.out.println(LastRemaining_Solution(5, 3));    }    public static int LastRemaining_Solution(int n, int m) {        if (n<=1||m==0) {            return -1;        }        LinkedList<Integer> list = new LinkedList<>();        for (int i = 0; i < n; i++) {            list.add(i);        }        int k  = 0;        while (list.size()!=1){            k = k + m;            // 第m人的索引位置            k = k % (list.size()) - 1;            // 判断是否到队尾            if (k < 0) {                System.out.println(list.get(list.size() - 1));                list.remove(list.size() - 1);                k = 0;            } else {                System.out.println(list.get(k));                list.remove(k);            }        }        return list.remove();    }}

48:不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
问题解析
使用位运算,异或 左移 
代码实现
package com.xu.fingeroffer.fifry;public class 位运算加48 {    public int Add(int num1,int num2) {        while (num2!=0){            int temp = num1^num2;            num2 = (num1&num2)<<1;            num1 = temp;        }        return num1;    }}

49、把字符串转换为整数

题目描述
描述将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0输入描述:输入一个字符串,包括数字字母符号,可以为空返回值描述:如果是合法的数值表达则返回该数字,否则返回0示例1输入:"+2147483647"复制返回值:2147483647复制示例2输入:"1a33"复制返回值:0
问题解析
从1号位置往后遍历,sum=sum*10+str.CharAt(i)-'0'若不是数字返回零,最后判断,若第一位为正,直接返回,为负,返回负数,为数字,则给sum*10的length-1次方乘以第一个数字+sum
代码实现
package com.xu.fingeroffer.fifty;public class 把字符串转换为数字49 {    public static void main(String[] args) {        System.out.println(StrToInt("1110"));    }    public static int StrToInt(String str) {        if(str==null||str.length()==0){            return 0;        }        int sum = 0;        int i = 0;        for (int j = 1; j < str.length(); j++) {            if((int)str.charAt(j)-'0'<0||(int)str.charAt(j)-'9'>0){                return 0;            }            sum=sum*10+(str.charAt(j)-'0');        }        if(str.charAt(0)=='+'){            return sum;        }else if(str.charAt(0)=='-'){            return -sum;        }else if(str.charAt(0)-'0'>=0&&(int)str.charAt(0)-'9'<=9){            int pow =(int) Math.pow(10, str.length()-1);            sum = sum + (str.charAt(0)-'0')*pow;        }                return sum;    }}

50-60

50:数组中重复的数字

题目描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-14UH4V0T-1629185491753)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210813172645364.png)]

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
问题解析
### 解法1:如题可知 数组中的数字大小不会超过数组的长度,所以可以用数组来实现,创建一个空数组与原数组长度相同,遍历元数组,使得新数组中原数组中值对应的下标位置的值+1;当对应下表值为2的时候 ,返回该值 若没有 返回-1### ==最优解法==: 对数组进行遍历,当num[i]!=i的时候,将i的值和num[i]的值进行替换,找到一个即可!!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wc2lGVQS-1629185491754)(%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.assets/image-20210813173557770.png)]

代码实现
package com.xu.fingeroffer.fifty;import java.util.HashMap;public class 数组中重复的数字50 {    public static void main(String[] args) {        int []a = {2, 3, 1, 0, 2, 5};        System.out.println(duplicate3(a));    }    public static int duplicate (int[] numbers) {        if(numbers==null||numbers.length==0){            return -1;        }        // write code here        HashMap<Integer, Integer> map = new HashMap<>();        for (int i = 0; i < numbers.length; i++) {            if(map.get(numbers[i])==null){                map.put(numbers[i],1);            }else {                return numbers[i];            }        }        return -1;    }    /**     * 解法二     * @param numbers     * @return     */        public static int duplicate2 (int[] numbers) {            int []test = new int [numbers.length];            for (int i = 0; i < numbers.length; i++) {                test[numbers[i]]++;                if(test[numbers[i]]==2){                    return numbers[i];                }            }            return -1;        }    /**     * 解法3     * @param numbers     * @return     */    public static int duplicate3 (int[] numbers) {        for (int i = 0; i < numbers.length; i++) {            while (numbers[i]!=i){                if(numbers[i] == numbers[numbers[i]]){                    return numbers[i];                }                swap(numbers,i,numbers[i]);            };        }        return -1;    }    private static void swap(int[] numbers, int i, int j) {        int tmp = numbers[i];        numbers[i] = numbers[j];        numbers[j] = tmp;    }}

51、构建乘积数组

问题描述
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在
题目分析
进行双重循环,若遇到下标等于自身下标,则跳过
代码实现
package com.xu.fingeroffer.six;

import java.util.Arrays;

public class 构建乘积数组51 {
    public static void main(String[] args) {
        int []a = {1,2,3,4,5};
        System.out.println(Arrays.toString(multiply1(a)));
    }
    public static int[] multiply(int[] A) {
        int []a = new  int [A.length];
        for (int i = 0; i < a.length; i++) {
            int sum=1;
            for (int i1 = 0; i1 < A.length; i1++) {
                if(i1!=i){
                    sum=sum*A[i1];
                }
            }
            a[i]=sum;
        }
        return a;

    }
    public static int[] multiply1(int[] A) {
        int [] array = new int [A.length];
        int sum = 1;
        for (int i = 0; i < A.length; i++) {
            sum*=A[i];
        }
        for (int i = 0; i < array.length; i++) {
            array[i] = sum/A[i];
        }

        return array;
    }
}

52、正则表达式匹配

问题描述
描述
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
题目分析
代码实现

53、表示数值的字符串

问题描述
描述请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。科学计数法的数字(按顺序)可以分成以下几个部分:1.若干空格2.一个整数或者小数3.(可选)一个 'e' 或 'E' ,后面跟着一个整数(可正可负)4.若干空格小数(按顺序)可以分成以下几个部分:1.若干空格2.(可选)一个符号字符('+' 或 '-')3. 可能是以下描述格式之一:3.1 至少一位数字,后面跟着一个点 '.'3.2 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字3.3 一个点 '.' ,后面跟着至少一位数字4.若干空格整数(按顺序)可以分成以下几个部分:1.若干空格2.(可选)一个符号字符('+' 或 '-')3. 至少一位数字4.若干空格例如,字符串["+100","5e2","-123","3.1416","-1E-16"]都表示数值。但是["12e","1a3.14","1.2.3","+-5","12e+4.3"]都不是数值。
题目分析

代码实现

54:字符流中第一个不重复的数

题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。输入:"google"复制返回值:"ggg#ll"
问题解析

创建一个链表,插入字符的时候,链表中没有这个字符,则给链表最后一个节点插入这个字符。如果有,则将删除这个字符

代码实现
package com.xu.fingeroffer.six;import java.util.LinkedList;public class 字符流中的第一个不重复的数54 {    LinkedList<Character> list = new LinkedList<Character>();    int [] time = new int [128];    public void Insert(char ch)    {        if(time[ch]==0){            list.addLast(ch);        }else {            list.remove(new Character(ch));        }        time[ch]++;    }    public char FirstAppearingOnce()    {        if(list.isEmpty()){            return '#';        }else {            return list.getFirst();        }    }}

55、链表中的入口节点

问题描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

题目分析

将每次链表中的值放入到map中,一直遍历链表,若有存在的节点则返回

代码实现
package com.xu.fingeroffer.six;import java.util.Arrays;public class 构建乘积数组51 {    public static void main(String[] args) {        int []a = {1,2,3,4,5};        System.out.println(Arrays.toString(multiply1(a)));    }    public static int[] multiply(int[] A) {        int []a = new  int [A.length];        for (int i = 0; i < a.length; i++) {            int sum=1;            for (int i1 = 0; i1 < A.length; i1++) {                if(i1!=i){                    sum=sum*A[i1];                }            }            a[i]=sum;        }        return a;    }    public static int[] multiply1(int[] A) {        int [] array = new int [A.length];        int sum = 1;        for (int i = 0; i < A.length; i++) {            sum*=A[i];        }        for (int i = 0; i < array.length; i++) {            array[i] = sum/A[i];        }        return array;    }}

56、删除链表中重复的节点

问题描述
描述在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
题目分析

代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.ListNode;import java.util.Arrays;import java.util.LinkedList;public class 删除链表中重复的节点56 {    public static void main(String[] args) {        int[] a = {1, 2, 3, 3, 4, 4, 5};        ListNode node = new ListNode(a);        deleteDuplication(node);    }    private static ListNode deleteDuplication(ListNode pHead) {        if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回            return pHead;        }        if (pHead.val == pHead.next.val) { // 当前结点是重复结点            ListNode pNode = pHead.next;            while (pNode != null && pNode.val == pHead.val) {                // 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点                pNode = pNode.next;            }            return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归        } else { // 当前结点不是重复结点            pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归            return pHead;        }    }}

57、二叉树的下一个结点

问题描述
描述给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针。下图为一棵有9个节点的二叉树。树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的用虚线表示
题目分析
给一个节点,先判断他有没有右子节点,如果有,则找该节点的右子树的最左节点如果没有右子树,则判断是不是父节点的左子节点,如果是,则返回父节点如果没有右子树,且是父节点的右节点,则回溯到父节点,从父节点开始查
代码实现
public TreeLinkNode GetNext(TreeLinkNode pNode) {        //如果当前节点有右子树,则查找右子树的最左节点        if (pNode.right != null) {            pNode = pNode.right;            while (pNode.left != null) {                pNode = pNode.left;            }            return pNode;        }        //若当前节点没有左右孩子,而且是父节点的左子树,则返回父节点        //防止空指针异常(必须先判断父节点不为null)        if (pNode.right == null && pNode.next != null && pNode == pNode.next.left) {            return pNode.next;        }        //若当前节点没有右孩子,而且是父节点的右子树,则沿着父节点回溯        //防止空指针异常(必须先判断父节点不为null)        if (pNode.right == null && pNode.next != null && pNode == pNode.next.right) {            pNode = pNode.next;            while (pNode.next != null && pNode != pNode.next.left) {                pNode = pNode.next;            }            return pNode.next;        }        return null;    }	    

58、对称的二叉树

问题描述
描述请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
题目分析
对根节点的的左右子树的子节点进行判断,然后判断其的左右是否对称,即是他的右子树的左节点,左子树的右节点 即判断左子树的右节点和右子树的左节点是否一致
代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.TreeNode;public class 对称的二叉树58 {    boolean isSymmetrical(TreeNode pRoot) {        if (pRoot == null) {            return true;        }        return isSymmetrical(pRoot.left, pRoot.right);    }    private boolean isSymmetrical(TreeNode left, TreeNode right) {        if (left == null && right == null) {            return true;        }        if (left == null || right == null) {            return false;        }        return left.val == right.val                && isSymmetrical(left.right, right.left)                && isSymmetrical(left.left, right.right);    }}

59、按之字顺序打印二叉树

问题描述
描述给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)例如:给定的二叉树是{1,2,3,#,#,4,5}该二叉树之字形层序遍历的结果是[[1],[3,2],[4,5]]示例1
题目分析
用一个辅助数链表,将根节点存入栈中,取出根节点的值,将其数值放入集合,将其左右子树放入到栈中,判断栈空即可返回
代码实现
package com.xu.fingeroffer.six;import com.xu.fingeroffer.node.TreeNode;import java.util.ArrayList;        import java.util.Stack;public class 按之字顺序打印二叉树59 {    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {        ArrayList<ArrayList<Integer>> printResult = new ArrayList<>();        if (pRoot == null) {            return printResult;        }        //碰到逆序的时候要想到栈!!!        Stack<TreeNode> floorNode = new Stack<>();        floorNode.push(pRoot);        Print(1, printResult,floorNode);        return printResult;    }    private void Print(int floor, ArrayList<ArrayList<Integer>> printResult, Stack<TreeNode> floorNode) {        if (floorNode.size() == 0) {            return;        }        //将上一层存进结果中        ArrayList<Integer> temp = new ArrayList<>();        for (TreeNode treeNode : floorNode) {            temp.add(treeNode.val);        }        printResult.add(temp);        Stack<TreeNode> nextFloorNode = new Stack<>();        while (!floorNode.empty()){            TreeNode node = floorNode.pop();            if (floor % 2 == 0) {                if (node.left!=null) {                    nextFloorNode.push(node.left);                }                if (node.right!=null) {                    nextFloorNode.push(node.right);                }            } else {                if (node.right!=null) {                    nextFloorNode.push(node.right);                }                if (node.left!=null) {                    nextFloorNode.push(node.left);                }            }        }        Print(floor + 1, printResult, nextFloorNode);    }}

60-66

60、把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。例如:给定的二叉树是{1,2,3,#,#,4,5}该二叉树多行打印层序遍历的结果是[[1],[2,3],[4,5]]
示例1输入:{1,2,3,#,#,4,5}复制返回值:[[1],[2,3],[4,5]]
问题解析
创建一个放Integer集合的的集合,创建一个放树的队列,先将根节点放入队列中,当队列的大小不为空的时候,递归,将节点值放入集合中,判断节点是否有子节点,若有,将子节点放入队列中
代码实现
package com.xu.fingeroffer.seventy;

import com.xu.fingeroffer.node.TreeNode;

import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import java.util.ArrayList;
import java.util.LinkedList;

public class 把二叉树打印成多行60 {
    ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        if (pRoot == null) {
            return null;
        }
        LinkedList<TreeNode> list = new LinkedList<>();
        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
        list.add(pRoot);
        while (!list.isEmpty()){
            ArrayList<Integer> integers = new ArrayList<>();
            int size = list.size();
            while (size !=0){
                TreeNode remove = list.remove(0);
                size--;
                integers.add(remove.val);
                if(remove.left!=null){
                    list.add(remove.left);
                }
                if(remove.right!=null){
                    list.add(remove.right);
                }

            }
            lists.add(integers);
        }
        return lists;
    }

}

61、序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,可以根据层序遍历并特定标志空结点的方案序列化,也可以根据满二叉树结点位置的标号规律来序列化,还可以根据先序遍历和中序遍历的结果来序列化。

假如一棵树共有 2 个结点, 其根结点为 1 ,根结点右子结点为 2 ,没有其他结点。按照上面第一种说法可以序列化为“1,#,2,#,#”,按照上面第二种说法可以序列化为“{0:1,2:2}”,按照上面第三种说法可以序列化为“1,2;2,1”,这三种序列化的结果都包含足以构建一棵与原二叉树完全相同的二叉树的信息。

不对序列化之后的字符串进行约束,所以欢迎各种奇思妙想。
问题分析
将二叉树层序遍历 即可转为一个字符串,根据字符串,提取数字 重组为二叉树
代码实现
 String Serialize(TreeNode root) {
        List<String> list = new ArrayList<>();
        if(root == null) {
            return "";
        }
        Deque<TreeNode> que = new LinkedList<>();
        TreeNode p = root;
        que.offer(p);
        while(!que.isEmpty()) {
            int len = que.size();
            for(int i = 0; i < len; i++) {
                p = que.poll();
                if(p == null) {
                    list.add("#");
                } else {
                    list.add(String.valueOf(p.val));
                    que.offer(p.left);
                    que.offer(p.right);
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        for(int i = 0; i < list.size(); i++) {
            sb.append(list.get(i));
            if(i < list.size()-1) {
                sb.append(",");
            }
        }
        sb.append("}");
        return sb.toString();
    }

    TreeNode Deserialize(String str) {
        if(str == null || str.length() == 0) {
            return null;
        }
        if(str.equals("{}")) {
            return null;
        }
        String subStr = str.substring(1, str.length()-1);
        String[] nodes = subStr.split(",");
        TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
        TreeNode p = root;
        Deque<TreeNode> que = new LinkedList<>();
        que.offer(p);
        int idx = 1;
        while(!que.isEmpty()) {
            int len = que.size();
            for(int i = 0; i < len; i++) {
                p = que.poll();
                if("#".equals(nodes[idx])) {
                    p.left = null;
                } else {
                    p.left = new TreeNode(Integer.parseInt(nodes[idx]));
                    que.offer(p.left);
                }
                idx++;
                if("#".equals(nodes[idx])) {
                    p.right = null;
                } else {
                    p.right = new TreeNode(Integer.parseInt(nodes[idx]));
                    que.offer(p.right);
                }
                idx++;
            }
        }
        return root;
    }

62、二叉搜索树的第k个节点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。
问题分析
第k小即中序遍历的第k个数值
代码实现
package com.xu.fingeroffer.seventy;

import com.xu.fingeroffer.node.TreeNode;

import java.util.ArrayList;

public class 二叉搜索时的第k个节点61 {
    int i = 0;
    TreeNode KthNode(TreeNode pRoot, int k) {
        TreeNode node = getK(pRoot, k);
        return node;
    }

    TreeNode getK(TreeNode pRoot, int k) {
        TreeNode node = null;
        if (pRoot.left != null) {
            node = getK(pRoot.left, k);
        }
        i++;
        if (i == k) {
            node = pRoot;
        }
        if (pRoot == null && pRoot.right == null) {
            node = getK(pRoot.right, k);
        }
        return node;
    }
}

63、数据流中的中位数

问题描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

输入:
[5,2,3,4,1,6,7,0,8]

返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "

说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...  
题目分析
利用arraylist的特性,在插入的时候插入排序一下,然后在求中位数的时候,如果list的size为奇数则返回最中间的那个,如果为偶数,则中间两个数取平均值
代码实现
package com.xu.fingeroffer.seventy;

import java.util.ArrayList;
import java.util.LinkedList;

public class 数据流中的中位数63 {
    ArrayList<Integer> list = new ArrayList<>();

    public void Insert(Integer num) {
        if (list.size() == 0) {
            list.add(num);
            return;
        }
        int start = 0;
        int end = list.size();
        int mid = 0;
        while (start < end) {
            mid = (end + start) / 2;
            Integer integer = list.get(mid);
            if (num > integer) {
                start = mid + 1;
            } else if (num < integer) {
                end = mid - 1;
            } else {
                list.add(mid, num);
                return;
            }
        }
        list.add(mid, num);
    }

    public Double GetMedian() {
        if ((list.size() & 1) == 1) {
            return Double.valueOf(list.get(list.size()/2));
        }else {
            return(Double.valueOf(list.get(list.size()/2))+Double.valueOf(list.get(list.size()/2-1)));
        }

    }
}
代码实现

64、滑动窗口最大值

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
问题分析
利用双指针的思想,窗口大小固定,只需要在遍历数组的时候求出该窗口的最大值即可
代码实现
package com.xu.fingeroffer.seventy;

import sun.nio.cs.SingleByte;

import java.util.ArrayList;
import java.util.Arrays;

public class 滑动窗口的最大值64 {
    public static void main(String[] args) {
        int []a = {2,3,4,2,6,2,5,1};
        ArrayList<Integer> windows = maxInWindows(a, 3);
        System.out.println(Arrays.toString(windows.toArray()));
    }
    public static ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> list = new ArrayList<>();
        if(size==0){
            return list;
        }
        if(size==1){
            for (int i = 0; i < num.length; i++) {
                list.add(num[i]);
            }
            return list;
        }
        int i = 0 ;
        int j = i+size-1;
        while (j<num.length){
            for (int i1 = 0; i1 < num.length-size+1; i1++) {
                int max = num[i] ;
                for (int k = i+1; k <= j; k++) {
                    if(num[k]>max) {
                        max=num[k];
                    }
                }
                list.add(max);
                j++;
                i++;
            }
        }

        return list;
    }
}

65、矩阵中的路径

问题描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
题目分析
遍历数组,找到数组的头,根据数组的头,找后面的,若都能找到返回true否则返回false
代码实现
package com.xu.fingeroffer.seventy;

import sun.misc.VM;

public class 矩阵中的路径 {
    public static void main(String[] args) {
        char[][] a = {{'a', 'b', 'c', 'e'}, {'s', 'f', 'c', 's'}, {'a', 'd', 'e', 'e'}};
        System.out.println(hasPath(a, "abcced"));

    }

    public static boolean hasPath(char[][] matrix, String word) {
        boolean flag = false;
        // 遍历找初始位置
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (word.charAt(0) == matrix[i][j]) {
                    if( dfs(matrix, word, i, j, 1)){
                        flag = true;
                        return flag;
                    };
                }
            }
        }
        return flag;
    }

    private static boolean dfs(char[][] matrix, String word, int i, int j, int index) {
        if (index > word.length() - 1) {
            return true;
        }
        char c = word.charAt(index);
        if (i + 1 < matrix.length&&c == matrix[i + 1][j]  ) {
            return dfs(matrix, word, i + 1, j, index + 1);
        } else if (i > 0 && c == matrix[i - 1][j]) {
            return dfs(matrix, word, i - 1, j, index + 1);
        } else if (j + 1 < matrix[0].length && c == matrix[i][j + 1]) {
            return dfs(matrix, word, i, j + 1, index + 1);
        } else if (j>0&&c == matrix[i][j - 1] ) {
            return dfs(matrix, word, i, j - 1, index + 1);
        } else {
            return false;
        }

    }
}

66、机器人的运动范围

题目描述
地上有一个rows行和cols列的方格。坐标从 [0,0][rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
问题解析
由题意可得,机器人运动的步数应该为threshold,每次运动都是上下左右运动,不能进入行坐标+列坐标之和大于threshold的数,进行dfs算法,将row和cols的列相加和threshold进行比较,如果小于threshold,则count++,否则,直接返回,然后对其的上下左右进行遍历
代码实现
package com.xu.fingeroffer.seventy;public class 机器人的运动范围 {    private int[][] range = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};    private boolean[][] flag;    private int count;    public int movingCount(int threshold, int rows, int cols) {        flag = new boolean[rows][cols];        count = 0;        //从0,0开始计数        dfs(0, 0, rows, cols, threshold);        return count;    }    /**     * dfs算法     *     * @param i         当前纵坐标     * @param j         当前横坐标     * @param rows      数组长度     * @param cols      数组宽度     * @param threshold 临界值     */    private void dfs(int i, int j, int rows, int cols, int threshold) {        //判断,如果不符合临界值条件,直接返回 不在数组中,或者被遍历过         if (i < 0 || i >= rows || j < 0 || j > cols || flag[i][j]) {            return;        }        flag[i][j] = true;        //根据i j 坐标 判断他是否符合条件        int sum = 0, m = i, n = j;                while (m > 0) {            sum += (m % 10);            m /= 10;        }        while (n > 0) {            sum += (n % 10);            n /= 10;        }        if (sum <= threshold) {            count++;        } else {            return;        }        for (int i1 = 0; i1 < range.length; i1++) {            dfs(i + range[i1][0], j + range[i1][1], rows, cols, threshold);        }    }}

67、剪绳子问题

题目描述
描述地上有一个rows行和cols列的方格。坐标从 [0,0] 到 [rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
问题解析
将绳子分为n个3,每三个见一次,如果到最后,绳子长度为4,则最后为切为22若
代码实现
package com.xu.fingeroffer.seventy;

public class 剪绳子67 {
    public static void main(String[] args) {
        System.out.println(cutRope(8));
    }

    public static int cutRope(int target) {
        if (target == 1 || target == 2 || target == 3) {
            return target;
        }

        int sum = 1;
        while (target > 0) {
            if (target <= 4) {
                sum = target * sum;
                target = 0;
            } else {
                target = target - 3;
                sum = sum * 3;
            }

        }
        return sum;
    }
}

力扣

双指针问题

977、有序数组的平方

题目描述
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

问题解析
利用双指针,建立一个辅助数组,对数组的头与数组尾部进行比较,然后将较大的值插入到新的数组最末端
代码实现
public static int[] sortedSquares(int[] nums) {
    int left = 0;
    int right = nums.length-1;
    int[] assist = new int[nums.length];
    int index = nums.length - 1;
    while (left <= right) {
        if (nums[left] * nums[left] > nums[right] * nums[right]) {
            assist[index--] = nums[left] * nums[left];
            left++;
        } else {
            assist[index--] = nums[right] * nums[right];
            right--;
        }
    }


    return assist;
}

189、旋转数组

题目描述
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

 输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
问题解析

利用循环队列,将数组的值加入到新的数组中,返回新的数组

代码实现
    public static void rotate(int[] nums, int k) {
        if(nums.length==1){
            return;
        }
            k=k%nums.length;

        int[] array = new int[nums.length];
        for (int i = 0; i < array.length; i++) {
            if(k==0){
                k=nums.length;
            }
            array[i] = nums[nums.length-k];
            k--;
        }
        for (int i = 0; i < array.length; i++) {
            nums[i] = array[i];
        }

    }

283、移动零

题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。示例:输入: [0,1,0,3,12]输出: [1,3,12,0,0]说明:必须在原数组上操作,不能拷贝额外的数组。尽量减少操作次数。
问题分析
利用双指针的思想,若右指针不为0,则指针右移,若右指针为0,则与左指针交换位置,左指针右移,直到右指针移出即可
代码实现
package com.xu.lecode.算法.双指针;

import java.util.Arrays;

public class 移动零283 {
    public static void main(String[] args) {
        int[] a = {0, 1, 0, 3, 12};
        moveZeroes(a);
        System.out.println(Arrays.toString(a));
    }

    public static void moveZeroes(int[] nums) {
        int left = 0;
        int right = 0;
        while (right < nums.length) {
            if (nums[right] != 0) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
                left++;
            }
            right++;
        }
    }
}

167、两数之和

题目描述
给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
问题分析
利用双指针,一个从前往后,一个从后往前,使两数之和和target进行比较,若大于则右指针左移,若小于,左指针右移,若等于,则返回
代码实现
package com.xu.lecode.算法.双指针;import java.util.Arrays;public class 两数之和167 {    public static void main(String[] args) {        int []a ={2,7,11,15};        System.out.println(Arrays.toString(twoSum(a, 9)));    }    public static int[] twoSum(int[] numbers, int target) {        int[] a = new int[2];        int m = 0;        int n = numbers.length - 1;        while (m < n) {            if(numbers[m]+numbers[n]>target){                n--;            }else if(numbers[m]+numbers[n]<target){                m++;            }else {                a[0] = m+1;                a[1] = n+1;                return a;            }        }        return a;    }}

344、反转字符串单词

题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

 

示例 1:

输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
问题解析
利用双指针的思想,进行替换即可
代码实现
package com.xu.lecode.数据结构.数组;

import java.util.Arrays;

public class 反转字符串344 {
    public static void main(String[] args) {
        char[] a = {'h', 'e', 'l', 'l', 'o'};
        reverseString(a);
        System.out.println(Arrays.toString(a));
    }

    public static void reverseString(char[] s) {
        int m = 0;
        int n = s.length - 1;
        while (m < n) {
            char tmp = s[m];
            s[m] = s[n];
            s[n] = tmp;
            m++;
            n--;
        }
    }
}

557、反转字符串中的单词

题目描述
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 示例:输入:"Let's take LeetCode contest"输出:"s'teL ekat edoCteeL tsetnoc"
问题解析
将字符串分为字符数组,遍历,遇到空格就从start到i-1替换,如果遍历到数组尾部,则从start到i进行替换
代码实现
package com.xu.lecode.数据结构.数组;

import java.util.Arrays;

public class 反转字符串344 {
    public static void main(String[] args) {
        char[] a = {'h', 'e', 'l', 'l', 'o'};
        reverseString(a);
        System.out.println(Arrays.toString(a));
    }

    public static void reverseString(char[] s) {
        int m = 0;
        int n = s.length - 1;
        while (m < n) {
            char tmp = s[m];
            s[m] = s[n];
            s[n] = tmp;
            m++;
            n--;
        }
    }
}

876、链表的中间节点

题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

 

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
问题解析
求中间值的问题,可以使用快慢指针来解决这一问题,当快指针到末尾的时候,满指针所在的节点,即为中间节点(注意快指针空指针异常问题)
代码实现
package com.xu.lecode.算法.双指针;import com.xu.fingeroffer.node.ListNode;public class 链表的中间节点876 {    public ListNode middleNode(ListNode head) {        if (head == null || head.next == null) {            return head;        }        ListNode node1 = head;        ListNode node2 = head;        while (node1 != null) {            if (node1.next != null) {                node1 = node1.next.next;                node2 = node2.next;            } else {                node1 = node1.next;            }        }        return node2;    }}

19、链表的倒数第N个结点

问题描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。进阶:你能尝试使用一趟扫描实现吗?
题目分析
使用快慢指针,解决这个问题,链表中,不new对象使用的是同一个对象,所以 ,slow 和fast用的都是同一个对象,而修改,
代码实现
package com.xu.lecode.算法.双指针;

import com.xu.fingeroffer.node.ListNode;

public class 删除链表的倒数第n个节点19 {
    public static void main(String[] args) {
    }

    public static ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        //快指针
        ListNode first = head;
        //满指针
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;

    }

}

3、无重复的最长字符串

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
问题分析
滑动窗口,m=0,n=1,如果m和n直接没有存在相同的字符,则n++,size++;如果m-n中存在相同的字符则将size和max比较,较大的给max,当n走到末尾的时候,比较一次size和max 最终返回较大的值
代码实现
int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;

深度优先广度优先

567、字符串的排列

题目描述
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,s1 的排列之一是 s2 的 子串 。
问题解析
将s1 的字符串中的所有值,存入到一个数组中,然后对s2利用滑动窗口,窗口长度为n进行遍历,如果数组的所有值都一一对应,则为真,否则为假
代码实现
package com.xu.lecode.算法.双指针;

import java.util.HashMap;
import java.util.Map;

public class 无重复字符的最长字串3 {
    public static void main(String[] args) {
        String s = "pwwkew";
        System.out.println(lengthOfLongestSubstring(s));
    }

    public static int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;


    }
}

733、图像渲染

题目描述
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。

给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。

为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。

最后返回经过上色渲染后的图像。

问题解析
深度优先算法计算
代码实现
package com.xu.lecode.算法.深度优先或广度优先;

public class 图像渲染733 {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        int currColor = image[sr][sc];
        if (currColor != newColor) {
            dfsFill(image, sr, sc, currColor, newColor);
        }
        return image;
    }

    public void dfsFill(int[][] grid, int i, int j, int currColor, int newColor) {
        if (i < 0 || i >= grid.length) {
            return;
        }
        if (j < 0 || j >= grid[0].length) {
            return;
        }
        if (grid[i][j] == currColor) {
            grid[i][j] = newColor;
            dfsFill(grid, i - 1, j, currColor, newColor);
            dfsFill(grid, i, j + 1, currColor, newColor);
            dfsFill(grid, i + 1, j, currColor, newColor);
            dfsFill(grid, i, j - 1, currColor, newColor);
        }
    }


}

695、岛屿中的最大面积

题目描述
给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
问题解析d
采用bfs算法,遍历数组,对数组中的每个数据都采用bfs算法,即遍历数组,进行查找,如果为1,则找他的周围的一圈,使得num+1,最后找出最大的num
代码实现
package com.xu.lecode.算法.深度优先或广度优先;

import java.util.Deque;
import java.util.LinkedList;

public class 岛屿的最大面积695 {
    public int maxAreaOfIsland(int[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == 1) {
                    res = Math.max(res, dfs(i, j, grid));
                }
            }
        }
        return res;
    }

    private int dfs(int i, int j, int[][] grid) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] == 0) {
            return 0;
        }
        grid[i][j] = 0;
        int num = 1;
        num += dfs(i + 1, j, grid);
        num += dfs(i - 1, j, grid);
        num += dfs(i, j + 1, grid);
        num += dfs(i, j - 1, grid);
        return num;

    }

}



617合并二叉树

题目描述
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

问题解析
深搜,先分别判断r1 r2是否为null,若为null 返回另外一个,然后new 一个新的节点,递归调用,返回新的节点
代码实现
package com.xu.lecode.算法.深度优先或广度优先;import com.xu.fingeroffer.node.TreeNode;import com.xu.lecode.算法.小飞.机器人碰撞问题.Robot;public class 合并二叉树617 {    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {        if (root1 == null) {            return root2;        }        if (root2 == null) {            return root1;        }        TreeNode node = new TreeNode(root1.val + root2.val);        node.left = mergeTrees(root1.left, root2.left);        node.right = mergeTrees(root1.right, root2.right);        return node;    }}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值