21.包含min函数的栈

 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

考点:栈

思路:同《九章算法》CHAPTER EIGHT 第四题 带最小值操作的栈

代码:

import java.util.Stack;

public class Solution {

    Stack<Integer> stack = new Stack<Integer>();
    Stack<Integer> minStack = new Stack<Integer>();
     
    public void push(int node) {
        stack.push(node);
        if (minStack.isEmpty()) {
            minStack.push(node);
        } else {
            minStack.push(Math.min(node, minStack.peek()));
        }
    }
     
    public void pop() {
        minStack.pop();
        stack.pop();
    }
     
    public int top() {
        return stack.peek();
    }
     
    public int min() {
         return minStack.peek();
    }
}
View Code

测试用例:

新压入栈的数字比之前的最小值大。

新压入栈的数字比之前的最小值小。

弹出栈的数字不是最小的元素。

弹出栈的数字是最小的元。

22.顺时针打印矩阵

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

考点:数组

思路:把矩阵想象成若干个圈,用一个循环来打印矩阵,每一次打印矩阵中的一个圈。

代码:

import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> list = new ArrayList<Integer>();
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int rows = matrix.length;
        int columns = matrix[0].length;
        int start = 0;
        while (rows > start * 2 && columns > start * 2) {
            printMatrixInCircle(matrix, rows, columns, start);
            start++;
        }
        return list;
    }
    public void printMatrixInCircle(int [][] matrix, int rows, int columns, int start){
        // 从左到右打印一行,这一步是必须的
        for (int i = start; i < columns - start; i++)
            list.add(matrix[start][i]);
        // 从上到下打印一列,前提条件:终止行号大于起始行号
        for (int j = start + 1; j < rows - start; j++)
            list.add(matrix[j][columns - start - 1]);
        // 从右到左打印一行,前提条件:终止行号大于起始行号,终止列号大于起始列号
        for (int m = columns - start - 2; m >= start && rows - start - 1 > start; m--)
            list.add(matrix[rows - start - 1][m]);
        // 从下到上打印一列,前提条件:终止行号比起始行号至少大2,终止列号大于起始列号
        for (int n = rows - start - 2; n >= start + 1 && columns - start - 1 > start; n--)
            list.add(matrix[n][start]);
    }
}
View Code

测试用例:

数组有多行多列、数组只有一行、数组中只有一列、数组中只有一行一列。

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

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

考点:举例让抽象具体化

思路:已知条件:后序序列最后一个值为root;二叉搜索树左子树值都比root小,右子树值都比root大。

1、确定root;2、遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树;
3、遍历左子树,若发现有大于root的值,则直接返回false;
4、分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3)。

代码:

public class Solution {
    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[] a, int start, int root){
        if (start >= root) {
            return true;
        }
        int i = start;
        while (a[i] < a[root]) {
            i++;
        }
        for(int j = i; j < root; j++) {
            if (a[j] < a[root]) {
                return false;
            }
        }
        return judge(a,start,i-1)&&judge(a, i, root-1);
    }
}
View Code

测试用例:

功能测试(输入的后续遍历的序列对应一棵二叉树,包括完全二叉树、所有结点没有左/右子树的二叉树、只有一个结点的二叉树;输入的后序遍历的序列没有对应一棵二叉树)

特殊输入测试(指向后序遍历序列的指针为NULL指针)。

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

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0

考点:数组

思路:

如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。

在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。
代码:
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array == null || array.length == 0) {
            return 0;
        }
        int result = array[0];
        int count = 1;
        for (int i=1; i < array.length; i++) {
            if (result == array[i]) {
                count++;  
            } else {
                count--; 
            }
            if (count == 0) {
                result = array[i];
                count = 1;
            }
        }
        int time = 0;
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == result)
                time++;
        }
        if (time * 2 <= array.length){
            return 0;
        }else{
            return result;
        }
    }
}
View Code

测试用例: 

功能测试(输入的数组中存在一个出现次数超过数组长度一半的数字,输入的数组中不存在一个出现次数超过数组长度一半的数字)。

特殊输入测试(输入的数组中只有一个数字、输入NULL指针)。

25.最小的k个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

考点:数组

思路:

