剑指Offer题目详解(CPP、JAVA)

2019.10.11更新

最近开始学习c++,在本文中增加c++的解法,并更新一部分题目的解法。

前言(2018.6.1)

虽然已经度过了艰辛的找实习的日子,到了公司实习。但依然非常怀念那段准备面试、坐在实验室里刷剑指offer的日子。

1.二维数组的查找

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路: 从右上角或者左下角进行查找,以减少判断次数。
JAVA求解

public class Solution {
     
     public boolean Find(int target, int [][] array) {
     
         int rows = array.length;
         int cols = array[0].length;
         for(int i = rows-1;i>=0;i--){
     
             for(int j = 0;j<cols;j++){
     
                 if(target == array[i][j]) return true;
             }
         }
         return false;
    }
}

CPP求解

class Solution {
     
public:
    bool Find(int target, vector<vector<int> > array) {
     
        int rows = array.size();
        int cols = array[0].size();
        for(int i = rows-1;i>=0;i--){
     
            for(int j = 0;j<cols;j++){
     
                if(target == array[i][j]){
     
                    return true;
                }
            }
        }
        return false;
    }
};

2.替换空格

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

解题思路:使用StringBuffer或StringBuilder类,进行替换。
JAVA求解

    public String replaceSpace(StringBuffer str) {
     
        StringBuffer newStr = new StringBuffer();
        if (str == null ){
     
            return  null;
        }
        int len = str.length();
        for (int i=0; i <len; i++){
     
            if (String.valueOf(str.charAt(i)).equals(" ")){
     
                newStr.append("%20");
            }else {
     
                newStr.append(str.charAt(i));
            }
        }
        return String.valueOf(newStr);
    }

CPP求解

class Solution {
     
public:
	void replaceSpace(char *str,int length) {
     
        int blank = 0;
        for(int i = 0; i < length; i++){
     
            if(str[i] == ' '){
     
                blank++;
            }
        }
        int newLen = length+ 2*blank;
        for(int i = length-1;i>=0;i--){
     
            if(str[i] == ' '){
     
                str[--newLen] = '0';
                str[--newLen] = '2';
                str[--newLen] = '%';
            }else{
     
                str[--newLen] = str[i];
            }
        }
	}
};

3.从尾到头打印链表

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

解题思路:利用栈来存放遍历得到的链表的值,这样会浪费O(n)的空间。可以先反转链表,再做输出,这样时间复杂度为O(n),空间复杂度为O(1)。
Java求解

import java.util.ArrayList;
public class Solution {
     
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
     
        //反转链表后输出,O(n)
        ArrayList<Integer> res = new ArrayList<>();
        if(listNode==null) return res;
        if(listNode.next==null){
     
            res.add(listNode.next.val);
            return res;
        }
        ListNode newHead = new ListNode(-1);
        while(listNode!=null){
     
            ListNode temp = listNode.next;
            listNode.next = newHead.next;
            newHead.next = listNode;
            listNode = temp;
        }
        newHead = newHead.next;
        while(newHead!=null){
     
            res.add(newHead.val);
            newHead = newHead.next;
        }
        return res;
    }
}

CPP求解

class Solution {
     
public:
    vector<int> printListFromTailToHead(ListNode* head) {
     
        vector<int> res;
        if(head==NULL) return res;
        if(head->next==NULL){
     
            res.push_back(head->val);
            return res;
        }
        ListNode* newHead =new ListNode(-1);
        while(head!=NULL){
     
            ListNode* temp = head->next;
            head->next = newHead->next;
            newHead->next = head;
            head = temp;
        }
        newHead = newHead->next;
        while(newHead!=NULL){
     
            res.push_back(newHead->val);
            newHead = newHead->next;
        }
        return res;
    }
};

}


## 4.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解题思路:使用递归的方法,在中序遍历的结果中找到根结点,该节点左边是左子树,右边是又子树。不断递归,求解。

import java.util.Arrays;

