41.把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
考点:时间效率
思路:
代码:
42.翻转单词顺序列
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
考点:知识迁移能力
思路:先翻转整个句子中所有的字符,然后依次翻转每个单词中字符的顺序。
依据空格来确定单词的起始和终止位置。在翻转每个单词的阶段,begin指向单词的第一个字符,end指向单词的最后一个字符。
代码:
public class Solution { public String ReverseSentence(String str) { if (str == null || str.trim().equals("")) { return str; } char[] c = str.toCharArray(); reverse(c, 0, str.length() - 1); int begin = 0; int end = 0; while (begin != c.length) { if (c[begin] == ' ') { begin++; end++; } else if (c[end] == ' ') { reverse(c, begin, --end); begin = ++end; } else if (end == c.length - 1) { reverse(c, begin, end); begin = ++end; } else { end++; } } return String.valueOf(c); } private void reverse(char[] c, int begin, int end) { while (begin < end) { char temp = c[begin]; c[begin] = c[end]; c[end] = temp; begin++; end--; } } }
测试用例:
功能测试(句子中有多个单词,句子中只有一个单词)
特殊输入测试(字符串指针为NULL指针、字符串的内容为空、字符串中只有空格)
43.二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
考点:分解让复杂问题变简单
思路:
(1)非递归版:核心是中序遍历的非递归算法,修改当前遍历节点(curt)与前一遍历节点(pre)的指针指向。
import java.util.*; public class Solution { public TreeNode Convert(TreeNode pRootOfTree) { if (pRootOfTree == null) { return null; } Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode curt = pRootOfTree; TreeNode pre = null;//用于保存中序遍历序列的上一个节点 boolean isFirst = true; while (curt != null || !stack.isEmpty()) { while (curt != null) { stack.push(curt); curt = curt.left; } curt = stack.pop(); if (isFirst) { pRootOfTree = curt;//将中序遍历序列中的第一个节点记为root pre = pRootOfTree; isFirst = false; } else { pre.right = curt; curt.left = pre; pre = curt; } curt = curt.right; } return pRootOfTree; } }
(2)递归版:中序遍历
public class Solution { TreeNode head = null; TreeNode realHead = null; public TreeNode Convert(TreeNode pRootOfTree) { //递归调用遇到叶子节点的左右子节点时返回null if (pRootOfTree == null) { return null; } //第一次运行时,会使最左边叶子节点为链表第一个节点 Convert(pRootOfTree.left); if (head == null) { head = pRootOfTree; realHead = pRootOfTree; } else { //把根节点插入到双向链表右边,head向后移动 head.right = pRootOfTree; pRootOfTree.left = head; head = pRootOfTree; } //把右叶子节点也插入到双向链表 Convert(pRootOfTree.right); return realHead; } }
测试用例:
功能测试(输入的二叉树是完全二叉树,所有结点都没有左/右子树的二叉树,只有一个结点的二叉树)。
特殊输入测试(指向二叉树根节点的指针为NULL指针)。
44.数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
考点:数组
思路:
解法一:将输入的数组进行排序,遍历排序后的数组找到重复的数字。排序一个长度为n的数字的时间复杂度为O(nlogn),所以这种方法的时间复杂度为O(nlogn)。
解法二:使用哈希表来解决这个问题。从头到尾顺序扫描数组中的每一个数,没扫描一个数字可以用O(1)的时间在哈希表中判断是否包含此数字,如果哈希表中没有此数字就将此数字加入到哈希表中,如果哈希表中已存在此数字就找到重复的数字。时间复杂度为O(n),但是空间复杂度也为O(n),以空间换时间。
解法三:数组中的数字为0到n-1的范围内。如果这个数组中没有重复的数字,则对应的i位置的数据也为i。可以重排此数组,扫描数组中的每一个数字,当扫描到下标为i的数字时,首先比较这个数字(m)是不是等于i。如果是,接着扫描下一个数字。如果不是,再拿它和第m个数字比较,如果相等则找到重复的数据。否则就把第i个数字与第m个数字交换。重复这个比较、交换的过程,直到找到重复的数字。
代码:
public class Solution { // Parameters: // numbers: an array of integers // length: the length of array numbers // duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation; // Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++ // 这里要特别注意~返回任意重复的一个,赋值duplication[0] // Return value: true if the input is valid, and there are some duplications in the array number // otherwise false public boolean duplicate(int numbers[],int length,int [] duplication) { if (numbers == null || length <= 0) { return false; } for (int i = 0; i < length; i++) { //判断输入数组的合法性 if (numbers[i] < 0 || numbers[i] > length - 1) { return false; } } for (int i = 0; i < length; i++) { while (i != numbers[i]) { if (numbers[i] == numbers[numbers[i]]) { duplication[0] = numbers[i]; return true; } int temp = numbers[i]; numbers[i] = numbers[numbers[i]]; numbers[numbers[i]] = temp; } } return false; } }
45.不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
考点:发散思维能力
思路:
首先看十进制是如何做的: 5+7=12,三步走 第一步:相加各位的值,不算进位,得到2。 第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。 第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。
同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。 第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。 第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
代码:
public class Solution { public int Add(int num1,int num2) { while (num2 != 0) { int sum = num1 ^ num2; int carry = (num1&num2)<<1; num1 = sum; num2 = carry; } return num1; } }
测试用例:
输入正数、负数和0.
46.和为S的连续正数序列
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? 输出描述:输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
考点:知识迁移能力
思路:
初始化small=1,big=2;如果从
small到big序列和小于sum,big++;大于sum,small++;等于sum,samll++big++;因为这个序列至少要有两个数字,
small增加到(1+sum)/2为止。
代码:
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); if (sum <= 1) { return lists; } int small = 1; int big = 2; while (small != (1 + sum) / 2) { //当small==(1+sum)/2的时候停止 int curSum = sumOfList(small, big); if (curSum == sum){ ArrayList<Integer> list = new ArrayList<Integer>(); for (int i = small; i <= big; i++){ list.add(i); } lists.add(list); small++; big++; } else if (curSum < sum){ big++; } else { small++; } } return lists; } public int sumOfList(int head,int leap) { //计算当前序列的和 int sum = head; for (int i = head + 1; i <= leap; i++) { sum += i; } return sum; } }
测试用例:
功能测试(存在和为s的连续序列,如9、100等;不存在和为s的连续序列,如4、0)。
边界值测试(连续序列的最小和3)
47.删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
考点:链表
思路:CC150 链表部分 1.2题
48.把字符串转换成整数
输出 2147483647 0
考点:字符串
思路:模拟题,先处理空格,然后处理符号,最后判断字符串中是不是有非数字。
代码:
public class Solution { public int StrToInt(String str) { if (str.equals("") || str.length() == 0) { return 0; } char[] a = str.toCharArray(); int fuhao = 0; if (a[0] == '-') { fuhao = 1; } int sum = 0; for (int i = fuhao; i < a.length; i++) { if (a[i] == '+') { continue; } if (a[i] < 48 || a[i] > 57) { return 0; } sum = sum * 10 + a[i] - 48; } return fuhao == 0 ? sum : sum * -1; } }
测试用例:
功能测试(输入的字符串表示正数、负数和0)。
边界值测试(最大的正整数、最小的负整数)。
特殊输入测试(输入字符串为NULL指针、输入字符串为空字符串、输入的字符串中有非数字字符等)。
49.数组中逆序对
public class Solution { public int InversePairs(int [] array) { if (array == null || array.length == 0) { return -1; } int[] temp= new int[array.length]; long res = getResult(array, temp, 0, array.length - 1); return (int)res%1000000007; } public long getResult(int[] array, int[] temp, int begin, int end) { if (begin >= end) { return 0; } int mid = (begin + end) / 2; long left = getResult(array, temp, begin, mid); long right = getResult(array, temp, mid + 1, end); long count = build(array, temp, begin, end); return count = (count + right + left) % 1000000007; } public long build(int[] array, int[] temp, int begin, int end){ int mid = (begin + end) / 2; int i = begin; int j = mid + 1; long count = 0; int te = begin; while (i <= mid && j <= end) { if (array[i] < array[j]) { temp[te++] = array[i++]; } else { temp[te++] = array[j++]; count += (mid - i + 1) % 1000000007; } } while (i <= mid) { temp[te++] = array[i++]; } while (j <= end) { temp[te++] = array[j++]; } for (i = begin; i <= end; i++){ array[i] = temp[i]; } return count % 1000000007; } }
注意:每次返回之前都要先取余!!!
public class Solution {
public int InversePairs(int [] array) {
...
return (int)res%1000000007;
}
public long getResult(int[] array, int[] temp, int begin, int end) {
...
return count = (count + right + left) % 1000000007;
}
public long build(int[] array, int[] temp, int begin, int end){
while (i <= mid && j <= end) {
if (array[i] < array[j]) {
temp[te++] = array[i++];
} else {
....
count += (mid - i + 1) % 1000000007;
}
}
...
return count % 1000000007;
}
}
测试用例:
50.链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
考点:链表
思路:同《九章算法》CHAPTER SIX 第十一题 带环链表 II
代码:
public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { if (pHead == null || pHead.next == null) { return null; } ListNode slow = pHead; ListNode fast = pHead.next; while (slow != fast) { if (fast == null || fast.next == null) { return null; } slow = slow.next; fast = fast.next.next; } slow = pHead; fast = fast.next; while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; } }
51.孩子们的游戏(圆圈中最后剩下的数)
其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
考点:模拟
思路:【约瑟夫环】分析每次被删除的数字规律并直接计算出圆圈中最后剩下的数字。
代码:
import java.util.*; public class Solution { public int LastRemaining_Solution(int n, int m) { if(n == 0 || m == 0) { return -1; } int index = 0; LinkedList<Integer> linkedList = new LinkedList<>(); for (int i = 0; i < n; i++) { linkedList.add(i); } int size = linkedList.size(); while (size > 1) { linkedList.remove((index+m-1)%size); index = (index+m-1)%size; size--; } return linkedList.get(0); } }
测试用例:
功能测试(输入的m小于n,比如从最初有5个数字的圆圈删除每次第2、3个数字;输入的m大于或者等于n,比如从最初有6个数字的圆圈删除每次第6、7个数字)。
特殊输入测试(圆圈中有0个数字)。
性能测试(从最初有4000个数字的圆圈中每次删除第997个数字)。
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]。不能使用除法。
考点:数组
思路:
(1)B[0] = A[1] * A[2] * A[3] * A[4] *....*A[n-1] ;(没有A[0])
import java.util.ArrayList; public class Solution { public int[] multiply(int[] A) { int[] B = new int[A.length]; boolean changed = false; for (int i = 0; i < B.length; i++) { B[i] = 1; for (int j = 0; j < A.length; j++) { int temp = 1; if (j == i) { temp = A[j]; A[j] = 1; changed = true; } B[i] *= A[j]; if (changed) { A[j] = temp; changed = false; } } } return B; } }
(2)分两步:1.计算前i - 1个元素的乘积,及后N - i个元素的乘积分别保存在两个数组中。这可以用动态规划实现。 2.计算B[i]的值。
import java.util.ArrayList; public class Solution { public int[] multiply(int[] A) { int len = A.length; int forword[] = new int[len]; int backword[] = new int[len]; int B[] = new int[len]; forword[0] = 1; backword[0] = 1; for(int i = 1;i < len; i++){ forword[i] = A[i - 1]*forword[i-1]; backword[i] = A[len - i]*backword[i - 1]; } for(int i = 0; i < len; i++){ B[i] = forword[i] * backword[len - i -1]; } return B; } }
53.扑克牌顺子
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的运气如何。为了方便起见,你可以认为大小王是0。
考点:字符串
思路:顺子的特性:最大值和最小值之差绝对为4!首先把数组排序,再统计数组中0的个数,最后统计排序之后的数组中相邻数字之间的空缺总数(后面一个数字比前面一个数字大1以上)。如果空缺的总数小于或者等于0的个数,那么这个数组就是连续的;反之则不连续。要注意:如果数组中的非0数字重复出现,则该数组不是连续的。
代码:
import java.util.Arrays; public class Solution { public boolean isContinuous(int [] numbers) { int numOfZero = 0; int numOfInterval = 0; int length = numbers.length; if (length == 0) { return false; } Arrays.sort(numbers); for (int i = 0; i < length - 1; i++) { if (numbers[i] == 0) { numOfZero++; continue; } //有对子,不可能是顺子 if (numbers[i] == numbers[i + 1]) { return false; } numOfInterval += numbers[i + 1] - numbers[i] - 1; } if (numOfZero >= numOfInterval) { return true; } return false; } }
测试用例:
功能测试(抽出的牌中有一个或者多个大、小王,抽出的牌中没有大、小王,抽出的牌中有对子)。
特殊输入测试(输入NULL指针)。
54.对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
考点:树
思路:根节点以及其左右子树、左子树的左子树和右子树的右子树、
左子树的右子树和右子树的左子树相同即可,采用递归非递归都行
,采用栈或队列存取各级子树根节点。
代码:递归版本
public class Solution { boolean isSymmetrical(TreeNode pRoot) { if (pRoot == null) { return true; } return comRoot(pRoot.left, pRoot.right); } private boolean comRoot(TreeNode left, TreeNode right) { // TODO Auto-generated method stub if (left == null) return right==null; if (right == null) return false; if (left.val != right.val) return false; return comRoot(left.right, right.left) && comRoot(left.left, right.right); } }
非递归版本
public class Solution { boolean isSymmetrical(TreeNode pRoot) { if(pRoot == null){ return true; } Stack<TreeNode> stack1 = new Stack<TreeNode>(); Stack<TreeNode> stack2 = new Stack<TreeNode>(); stack1.push(pRoot.left); stack2.push(pRoot.right); while(!stack1.isEmpty() && !stack2.isEmpty()){ TreeNode node1 = stack1.pop(); TreeNode node2 = stack2.pop(); if(node1 == null && node2 == null){ continue; } if(node1 == null || node2 == null){ return false; } if(node1.val != node2.val){ return false; } stack1.push(node1.left); stack1.push(node1.right); stack2.push(node2.right); stack2.push(node2.left); } return true; } }
55.二叉树的下一个结点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
考点:树
思路:
如果一个结点有右子树,它的下一个结点是它的右子树中的最左子节点。
如果一个结点没有右子树,沿着父节点的指针向上遍历,找到一个是它父节点的左子节点的结点,这个结点的父节点就是下一个结点。
代码:
public class Solution { public TreeLinkNode GetNext(TreeLinkNode pNode) { if (pNode == null) return null; if (pNode.right != null) { pNode = pNode.right; while (pNode.left != null) { pNode = pNode.left; } return pNode; } while (pNode.next != null) { if (pNode.next.left == pNode) { return pNode.next; } pNode = pNode.next; } return null; } }
56.把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
考点:树
思路:二叉树的层次遍历
代码:
import java.util.ArrayList; import java.util.Queue; import java.util.LinkedList; public class Solution { ArrayList<ArrayList<Integer> >Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>(); if (pRoot == null) { return results; } Queue<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(pRoot); while (!queue.isEmpty()) { ArrayList<Integer> level = new ArrayList<Integer>(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } results.add(level); } return results; } }
57.二叉搜索树的第k个结点
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
考点:树
思路:中序遍历一棵二叉搜索树,遍历序列的数值是递增的,因此只需要中序遍历一棵二叉搜索树,就很容易找出它的第K大结点。
代码:
public class Solution { TreeNode KthNode(TreeNode pRoot, int k) { if (pRoot == null || k == 0) { return null; } int count = 0; Stack<TreeNode> stack = new Stack<>(); while (pRoot != null || ! stack.isEmpty()) { while (pRoot != null) { stack.push(pRoot); pRoot = pRoot.left; } pRoot = stack.pop(); count++; if (count == k) { return pRoot; } pRoot = pRoot.right; } return null; } }
58.字符流中第一个不重复的字符
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。输出描述:如果当前字符流没有存在出现一次的字符,返回#字符。
考点:字符串
思路:
使用一个HashMap<Character, Integer>来统计字符出现的次数,同时用一个ArrayList来记录输入流,每次返回第一个出现一次的字符都是在这个ArrayList(输入流)中的字符作为key去map中查找。
代码:
import java.util.*; public class Solution { HashMap<Character, Integer> map = new HashMap(); ArrayList<Character> list = new ArrayList<Character>(); //Insert one char from stringstream public void Insert(char ch) { if (map.containsKey(ch)) { map.put(ch, map.get(ch) + 1); } else { map.put(ch, 1); } list.add(ch); } //return the first appearence once char in current stringstream public char FirstAppearingOnce() { char c = '#'; for (char key : list) { if (map.get(key) == 1) { c = key; break; } } return c; } }
测试用例:
功能测试(字符串中存在只出现一次的字符,字符串中不存在只出现一次的字符,字符串中所有字符都只出现一次)。
特殊输入测试(字符串为NULL指针)。
59.按之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
考点:树
思路:同《九章算法》CHAPTER FOUR 第三题
二叉树的锯齿形遍历 使用Stack(栈)先进后出的特征,curtLevel和nextLevel交替使用,normalOrder变量用于标记是否是正常顺序(从左到右)。初始时为从左至右的顺序,下一层就应该先放入左儿子,再放入右儿子。由于栈的先进后出特性,右儿子先出来。
代码:
import java.util.*; /* public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>(); if (pRoot == null) { return results; } Stack<TreeNode> curtLevel = new Stack<TreeNode>(); Stack<TreeNode> nextLevel = new Stack<TreeNode>(); Stack<TreeNode> temp; curtLevel.push(pRoot); boolean normalOrder = true; while (!curtLevel.empty()) { ArrayList<Integer> curtLevelResult = new ArrayList<Integer>(); while (!curtLevel.empty()) { TreeNode node = curtLevel.pop(); curtLevelResult.add(node.val); if (normalOrder) { if (node.left != null) { nextLevel.push(node.left); } if (node.right != null) { nextLevel.push(node.right); } } else { if (node.right != null) { nextLevel.push(node.right); } if (node.left != null) { nextLevel.push(node.left); } } } results.add(curtLevelResult); temp = curtLevel; curtLevel = nextLevel; nextLevel = temp; normalOrder = !normalOrder; } return results; } }
60.滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
考点:栈和队列
思路:
单调递减队列是最佳的做法。那么什么是单调队列呢,简单说一下单调队列:如果你知道单调栈的话,那么单调队列很好理解, 单调队列的目的就是去除冗余的信息,留下那些有用的信息,举个例子,输入数组{2, 3, 4, 1, -1, -2}
,滑动窗口大小是3,那么2入
队列;3
入队列前,发现队尾元素为2
,于是弹出2
,3
入队;4
入队前发现队尾元素是3
,于是弹出3
,4
入队;1
入队;-1
入队;-2
入队,以上就是单调队列的操作过程。这里求的是固定区间长度的最大值,那么单调队列始终保持队头元素最大,队内元素依次递减,故要得到一个某一个固定长度的区间的最大值,直接取出队头元素即可。
代码:
61.表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
考点:字符串
思路:
代码:
62.正则表达式匹配
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
考点:字符串
思路:
代码:
63.机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
考点:回溯法
思路:广度优先搜索
1.从(0,0)开始走,每成功走一步标记当前位置为true,然后从当前位置往四个方向探索,返回1 + 4 个方向的探索值之和。
代码:
public class Solution { public int movingCount(int threshold, int rows, int cols) { boolean[][] visited = new boolean[rows][cols]; return countingSteps(threshold,rows,cols,0,0,visited); } public int countingSteps(int limit,int rows,int cols,int r,int c,boolean[][] visited){ if (r < 0 || r >= rows || c < 0 || c >= cols || visited[r][c] || bitSum(r) + bitSum(c) > limit) return 0; visited[r][c] = true; return countingSteps(limit,rows,cols,r - 1,c,visited) + countingSteps(limit,rows,cols,r,c - 1,visited) + countingSteps(limit,rows,cols,r + 1,c,visited) + countingSteps(limit,rows,cols,r,c + 1,visited) + 1; } public int bitSum(int t){ int count = 0; while (t != 0){ count += t % 10; t /= 10; } return count; } }
64.序列化二叉树
设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。如何反序列化或序列化二叉树是没有限制的,你只需要确保可以将二叉树序列化为一个字符串,并且可以将字符串反序列化为原来的树结构。
考点:树
思路:同《九章算法》CHAPTER FOUR 第五题
代码:
import java.util.*; public class Solution { String Serialize(TreeNode root) { if (root == null) { return "{}"; } ArrayList<TreeNode> list = new ArrayList<TreeNode>(); list.add(root); //将每个节点及其左右儿子放入list中 for (int i = 0; i < list.size(); i++) { TreeNode node = list.get(i); if (node == null) { continue; } list.add(node.left); list.add(node.right); } //删除list最后的空节点 while (list.get(list.size() - 1) == null) { list.remove(list.size() - 1); } //将list转换为string字符串 StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append(list.get(0).val); for (int i = 1; i < list.size(); i++) { if (list.get(i) == null) { sb.append(",#"); } else { sb.append(","); sb.append(list.get(i).val); } } sb.append("}"); return sb.toString(); } TreeNode Deserialize(String str) { if (str.equals("{}")) { return null; } //将字符串进行分割 String[] vals = str.substring(1, str.length() - 1).split(","); ArrayList<TreeNode> list = new ArrayList<TreeNode>(); TreeNode root = new TreeNode(Integer.parseInt(vals[0])); list.add(root); int index = 0; boolean isLeftChild = true;//标记添加到左儿子还是右儿子 for (int i = 1; i < vals.length; i++) { if (!vals[i].equals("#")) { TreeNode node = new TreeNode(Integer.parseInt(vals[i])); if (isLeftChild) { list.get(index).left = node; } else { list.get(index).right = node; } list.add(node); } //index标记当前根节点,当右儿子已添加时,就换下一个根节点。 if (!isLeftChild) { index++; } isLeftChild = !isLeftChild; } return root; } }
65.数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
考点:树
思路:
代码:
66.矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
考点:回溯法
思路:深度优先搜索
1.将matrix字符串模拟映射为一个字符矩阵(但并不实际创建一个矩阵)
代码:
public class Solution { public boolean hasPath(char[] matrix, int rows, int cols, char[] str) { int flag[] = new int[matrix.length]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (helper(matrix, rows, cols, i, j, str, 0, flag)) return true; } } return false; } private boolean helper(char[] matrix, int rows, int cols, int i, int j, char[] str, int k, int[] flag) { int index = i * cols + j; if (i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[k] || flag[index] == 1) return false; if(k == str.length - 1) return true; flag[index] = 1; if (helper(matrix, rows, cols, i - 1, j, str, k + 1, flag) || helper(matrix, rows, cols, i + 1, j, str, k + 1, flag) || helper(matrix, rows, cols, i, j - 1, str, k + 1, flag) || helper(matrix, rows, cols, i, j + 1, str, k + 1, flag)) { return true; } flag[index] = 0; return false; } }