基于堆排序算法,构建最大堆(PriorityQueue默认为小顶堆,使用Collections.reverseOrder()变为大顶堆),时间复杂度为O(nlogk);如果用快速排序,时间复杂度为O(nlogn);如果用冒泡排序,时间复杂度为O(n*k)。

创建一个大小为k的数据容器来存储最小的k个数字,从输入的n个整数中一个一个读入放入该容器中,如果容器中的数字少于k个,按题目要求直接返回空;如果容器中已有k个数字,而数组中还有值未加入,此时就不能直接插入了,而需要替换容器中的值。按以下步骤进行插入:先找到容器中的最大值;将待查入值和最大值比较,如果待查入值大于容器中的最大值,则直接舍弃这个待查入值即可;如果待查入值小于容器中的最大值,则用这个待查入值替换掉容器中的最大值;重复上述步骤,容器中最后就是整个数组的最小k个数字。

代码:

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Collections;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        if (input == null || k <= 0 || k > input.length) {
            return res;
        }
        PriorityQueue<Integer> queue = new PriorityQueue<>(k, Collections.reverseOrder());
        for (int i = 0; i < input.length; i++) {
            if (queue.size() < k) {
                queue.add(input[i]);
            } else {
                if (input[i] < queue.peek()) {
                    queue.remove();
                    queue.add(input[i]);
                }
            }
        }
        while (!queue.isEmpty()) {
            res.add(queue.remove());
        }
        return res;
    }
}
View Code

测试用例:

功能测试(输入的数组中有相同的数字,输入的数组中没有相同的数字)

边界值测试(输入的k等于1或者等于数组的长度)

特殊输入测试(k小于1、k大于数组的长度、指向数组的指针为NULL)

26.连续子数组的最大和

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

考点:数组

思路:同《九章算法》CHAPTER SIX 第十七题 最大子数组

动态规划法:当以第i-1个数字结尾的子数组中所有数字的和小于0时,以第i个数字结尾的子数组就是第i个数字本身;如果以第i-1个数字结尾的子数组中所有数字的和大于0,与第i个数字累加就得到以第i个数字结尾的子数组中所有数字的和。

代码:

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if (array == null || array.length == 0) {
            return 0;
        }
        int max = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = 0; i < array.length; i++) {
            if (sum <= 0) {
                sum = array[i];
            } else {
                sum += array[i];
            }
            if (sum > max) {
                max = sum;
            }
        }
        return max;
    }
}
View Code

测试用例:

功能测试(输入的数组中有正数也有负数,输入的数组中全是正数,输入的数组中全是负数)。

特殊输入测试(表示数组的指针为NULL指针)。

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

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

考点:举例让抽象具体化

思路:同《九章算法》CHAPTER THREE 第十七题 二叉树的路径和

代码:

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
        if (root == null) {
            return paths;
        }
        ArrayList<Integer> path = new ArrayList<Integer>();
        path.add(root.val);
        helper(root, path, root.val, target, paths);
        return paths;
    }
    private void helper(TreeNode root,
                        ArrayList<Integer> path,
                        int sum,
                        int target,
                        ArrayList<ArrayList<Integer>> paths) {
        if (root.left == null && root.right == null) {
            if (sum == target) {
                paths.add(new ArrayList<Integer>(path));
            }
        }
        if (root.left != null) {
            path.add(root.left.val);
            helper(root.left, path, sum + root.left.val, target, paths);
            path.remove(path.size() - 1);
        }
        if (root.right != null) {
            path.add(root.right.val);
            helper(root.right, path, sum + root.right.val, target, paths);
            path.remove(path.size() - 1);
        } 
    }
}
View Code

测试用例:

功能测试(二叉树中有一条、多条符合条件的路径,二叉树中没有符合条件的路径)

特殊输入测试(指向二叉树根节点的指针为NULL指针)

28.二叉树的深度

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

考点:知识迁移能力

思路:同《九章算法》CHAPTER  THREE 第四题  二叉树的最大深度

代码:

public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
}
View Code

测试用例:

功能测试(输入普通的二叉树、二叉树中所有结点都没有左/右子树)。

特殊输入测试(二叉树只有一个结点,二叉树的头结点为NULL指针)。

29.第一个只出现一次的字符位置

 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置。

考点:数组

