1.二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
考点:查找
思路:类search 2D Matrix II的解题思路
代码:
public class Solution { public boolean Find(int target, int [][] array) { if (array == null || array.length == 0) { return false; } if (array[0] == null || array[0].length == 0) { return false; } int m = array.length; int n = array[0].length; int x = m - 1; int y = 0; while (x >= 0 && y < n) { if (target == array[x][y]) { return true; } else if (target < array[x][y]) { x--; } else { y++; } } return false; } }
测试用例:
二维数组中包含查找的数字(查找的数字是数组中的最大值和最小值,介于数组中的最大值和最小值之间)。
二维数组中没有查找的数字(查找的数字大于数组中的最大值,小于数组中的最小值, 在数组的最大值和最小值之间但数组中没有这个数字)。
特殊输入测试(输入空指针)
2.替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
考点:字符串
思路:CC150 数组和字符串部分 第四题
代码:
public class Solution { public String replaceSpace(StringBuffer str) { String c = str.toString(); int length = c.length(); char[] array = c.toCharArray(); int spaceCount = 0; for (int i = 0; i < length; i++) { if (array[i] == ' ') { spaceCount++; } } int newLength = length + 2 * spaceCount; char[] newArray = new char[newLength]; int index = newLength - 1; for (int i = length - 1; i >= 0; i--) { if (array[i] == ' ') { newArray[index--] = '0'; newArray[index--] = '2'; newArray[index--] = '%'; } else { newArray[index--] = array[i]; } } return String.valueOf(newArray); } }
测试用例:
输入的字符串中包含空格(空格位于字符串的最前面,空格位于字符串的最后面,空格位于字符串的中间,字符串中有连续多个空格)。
输入的字符串中没有空格。
特殊输入测试(字符串是个NULL指针、字符串是个空字符串、字符串只有一个空格字符、字符串中只有连续多个空格)。
3.从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
考点:链表
思路:
(1)使用Collections的reverse方法,直接将list反转。
import java.util.ArrayList; import java.util.Collections; public class Solution { public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { ArrayList<Integer> list = new ArrayList<Integer>(); while (listNode != null) { list.add(listNode.val); listNode = listNode.next; } Collections.reverse(list); return list; } }
(2)借助堆栈的“后进先出”实现。
import java.util.ArrayList; import java.util.Stack; public class Solution { public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { Stack<Integer> stack=new Stack<Integer>(); while(listNode!=null){ stack.push(listNode.val); listNode=listNode.next; } ArrayList<Integer> list=new ArrayList<Integer>(); while(!stack.isEmpty()){ list.add(stack.pop()); } return list; } }
测试用例:
功能测试(输入的链表有多个结点,输入的链表只有一个结点)。
特殊输入测试(输入的链表头结点指针为NULL)。
4.跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
考点:递归和循环
思路:同 《九章算法》CHAPTER NINE 第五题 爬楼梯
代码:
public class Solution { public int JumpFloor(int target) { if (target <= 1) { return 1; } int[] f = new int[target + 1]; f[0] = 1; f[1] = 1; for (int i = 2; i < target + 1; i++) { f[i] = f[i - 1] + f[i - 2]; } return f[target]; } }
测试用例:
功能测试(输入3、5、10等)。
边界值测试(输入0、1、2等)。
性能测试(输入较大的数字,40、50、100等)。
5.变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
考点:递归和循环
思路:关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:
f(1) = 1 f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数 f(3) = f(3-1) + f(3-2) + f(3-3) ... f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n)
说明:
1)这里的f(n) 代表的是n个台阶有一次1,2,...n阶的 跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
3) n = 2时,会有两个跳的方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2)
4) n = 3时,会有三种跳的方式,1阶、2阶、3阶,
那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)
因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
5) n = n时,会有n种跳的方式,1阶、2阶...n阶,得出结论:
f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)
6) 由以上已经是一种结论,但是为了简单,我们可以继续简化:
f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1)
可以得出: f(n) = 2*f(n-1)
7) 得出最终结论,在n阶台阶,一次有1、2、...n阶的跳的方式时,总的跳法为:
f(n) = | 1 ,(n<=1 )
代码:
public class Solution { public int JumpFloorII(int target) { if (target <= 1) { return 1; } else { return 2 * JumpFloorII(target - 1); } } }
6.斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39。
考点:递归和循环
思路:从下往上计算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3).......以此类推就可以算出第n项了。时间复杂度是o(n)。
代码:
public class Solution { public int Fibonacci(int n) { if (n <= 1) { return n; } int[] f = new int[n + 1]; f[0] = 0; f[1] = 1; for (int i = 2; i < n + 1; i++) { f[i] = f[i - 1] + f[i - 2]; } return f[n]; } }
7.用两个栈实现队列
考点:栈和队列
思路:同《九章算法》CHAPTER EIGHT 第二题 用两个栈实现队列
代码:
import java.util.Stack; public class Solution { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); private void stack2ToStack1() { while (!stack2.isEmpty()) { stack1.push(stack2.pop()); } } public void push(int node) { stack2.push(node); } public int pop() { if (stack1.isEmpty()) { this.stack2ToStack1(); } return stack1.pop(); } }
测试用例:
往空的队列里添加、删除元素。
往非空的队列里添加、删除元素。
连续删除元素直至队列为空。
8.旋转数字的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
考点:查找
思路:同《九章算法》CHAPTER TWO 第六题 寻找旋转排序数组中的最小值
代码:
import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if (array.length == 0 || array == null) { return 0; } int start = 0; int end = array.length - 1; int target = array[end]; while (start + 1 < end) { int mid = start + (end - start) / 2; if (array[mid] <= target) { end = mid; } else { start = mid; } } if (array[start] <= target) { return array[start]; } else { return array[end]; } } }
测试用例:
功能测试(输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字)。
边界值测试(输入的数组是一个升序排序的数组,只包含一个数字的数组)。
特殊输入测试(输入NULL指针)。
9.二进制中1的个数
考点:位运算
思路:如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
代码:
public class Solution { public int NumberOf1(int n) { int count = 0; while (n != 0) { n = n&(n-1); count++; } return count; } }
测试用例:
正数(包括边界值1、0x7FFFFFFF)。
负数(包括边界值0x80000000、0xFFFFFFFF)。
0。
10.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
考点:树
思路:前序遍历:根->左->右 中序遍历:左->根->右 首先可以在前序遍历中找到根节点即1,然后在中序遍历中找到1的位置,此后在中序遍历中1的左边即为左子树,1的右边即为右子树的内容。此后用相同的思路进行迭代即可重建此二叉树。
代码:
public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { TreeNode root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1); return root; } private TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) { if (startPre > endPre || startIn > endIn) { return null; } TreeNode root = new TreeNode(pre[startPre]); for (int i = startIn; i <=endIn; i++) { if (in[i] == pre[startPre]) { root.left = reConstructBinaryTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1); root.right = reConstructBinaryTree(pre, startPre + i - startIn + 1, endPre, in, i + 1, endIn); break; } } return root; } }
测试用例:
普通二叉树(完全二叉树,不完全二叉树)。
特殊二叉树(所有结点都没有右子结点的二叉树,所有结点都没有左子结点的二叉树,只有一个结点的二叉树)。
特殊输入测试(二叉树的根节点指针为NULL、输入的前序遍历序列和中序遍历序列不匹配)。
11.链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
考点:链表
思路:两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指针走(k-1)步,到达第k个结点。然后两个指针同时往后移动,当第一个指针到达末尾的时候,第二个指针所在位置就是倒数第k个结点了。
代码:
public class Solution { public ListNode FindKthToTail(ListNode head,int k) { if (head == null || k <= 0) { return null; } ListNode pre = head; ListNode last = head; for (int i = 1; i < k; i++) { if (pre.next != null) { pre = pre.next; } else { return null; } } while (pre.next != null) { pre = pre.next; last = last.next; } return last; } }
测试用例:
功能测试(第k个结点在链表的中间,第k个结点是链表的头结点,第k个结点是链表的尾结点)。
特殊输入测试(链表头结点为NULL指针,链表的结点总数少于k,k等于0)。要处理这三种情况,增强代码的鲁棒性。
12.矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法。
考点:递归和循环
思路:依旧是斐波那契数列。2*n的大矩形,和n个2*1的小矩形,其中target*2为大矩阵的大小。
√ | |||||||
√ |
第一次摆放一块1*2的小矩阵,则摆放方法总共为f(target-2)因为,摆放了一块1*2的小矩阵(用√√表示),对应下方的1*2(用××表示)摆放方法就确定了,所以为f(targte-2)
√ | √ | ||||||
× | × |
代码:
public class Solution { public int RectCover(int target) { if (target <= 0) { return 0; } else if (target <= 2) { return target; } else { return RectCover(target - 1) + RectCover(target - 2); } } }
13.调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
考点:数组
思路:从题目得出的信息:相对位置不变--->保持稳定性;奇数位于前面,偶数位于后面 --->存在判断,挪动元素位置;
插入排序:
public class Solution { public void reOrderArray(int [] array) { int m = array.length; int k = 0;//记录已经摆好位置的奇数的个数 for (int i = 0; i < m; i++) { if (array[i] % 2 == 1) { int j = i; while (j > k) {//j >= k+1 int tmp = array[j]; array[j] = array[j-1]; array[j-1] = tmp; j--; } k++; } } } }
冒泡排序:
链接:https://www.nowcoder.com/questionTerminal/beb5aa231adc45b2a5dcc5b62c93f593 来源:牛客网 public class Solution { public void reOrderArray(int [] array) { for(int i=0;i<array.length-1;i++) for(int j=0;j<array.length-i-1;j++){ if(array[j]%2==0 && array[j+1]%2==1){ int temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
空间换时间:开辟两个新的数组odd和even
链接:https://www.nowcoder.com/questionTerminal/beb5aa231adc45b2a5dcc5b62c93f593 来源:牛客网 public class Solution { public void reOrderArray(int [] array) { int[] odd = new int[array.length]; int[] even = new int[array.length]; int evenCount=0; int oddCount=0; for(int i=0;i<array.length;i++){ if(array[i]%2==0){ even[evenCount] = array[i]; evenCount++; }else{ odd[oddCount] = array[i]; oddCount++; } } for(int i=0;i<oddCount;i++){ array[i] = odd[i]; } for(int i=0;i<evenCount;i++){ array[i+oddCount] = even[i]; } } }
测试用例:
功能测试(输入数组中的奇数、偶数交替出现,输入的数组中所有偶数都出现在奇数的前面,输入的数组中所有奇数都出现在偶数的前面)。
特殊输入测试(输入NULL指针、输入的数组只包含一个数字)。
14.数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
考点:代码的完整性
思路:
1.当底数为0且指数<0时,
会出现对0求倒数的情况,需进行错误处理,设置一个全局变量;
2.判断底数是否等于0,
由于base为double型,不能直接用==判断
3.优化求幂函数,
当n为偶数,a^n =(a^n/2)*(a^n/2),
当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a
时间复杂度O(logn)
public class Solution { public double Power(double base, int exponent) { int n = Math.abs(exponent); if (n == 0) return 1; if (n == 1) return base; double result = Power(base, n >> 1); result *= result; if ((n&1) == 1) result *= base; if (exponent < 0) result = 1 / result; return result; } }
测试用例:
把底数和指数分别设为正数、负数和零。
15.反转链表
输入一个链表,反转链表后,输出链表的所有元素。
考点:链表
思路:同《九章算法》CHAPTER SIX 第二题 翻转链表
代码:
public class Solution { public ListNode ReverseList(ListNode head) { ListNode prev = null; ListNode curt = head; while (curt != null) { ListNode temp = curt.next; curt.next = prev; prev = curt; curt = temp; } return prev; } }
测试用例:
功能测试(输入的链表含有多个结点,链表中只有一个结点)。
特殊输入测试(链表头结点为NULL指针)。
16.合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
考点:链表
思路:同《九章算法》CHAPTER SIX 第五题 合并两个排序链表
代码:
public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { ListNode dummy = new ListNode(0); ListNode head = dummy; while (list1 != null && list2 != null) { if (list1.val < list2.val) { head.next = list1; list1 = list1.next; } else { head.next = list2; list2 = list2.next; } head = head.next; } if (list1 != null) { head.next = list1; } else { head.next = list2; } return dummy.next; } }
测试用例:
功能测试(输入的两个链表有多个结点,结点的值互不相同或者存在值相等的多个结点)。
特殊输入测试(两个链表的一个或者两个头结点为NULL指针、两个链表中只有一个结点)。
17.二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5
考点:面试思路
思路:先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子结点。当交换完所有非叶子结点的左右子结点之后,就得到了树的镜像。
代码:
递归版本:
public class Solution { 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; if (root.left != null) { Mirror(root.left); } if (root.right != null) { Mirror(root.right); } } }
非递归版本:
链接:https://www.nowcoder.com/questionTerminal/564f4c26aa584921bc75623e48ca3011 来源:牛客网 import java.util.Stack; public class Solution { public void Mirror(TreeNode root) { if(root == null){ return; } Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root); while(!stack.isEmpty()){ TreeNode node = stack.pop(); if(node.left != null||node.right != null){ TreeNode temp = node.left; node.left = node.right; node.right = temp; } if(node.left!=null){ stack.push(node.left); } if(node.right!=null){ stack.push(node.right); } } } }
测试用例:
功能测试(普通的二叉树,二叉树的所有结点都没有左子树或者右子树,只有一个结点的二叉树)。
特殊输入测试(二叉树的根结点为NULL指针)。
18.树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)。
考点:代码的鲁棒性
思路:
第一步在树A中找到和B的根节点的值一样的结点R:
当A和B都不为零的时候,才进行比较,否则直接返回false。如果在树A中找到了对应B的根节点的结点R,以这个根节点R为起点判断是否包含B;如果找不到,那么就再去A中R的左儿子当作起点,去判断是否包含B;如果还找不到,那么就再去A中R的右儿子当作起点,去判断是否包含B。
第二步再判断树A中以R为根节点的子树是不是包含和树B一样的结构:
如果树B已经遍历完了都能对应的上,返回true;如果B还没有遍历完,A却遍历完了,返回false;如果其中有一个点没有对应上,返回false;如果根节点对应的上,就分别去子节点里面匹配。
代码:
public class Solution { public boolean HasSubtree(TreeNode root1,TreeNode root2) { boolean result = false; //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false if (root2 != null && root1 != null) { //如果找到了对应Tree2的根节点的点 if (root1.val == root2.val) { //以这个根节点为为起点判断是否包含Tree2 result = doesTree1HaveTree2(root1, root2); } //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2 if (!result) { result = HasSubtree(root1.left, root2); } //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2 if (!result) { result = HasSubtree(root1.right, root2); } } //返回结果 return result; } public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) { //如果Tree2已经遍历完了都能对应的上,返回true if (node2 == null) { return true; } //如果Tree2还没有遍历完,Tree1却遍历完了。返回false if (node1 == null) { return false; } //如果其中有一个点没有对应上,返回false if (node1.val != node2.val) { return false; } //如果根节点对应的上,那么就分别去子节点里面匹配 return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right); } }
测试用例:
功能测试(树A和树B都是普通的二叉树,树B是或者不是树A的子结构)。
特殊输入测试(两棵二叉树的一个或者两个根节点为NULL指针、二叉树的所有结点都没有左子树或者右子树)。
19.从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
考点:举例让抽象具体化
思路:二叉树的层次遍历
代码:
import java.util.ArrayList; import java.util.Queue; import java.util.LinkedList; public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> result = new ArrayList<Integer>(); if (root == null) { return result; } Queue<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); while (!queue.isEmpty()) { TreeNode treeNode = queue.poll(); if (treeNode.left != null) { queue.offer(treeNode.left); } if (treeNode.right != null) { queue.offer(treeNode.right); } result.add(treeNode.val); } return result; } }
20.栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
考点:栈
思路:建立一个辅助栈,把输入的第一个序列中的数字依次压入该辅助栈,并按照第二个序列的顺序依次从该栈中弹出数字。
判断一个序列是不是栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。如果下一个弹出的数字不在栈顶,把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。如果所有的数字都压入栈了仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。
代码:
import java.util.ArrayList; import java.util.Stack; public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { if (pushA.length == 0 || popA.length == 0) { return false; } Stack<Integer> stack = new Stack<Integer>(); int j = 0; for (int i = 0; i < popA.length; i++) { stack.push(pushA[i]); while (j < popA.length && stack.peek() == popA[j]) { stack.pop(); j++; } } return stack.empty() ? true : false; } }
测试用例:
功能测试(输入的两个数组含有多个数字或者只有1个数字,第二个数组是或者不是第一个数组表示的压入序列对应的栈的弹出序列)
特殊输入测试(输入两个NULL指针)