public class Solution {
public TreeNode reConstructBinaryTree(int [] pre, int [] in) {

    if(pre.length == 0 ||in.length ==0){
        return  null;
    }
    if (pre.length!= in.length){
        return null;
    }
    TreeNode res = new TreeNode(pre[0]);
    for (int i = 0;i

}


## 5.用两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

解题思路:一个栈压入元素,而另一个栈作为缓冲,将栈1的元素出栈后压入栈2中。

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

public int pop() throws Exception {
if (stack1.isEmpty() && stack2.isEmpty()) {
throw new Exception(“stack is empty”);
}

if (stack2.isEmpty()) {
    while(!stack1.isEmpty()) {
        stack2.push(stack1.pop());
    }
}
return stack2.pop();

}


## 6.旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路:这个很简单,找出第一个递减的位置即可。
JAVA求解
```java
import java.util.ArrayList;
public class Solution {
    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 0;
    }
}

CPP求解

class Solution {
     
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
     
        if(rotateArray.size()==0) return 0;
        for(int i =0;i < rotateArray.size()-1;i++){
     
            if(rotateArray[i]>rotateArray[i+1]){
     
                return rotateArray[i+1];
            }
        }
        return 0;
    }
};

7.斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39

解题思路:用非递归的形式写斐波那契数列,效率更高

    public int Fibonacci(int n) {
        int res = 0;
        int lost2 = 0;
        int lost1 = 1;
        if (n==0){
            return  lost2;
        }
        if(n==1){
            return  lost1;
        }
        for (int i = 2;i <=n; i++){
            res = lost1+lost2;
            lost2 = lost1;
            lost1 = res;
        }
        return res;
    }

8.跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

解题思路:这个题目其实就是斐波那契数列的变形。

    public int JumpFloor(int target) {
        int step1 = 1;
        int step2 = 2;
        if (target==0){
            return 0;
        }
        if (target==1){
            return 1;
        }
        if (target==2){
            return 2;
        }
        int res = 0;
        for (int i=3;i<=target;i++){
            res = step1+step2;
            step1 = step2;
            step2 = res;
        }
        return res;
    }

9.变态跳青蛙

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:Fib(n)=Fib(n-1)+Fib(n-2)+Fib(n-3)+…+Fib(n-n)=Fib(0)+Fib(1)+Fib(2)+…+Fib(n-1)
Fib(n) = 2Fib(n-1)

10.矩形覆盖

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解题思路:填充一个的时候,问题规模变成n-1,填充2个的时候,问题规模变成n-2

    public int RectCover(int target) {
        if(target == 0){
            return  0;
        }

        if (target == 1){
            return  1;
        }

        if (target == 2){
            return 2;
        }

        return  RectCover(target-1)+RectCover(target-2);

    }

11.二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
解题思路:移位计算即可。

    public int NumberOf1(int n) {
        int count = 0;
        int flag = 1;
        while(flag != 0){
            if ((flag&n) != 0){
                count++;
            }
            flag = flag <<1;
        }
        return count;

    }

12.数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
解题思路:考虑输入值的多种情况。

public class Solution {
public double Power(double base, int exponent) {
    double res = 0;
    if (equal(base,0)) {
        return 0;
    }
    if (exponent == 0) {
        return 1.0;
    }
    if (exponent > 0) {
        res = mutiply(base,exponent);
    }else {
        res = mutiply(1/base,-exponent);
    }
    return res;
}

public double mutiply(double base, int e) {
    double sum = 1;
    for (int i = 0; i < e; i++) {
        sum = sum * base;
    }
    return sum;
}

public boolean equal(double a, double b) {
    if (a - b < 0.000001 && a - b > -0.000001) {
        return true;
    }
    return false;
}
}

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

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路:通过辅助队列解题。

import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    public void reOrderArray(int [] array) {
        if(array==null || array.length==0){
            return;
        }
        Queue qe = new LinkedList();
        for (int i= 0;i< array.length;i++){
            if(array[i]%2==1){
                qe.offer(array[i]);
            }
        }
        for (int j =0;j< array.length;j++){
            if (array[j]%2==0) {
                qe.offer(array[j]);
            }
        }
        for(int k=0;k

14.链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

解题思路:快慢指针,快指针先走k-1

    public ListNode FindKthToTail(ListNode head,int k) {
        if (head==null||k<=0){
            return  null;
        }
        ListNode fast = head;
        ListNode slow = head;
        for (int i = 1;i

15.反转链表

输入一个链表,反转链表后,输出新链表的表头。

解题思路:遍历链表,采用头插法。
JAVA求解

public class Solution {
     
    public ListNode ReverseList(ListNode head) {
     
        if(head==null || head.next==null) return head;
        ListNode newHead = new ListNode(-1);
        while(head!=null){
     
            ListNode temp = head.next;
            head.next = newHead.next;
            newHead.next = head;
            head = temp;
        }
        return newHead.next;
    }
}

CPP求解

class Solution {
     
public:
    ListNode* ReverseList(ListNode* pHead) {
     
        if(pHead==NULL ||pHead->next==NULL) return pHead;
        ListNode* newHead = new ListNode(-1);
        while(pHead!=NULL){
     
            ListNode* temp = pHead->next;
            pHead->next = newHead->next;
            newHead->next = pHead;
            pHead = temp;
        }
        return newHead->next;
    }
};

16.合并两个排序链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解题思路:递归求解

    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null){
            return list2;
        }
        if (list2 == null){
            return list1;
        }
        ListNode res = null;
        if (list1.val <= list2.val){
            res = list1;
            res.next = Merge(list1.next,list2);
        }else {
            res = list2;
            res.next = Merge(list1,list2.next);
        }

        return res;

    }

17.树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

解题思路:递归判断

    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if (root2 == null || root1 == null){
            return false;
        }

        if (root1 == root2){
            return reallyHasSubTree(root1,root2);
        }else {
            return reallyHasSubTree(root1.left,root2)||reallyHasSubTree(root1.right,root2);
        }

    }


    public boolean reallyHasSubTree(TreeNode root1,TreeNode root2){
        if (root1==null&&root2!=null){
            return false;
        }
        if (root2 ==null){
            return true;
        }

        if (root1.val != root2.val){
            return false;
        }
        return reallyHasSubTree(root1.left,root2.left)&&reallyHasSubTree(root1.right,root2.right);

    }

18.二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。
解题思路:对于树的问题,基本都采用递归求解,比较方便。

    public void Mirror(TreeNode root) {
        if(root == null){
            return;
        }
        if(root.left==null&&root.right==null){
            return;
        }
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        Mirror(root.left);
        Mirror(root.right);
        
    }

19.顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 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.

解题思路:

21.包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

解题思路:定义2个栈,其中一个记录min元素

        Stack stackData = new Stack<>();
        Stack stackMin = new Stack<>();
        public void push(int node) {
                stackData.push(node);
                if(stackMin.isEmpty()){
                        stackMin.push(node);
                }else {
                        if(stackMin.peek()>node){
                                stackMin.push(node);
                        }
                }
        }

        public void pop() {
                int temp = stackData.pop();
                if(stackMin.peek()==temp){
                        stackMin.pop();
                }
        }

        public int top() {
                return stackData.peek();
        }

        public int min() {
                return stackMin.peek();
        }

22.栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路:模拟栈的压入过程,不断和pop序列进行比较。

        public boolean IsPopOrder(int [] pushA,int [] popA) {
                if(pushA.length!=popA.length ||pushA.length==0 ||popA.length==0){
                        return false;
                }
                Stack sk = new Stack<>();
                int index = 0;
                for (int i =0;i

23.从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

解题思路:实则为二叉树的层次遍历,用一个队列依次记录顺序。

        public ArrayList PrintFromTopToBottom(TreeNode root) {
                ArrayList arr = new ArrayList<>();
                if(root == null) return arr;
                LinkedList queue = new LinkedList<>();
                queue.add(root);
                while(!queue.isEmpty()){
                        TreeNode temp = queue.pop();
                        arr.add(temp.val);
                        if(temp.left != null){
                                queue.add(temp.left);
                        }

                        if(temp.right != null){
                                queue.add(temp.right);
                        }
                }
                return arr;
        }

24.二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路:后序遍历最后一个节点是树的根结点,以根结点去切分,类似于递归形式的后序遍历,结合搜索树的特点,去判断序列是不是我们想要的序列。

        public boolean VerifySquenceOfBST(int [] sequence) {
                if(sequence.length == 0) return false;
                if(sequence.length == 1) return true;
                return judge(sequence,0,sequence.length -1);
        }

        public boolean judge(int [] sequence, int start, int end){
                if(start>=end) return true;
                int i = start;
                while(i < end && sequence[i] < sequence[end])
                        i++;
                for(int j = i;j

25.二叉树中和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

解题思路:用先序遍历来遍历二叉树,用target-root.val不断更新target。

         ArrayList> res = new ArrayList<>();
         ArrayList temp = new ArrayList<>();
        public ArrayList> FindPath(TreeNode root,int target) {
                if(root==null) return res;
                target -= root.val;
                temp.add(root.val);
                if(target==0 && root.left==null && root.right==null){
                        res.add(new ArrayList(temp));
                }else {
                        FindPath(root.left,target);
                        FindPath(root.right,target);
                }
                temp.remove(temp.size()-1);
                return res;
        }

27.二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
解题思路:

28.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
解题思路:

29.数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路:先找出出现次数最多的数字,再判断该数字出现次数是否超过一半

public int MoreThanHalfNum_Solution(int [] array) {
        int res = array[0], count = 1;
        for(int i=1; i array.length/2 ? res : 0;
    }

30.最小的k个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
解题思路:利用最大堆,每次和堆顶元素比较,最后剩下的就是最小的k个数。

        public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
                ArrayList res = new ArrayList<>();
                if(input==null || k==0 ||k >input.length) return res;
                PriorityQueue maxHeap = new PriorityQueue(k, new Comparator() {
                                @Override
                                public int compare(Integer o1, Integer o2) {
                                        return o2 - o1;
                                }
                        });
                for(int i =0; iinput[i]){
                                        maxHeap.poll();
                                        maxHeap.offer(input[i]);
                                }
                        }
                }
                for(Integer i: maxHeap){
                        res.add(i);
                }
                return res;

        }

31.连续子数组的和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

解题思路:对于一个数组中的一个数x,若是x的左边的数加起来非负,那么加上x能使得值变大,这样我们认为x之前的数的和对整体和是有贡献的。如果前几项加起来是负数,则认为有害于总和。
我们用cur记录当前值, 用max记录最大值,如果cur<0,则舍弃之前的数,让cur等于当前的数字,否则,cur = cur+当前的数字。若cur和大于max更新max。

        public int FindGreatestSumOfSubArray(int[] array) {
                if(array.length < 1) return -1;
                int cur = array[0];
                int max = array[0];
                for(int i = 1;i 0 ? cur +array[i] : array[i];
                        if(cur > max) max = cur;
                }
                return max;
        }

32.整数中1出现的次数

求出1到13的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下113中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

解题思路:把数字变成字符串,遍历

    public int NumberOf1Between1AndN_Solution(int n) {
        int res = 0;
        StringBuffer s = new StringBuffer();
        for(int i = 1; i<=n; i++){
            s.append(i);
        }
        String str = s.toString();
        for(int i=0; i

33.把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路:把数字转换为字符串,通过比较大小进行排序,然后合并输出。

        public String PrintMinNumber(int [] numbers) {
                if(numbers.length <= 0) return "";
                if(numbers.length == 1) return String.valueOf(numbers[0]);
                StringBuffer res = new StringBuffer();
                String [] strArr = new String[numbers.length];
                for(int i =0;i() {
                        public int compare(String o1, String o2) {
                                String s1 = o1 + o2;
                                String s2 = o2 + o1;
                                return s1.compareTo(s2);
                        }
                });
                for(int i = 0; i

34.丑数

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

public int GetUglyNumber_Solution(int index) {
        if(index <= 0)
            return 0;
        if(index == 1)
            return 1;
        int t2 = 0, t3 = 0, t5 = 0;
        int [] res = new int[index];
        res[0] = 1;
        for(int i = 1; i

35.第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
解题思路:利用hashmap映射,遍历map

public int FirstNotRepeatingChar(String str) {
        int len = str.length();
        if(len == 0)
            return -1;
        HashMap map = new HashMap<>();
        for(int i = 0; i < len; i++){
            if(map.containsKey(str.charAt(i))){
                int value = map.get(str.charAt(i));
                map.put(str.charAt(i), value+1);
            }else{
                map.put(str.charAt(i), 1);
            }
        }
        for(int i = 0; i < len; i++){
            if(map.get(str.charAt(i)) == 1)
                return i;
        }
        return -1;
    }

36.数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
解题思路:用归并排序的思想,在排序的过程中计算逆序对

    public int InversePairs(int [] array) {
        int len = array.length;
        if(array== null || len <= 0){
            return 0;
        }
        return mergeSort(array, 0, len-1);
    }
    public int mergeSort(int [] array, int start, int end){
        if(start == end)
            return 0;
        int mid = (start + end) / 2;
        int left_count = mergeSort(array, start, mid);
        int right_count = mergeSort(array, mid + 1, end);
        int i = mid, j = end;
        int [] copy = new int[end - start + 1];
        int copy_index = end - start;
        int count = 0;
        while(i >= start && j >= mid + 1){
            if(array[i] > array[j]){
                copy[copy_index--] = array[i--];
                count += j - mid;
                if(count > 1000000007){
                    count %= 1000000007;
                }
            }else{
                copy[copy_index--] = array[j--];
            }
        }
        while(i >= start){
            copy[copy_index--] = array[i--];
        }
        while(j >= mid + 1){
            copy[copy_index--] = array[j--];
        }
        i = 0;
        while(start <= end) {
            array[start++] = copy[i++];
        }
        return (left_count+right_count+count)%1000000007;
    }

37.两个链表的第一个公公节点

输入两个链表,找出它们的第一个公共结点。
解题思路:第一个公共节点后都是公共节点,只需找到两个链表的差值,然后让一个指针先走diff步

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null ||pHead2==null) return null;
        int count1 = 1;
        int count2 = 1;
        ListNode p1 = pHead1;
        while (p1.next!=null){
            count1++;
            p1 = p1.next;
        }
        ListNode p2 = pHead2;
        while(p2.next!=null){
            count2++;
            p2 = p2.next;
        }
        if(count1-count2>0){
            int diff = count1 -count2;
            while(diff!=0){
                pHead1 = pHead1.next;
                diff--;
            }
        }else {
            int diff = count2 - count1;
            while(diff!=0){
                pHead2 = pHead2.next;
                diff--;
            }
        }
        while(pHead1!=null&&pHead2!=null){
            if(pHead1==pHead2){
                return pHead1;
            }
            pHead1=pHead1.next;
            pHead2=pHead2.next;
        }
        return null;
    }

38.数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。
解题思路:用二分查找的思想,比如我们要查找2,就在整数数组里查找1.5和2.5,找到这两个应该插入的位置,做差。

    public int GetNumberOfK(int [] array , int k) {
        if(array.length<=0) return 0;
        return find(array,k+0.5) - find(array,k-0.5);
    }

    public int find(int[] array,double k){
        int low = 0;
        int high = array.length - 1;
        while(low<=high){
            int mid = (low+high)/2;
            if(array[mid]>k){
                high = mid-1;
            }else {
                low = mid+1;

            }
        }
        return low;
    }

39.二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
解题思路:常规问题,递归。

    public int TreeDepth(TreeNode root) {
        if(root==null) return 0;
        int left = treeDepth(root.left) +1;
        int right = treeDepth(root.right)+1;
        return left>right?left:right;
    }

40.平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。
解题思路:平衡二叉树的定义,每一个棵树的左右子树的最大深度都小于等于1,对于空树,是平衡二叉树。

    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null) return true;
        return IsBalanced_Solution(root.left)&& IsBalanced_Solution(root.right)&&
                Math.abs(MaxDeep(root.left)-MaxDeep(root.right))<=1;
    }
    public int MaxDeep(TreeNode root){
        if(root==null) return 0;
        return 1+Math.max(MaxDeep(root.left),MaxDeep(root.right));
    }

41.数组中只出现一次的数

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
解题思路:hashmap法

import java.util.HashMap;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap temp = new HashMap();
        for(int i = 0; i < array.length; i++){
            if(temp.containsKey(array[i]))
                temp.remove(array[i]);
            else
                temp.put(array[i], 1);
        }
        int [] a = new int [array.length];
        int i = 0;
        for(Integer k: temp.keySet()){
            a[i] = k;
            i++;
        }
        num1[0] = a[0];
        num2[0] = a[1];
    }
}

42. 和为s的连续正数序列

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
解题思路:滑动窗口的思想,一点一点扩张

   public ArrayList> FindContinuousSequence(int sum) {
        ArrayList> res = new ArrayList<>();
        if(sum <3) return res;
        int start = 1;
        int end = 2;
        int mid = (1+sum)/2;
        while(start all){
                end++;
            }else if(sum  getSequence(int start, int end){
        ArrayList temp = new ArrayList<>();
        for(int i = start; i <= end; i++){
            temp.add(i);
        }
        return temp;
    }

43.和为s的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
解题思路:数组顺序递增,从左右逼近

    public ArrayList FindNumbersWithSum(int [] array,int sum) {
        ArrayList res = new ArrayList();
        if(array.length < 2)
            return res;
        int i = 0, j = array.length - 1;
        while(i != j){
            if(array[i] + array[j] == sum){
                res.add(array[i]);
                res.add(array[j]);
                break;
            }else if(array[i] + array[j] < sum){
                i++;
            }else{
                j--;
            }
        }
        return res;
    }

44.左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
解题思路:在移动位数的地方一刀切。

    public String LeftRotateString(String str,int n) {
        if(str.length() ==0) return "";
        String s1 = str.substring(0,n);
        String s2 = str.substring(n,str.length());
        return s2+s1;
    }

45.翻转单词顺序

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

解题思路:以空格split,翻转。。

    public String ReverseSentence(String str) {
        if(str.trim().length() == 0)
            return str;
        String [] temp = str.split(" ");
        String res = "";
        for(int i = temp.length - 1; i >= 0; i--){
            res += temp[i];
            if(i != 0)
                res += " ";
        }
        return res;
    }

46.扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
解题思路:跟着题目的思路。。设置好边界条件

    public boolean isContinuous(int [] numbers) {
        int zero = 0, dis = 0;
        if(numbers.length != 5)
            return false;
        Arrays.sort(numbers);
        for(int i = 0; i < 4; i++){
            if(numbers[i] == 0){
                zero ++;
                continue;
            }
            if(numbers[i] == numbers[i+1])
                return false;
            if(numbers[i+1] - numbers[i] > 1){
                dis += numbers[i+1] - numbers[i] - 1;
            }
        }
        if(zero >= dis)
            return true;
        else
            return false;
    }

47.孩子们的游戏

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

解题思路:用环形链表模拟圆圈。

    public int LastRemaining_Solution(int n, int m) {
        if(n < 1 || m < 1)
            return -1;
        LinkedList link = new LinkedList();
        for(int i = 0; i < n; i++)
            link.add(i);
        int index = -1;  
        while(link.size() > 1){
            index = (index + m) % link.size();  //对 link的长度求余不是对 n
            link.remove(index);
            index --;
        }
        return link.get(0);
    }

48.求1+2+3…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
解题思路:递归调用

    public int Sum_Solution(int n) {
        int sum = n;
        boolean t = (n > 0) && (sum += Sum_Solution(n-1))>0;
        return sum;
    }

49.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

public int Add(int num1, int num2) {
        if(num2 == 0)
            return num1;
        int sum = 0, carry = 0;
        while(num2 != 0){
            sum = num1 ^ num2;
            carry = (num1 & num2) << 1;
            num1 = sum;
            num2 = carry;
        }
        return num1;
    }

50.把字符串转换成整数

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
解题思路:判断正负,判断字符是否为数字字符

        public int StrToInt(String str) {
                if(str.length()==0) return 0;
                int flag= 0;
                if(str.charAt(0)=='+') flag = 1;
                if(str.charAt(0)=='-') flag = 2;
                int start = flag>0?1:0;
                long res = 0;
                while (start'9') return 0;
                        res = res*10 + str.charAt(start) - '0';
                        start++;
                }
                return flag ==2?-(int)res:(int)res;
        }

51.数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

        public boolean duplicate(int numbers[],int length,int [] duplication) {
                if(length==0) return false;
                for(int i = 0;i 

52.构建乘积数组

给定一个数组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]。不能使用除法。

        public int[] multiply(int[] A) {
                if(A.length<=1) return A;
                int[] res = new int[A.length];
                res[0] = 1;
                for(int i = 1;i=0;j--){
                        temp *= A[j+1];
                        res[j] *= temp;
                }
                return res;
        }

53.正则表达式匹配

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

54.表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

public boolean isNumeric(char[] str) {
        int len = str.length;
        boolean sign = false, decimal = false, hasE = false;
        for(int i = 0; i < len; i++){
            if(str[i] == '+' || str[i] == '-'){
                if(!sign && i > 0 && str[i-1] != 'e' && str[i-1] != 'E')
                    return false;
                if(sign && str[i-1] != 'e' && str[i-1] != 'E')
                    return false;
                sign = true;
            }else if(str[i] == 'e' || str[i] == 'E'){
                if(i == len - 1)
                    return false;
                if(hasE)
                    return false;
                hasE = true;
            }else if(str[i] == '.'){
                if(hasE || decimal)
                    return false;
                decimal = true;
            }else if(str[i] < '0' || str[i] > '9')
                return false;
        }
        return true;
    }

55.字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

解题思路:哈希

import java.util.HashMap;
public class Solution {
    HashMap map = new HashMap();
    StringBuffer s = new StringBuffer();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        s.append(ch);
        if(map.containsKey(ch)){
            map.put(ch, map.get(ch)+1);
        }else{
            map.put(ch, 1);
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(int i = 0; i < s.length(); i++){
            if(map.get(s.charAt(i)) == 1)
                return s.charAt(i);
        }
        return '#';
    }
}

56.链表中的入口节点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路:数组记录

        public ListNode EntryNodeOfLoop(ListNode pHead){
                if(pHead==null ||pHead.next==null) return null;
                ArrayList arr = new ArrayList();
                arr.add(pHead);
                pHead = pHead.next;
                while (!arr.contains(pHead)){
                        arr.add(pHead);
                        pHead = pHead.next;
                }
                return pHead;
        }

57.删除链表中重复的节点

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

public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null || pHead.next == null)
            return pHead;
        ListNode head = new ListNode(-1);
        head.next = pHead;
        ListNode first = head;
        ListNode second = first.next;
        while(second != null){
            if(second.next != null && second.val == second.next.val){
                while(second.next != null && second.val == second.next.val){
                    second = second.next;
                }
                first.next = second.next;
            }else{
                first = first.next;
            }
            second = second.next;
        }
        return head.next;
    }

58.二叉树的下一个节点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null){
            return null;
        }
        if(pNode.right != null){
            TreeLinkNode node = pNode.right;
            while(node.left != null){
                node = node.left;
            }
            return node;
        }
        while(pNode.next != null){
            TreeLinkNode root = pNode.next;
            if(pNode == root.left)
                return root;
            pNode = root;
        }
        return null;
    }

59.对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null)
            return true;
        return isSymmetrical(pRoot.left, pRoot.right);
    }
    boolean isSymmetrical(TreeNode left, TreeNode right){
        if(left == null && right == null)
            return true;
        if(left == null || right == null)
            return false;
        if(left.val == right.val){
            return isSymmetrical(left.left, right.right) && 
                isSymmetrical(left.right, right.left);
        }
        return false;
    }

60.按之字形顺序遍历二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

public ArrayList > Print(TreeNode pRoot) {
        ArrayList > res = new ArrayList >();
        Stack s1 = new Stack();
        Stack s2 = new Stack();
        int flag = 1;
        if(pRoot == null)
            return res;
        s2.push(pRoot);
        ArrayList temp = new ArrayList();
        while(!s1.isEmpty() || !s2.isEmpty()){
            if(flag % 2 != 0){
                while(!s2.isEmpty()){
                    TreeNode node = s2.pop();
                    temp.add(node.val);
                    if(node.left != null){
                        s1.push(node.left);
                    }
                    if(node.right != null){
                        s1.push(node.right);
                    }
                }
            }
            if(flag % 2 == 0){
                while(!s1.isEmpty()){
                    TreeNode node = s1.pop();
                    temp.add(node.val);
                    if(node.right != null){
                        s2.push(node.right);
                    }
                    if(node.left != null){
                        s2.push(node.left);
                    }
                }
            }
            res.add(new ArrayList(temp));
            temp.clear();
            flag ++;
        }
        return res;
    }

61.把二叉树打印成多行

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


    ArrayList > Print(TreeNode pRoot) {
        ArrayList > res = new ArrayList >();
        if(pRoot == null)
            return res;
        ArrayList temp = new ArrayList();
        Queue layer = new LinkedList();
        layer.offer(pRoot);
        int start = 0, end = 1;
        while(!layer.isEmpty()){
            TreeNode node = layer.poll();
            temp.add(node.val);
            start ++;
            if(node.left != null)
                layer.add(node.left);
            if(node.right != null)
                layer.add(node.right);
            if(start == end){
                start = 0;
                res.add(temp);
                temp = new ArrayList();
                end = layer.size();
            }
        }
        return res;
    }

62.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

import java.util.Queue;
import java.util.LinkedList;
public class Solution {
    String Serialize(TreeNode root) {
        if(root == null){
            return "#,";
        }
        StringBuffer res = new StringBuffer(root.val + ",");
        res.append(Serialize(root.left));
        res.append(Serialize(root.right));
        return res.toString();
    }
    TreeNode Deserialize(String str) {
        String [] res = str.split(",");
        Queue queue = new LinkedList();
        for(int i = 0; i < res.length; i++){
            queue.offer(res[i]);
        }
        return pre(queue);
    }
    TreeNode pre(Queue queue){
        String val = queue.poll();
        if(val.equals("#"))
            return null;
        TreeNode node = new TreeNode(Integer.parseInt(val));
        node.left = pre(queue);
        node.right = pre(queue);
        return node;
    }
}

63.二叉搜索树的第k个节点

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

public class Solution {
    int index = 0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot != null){
            TreeNode node = KthNode(pRoot.left, k);
            if(node != null)
                return node;
            index ++;
            if(index == k)
                return pRoot;
            node = KthNode(pRoot.right, k);
            if(node != null)
                return node;
        }
        return null;
    }
}

64.数据流中的中位数

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

65.滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。

import java.util.ArrayList;
public class Solution {
    public ArrayList maxInWindows(int [] num, int size)
    {
        ArrayList res = new ArrayList();
        if(num.length < size || size == 0)
            return res;
        for(int i = 0; i < num.length - size + 1; i++){
            res.add(max(num, i, size));
        }
        return res;
    }
    public int max(int [] num, int index, int size){
        int res = num[index];
        for(int i = index + 1; i < index + size; i++){
            if(num[i] > res)
                res = num[i];
        }
        return res;
    }
}

66.矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

67.机器人的运动轨迹

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

你可能感兴趣的:(Coding,剑指offer,leetcode,java,算法,面试)