思路:hashmap底层是散列表,数据存储与插入顺序无关,比如依次put进q,w,e字符,可能遍历的时候是e,w,q的顺序。而linkedhashmap底层有一个双向循环链表来维护其存储顺序,遍历的时候是按照插入顺序来遍历的。所以使用LinkedHashMap<Character, Integer>存储字母及相应出现次数更合适。

代码:

import java.util.LinkedHashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        LinkedHashMap<Character, Integer> map = new LinkedHashMap<>();
        if (str == null) {
            return -1;
        }
        int length = str.length();
        for (int i = 0; i < length; i++) {
            if (map.containsKey(str.charAt(i))) {
                int value = map.get(str.charAt(i));
                map.remove(str.charAt(i));
                map.put(str.charAt(i),value + 1);
            } else {
                map.put(str.charAt(i), 1);
            }
        }
        for (int i = 0; i < length; i++) {
            if (map.get(str.charAt(i)) == 1)
                return i;
        }
        return -1;
    }
}
View Code

30.求1+2+3+...+n

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

考点:发散思维能力

思路:

1.需利用逻辑与的短路特性实现递归终止。
2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。

代码:

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
        return sum;
    }
}
View Code

测试用例:

功能测试(输入5、10 求1+2+...+5和1+2+...+10)

边界值测试(输入0和1)

31.字符串的排列

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

考点:字符串

思路:

代码:

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if (str != null && str.length() > 0) {
            PermutationHelper(str.toCharArray(), 0, res);
            Collections.sort(res);
        }
        return (ArrayList)res;
    }
    public void PermutationHelper(char[] cs, int i, ArrayList<String> list) {
        if (i == cs.length - 1) {
            String val = String.valueOf(cs);
            if (!list.contains(val))
                list.add(val);
        } else {
            for (int j = i; j < cs.length; j++) {
                swap(cs, i, j);
                PermutationHelper(cs, i+1, list);
                swap(cs, i, j);
            }
        }
    }
    public void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}
View Code

测试用例:

功能测试(输入的字符串中有1个或者多个字符)。

特殊输入测试(输入的字符串的内容为空或者是NULL指针)。

32.两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

考点:时间空间效率的平衡

思路:同《九章算法》CHAPTER SIX  第十二题 两个链表的交叉

代码:

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        //step one
        ListNode node = pHead1;
        while (node.next != null) {
            node = node.next;
        }
        node.next = pHead2;
        //step two、three
        ListNode result = helper(pHead1);
        //step four
        node.next = null;
        return result;
    }
    
    private ListNode helper(ListNode head) {
        //step two
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        //step three
        slow = head;
        fast = fast.next;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}
View Code

测试用例:

功能测试(输入的两个链表有公共交点:第一个公共结点在链表的中间,第一个公共结点在链表的末尾,第一个公共结点是链表的头结点;输入的两个链表没有公共结点)。

特殊输入测试(输入的链表头结点是NULL指针)。

33. 数组中只出现一次的数字

考点:知识迁移能力

思路:CC150 位操作 1.3题

测试用例:

功能测试(数组中多对重复的数字,数组中没有重复的数字)

34.复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

考点:链表

思路:同《九章算法》CHAPTER SIX 第九题 复制带随机指针的链表

代码:

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    private void copyNext(RandomListNode head) {
        while (head != null) {
            RandomListNode newNode = new RandomListNode(head.label);
            newNode.next = head.next;
            newNode.random = head.random;
            head.next = newNode;
            head = head.next.next;
        }
    }
    private void copyRandom(RandomListNode head) {
        while (head != null) {
            if (head.next.random != null) {
                head.next.random = head.random.next;
            }
            head = head.next.next;
        }
    }
    private RandomListNode splitList(RandomListNode head) {
        RandomListNode newHead = head.next;
        while (head != null) {
            RandomListNode temp = head.next;
            head.next = temp.next;
            head = head.next;
            if (temp.next != null) {
                temp.next = temp.next.next;
            }
        }
        return newHead;
    }
    public RandomListNode Clone(RandomListNode pHead)
    {
        if (pHead == null) {
            return pHead;
        }
        copyNext(pHead);
        copyRandom(pHead);
        return splitList(pHead);
    }
}
View Code

测试用例:

功能测试(包括结点中的特殊指针指向结点自身,两个结点的特殊指针形成环状结构,链表中只有一个结点)

特殊输入测试(指向链表头结点的指针为NULL指针)

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

统计一个数字在排序数组中出现的次数。

考点:知识迁移能力

思路:同《九章算法》CHAPTER TWO 第十四题 目标出现总和

代码:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int[] result = new int[2];
        result[0] = 0;
        result[1] = 0;
        if (array == null || array.length == 0 || k < array[0] || k > array[array.length - 1]) {
            return 0;
        }
        int start = 0;
        int end = array.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (array[mid] >= k) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (array[start] == k) {
            result[0] = start;
        } else if (array[end] == k) {
            result[0] = end;
        } else {
            return 0;
        }
        start = 0;
        end = array.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (array[mid] <= k) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (array[end] == k) {
            result[1] = end;
        } else if (array[start] == k) {
            result[1] = start;
        } else {
            return 0;
        }
        return result[1] - result[0] + 1;
    }
}
View Code

测试用例:

功能测试(数组中包含查找的数字,数组中没有查找的数字,查找的数字在数组中出现一次/多次)。

边界值测试(查找数组中的最大值、最小值,数组中只有一个数字)。

36.丑数

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

考点:数组

思路:同《九章算法》CHAPTER EIGHT 第十六题 丑数II

代码:

import java.util.ArrayList;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if (index == 0) {
            return 0;
        }
        ArrayList<Integer> uglys = new ArrayList<Integer>();
        uglys.add(1);
        int p2 = 0;
        int p3 = 0;
        int p5 = 0;
        for (int i = 1; i < index; i++) {
            int lastNumber = uglys.get(i - 1);
            while (uglys.get(p2) * 2 <= lastNumber) {
                p2++;
            }
            while (uglys.get(p3) * 3 <= lastNumber) {
                p3++;
            }
            while (uglys.get(p5) * 5 <= lastNumber) {
                p5++;
            }
            uglys.add(Math.min(
                Math.min(uglys.get(p2) * 2, uglys.get(p3) * 3),
                uglys.get(p5) * 5
            ));
        }
        return uglys.get(index - 1);
    }
}
View Code

测试用例:

功能测试(输入2、3、4、5、6等)。

特殊输入测试(边界值1、无效输入0)。

性能测试(输入较大的数字,如1500)。

37.和为S的两个数字

 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。对应每个测试案例,输出两个数,小的先输出。

考点:知识迁移能力

思路:数列满足递增,设头尾两个指针i和j,若ai + aj == sum,就是答案(相差越远乘积越小);若ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j -= 1

若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i += 1。时间复杂度为o(n)。

代码:

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (array == null || array.length < 2) {
            return list;
        }
        int i = 0;
        int j = array.length-1;
        while (i < j) {
            if (array[i] + array[j] == sum) {
                list.add(array[i]);
                list.add(array[j]);
                return list;
            } else if (array[i] + array[j] > sum) {
                j--;
            } else {
                i++;
            }
        }
        return list;
    }
}
View Code

测试用例:

功能测试(数组中存在和为s的两个数,数组找那个不存在和为s的两个数)

特殊输入测试(表示数组的指针为NULL指针)

38.左旋转字符串

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

考点:知识迁移能力

思路:三次翻转法

代码:

public class Solution {
    public String LeftRotateString(String str,int n) {
        char[] chars = str.toCharArray();        
        if(chars.length < n) {
            return "";
        }
        reverse(chars, 0, n-1);
        reverse(chars, n, chars.length-1);
        reverse(chars, 0, chars.length-1);
        return String.valueOf(chars);
    }
    
    public void reverse(char[] chars,int low,int high){
        char temp;
        while(low < high){
            temp = chars[low];
            chars[low] = chars[high];
            chars[high] = temp;
            low++;
            high--;
        }
    }
}
View Code

测试用例:

功能测试(把长度为n的字符串左旋转0个字符、1个字符、2个字符、n-1个字符、n个字符、n+1个字符)

特殊输入测试(字符串的指针为NULL指针)

39.整数中1出现的次数(从1到n整数中1出现的次数)

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

考点;

思路:

代码:

测试用例:

 

40.平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

考点:知识迁移能力

思路:同《九章算法》CHAPTER THREE 第八题 平衡二叉树

代码:

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        return maxDepth(root) != -1;
    }
    
    private int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
            return -1;
        }
        return Math.max(left, right) + 1;
    }
}
View Code