目录
第7章
第8章
转圈打印矩阵
将正方形矩阵顺时针转动90度
之字形打印矩阵
找到无须数组中最小的k个数
需要排序的最短子数组长度
在数组中找到出现次数大于N/K的数
在行列都排好序的矩阵中找数
最长的可整合子数组的长度
不重复打印排序数组中相加和为给定值的所有二元组和三元组
未排序正数数组中累加和为给定值的
最长子数组长度
未排序数组中累加和为给定值的最长子数组系列问题
未排序数组中累加和小于或等于给定值的最长子数组长度
计算数组的小和
自然数数组的排序
奇数下标都是奇数或者偶数下标都是
偶数
子数组的最大累加和问题
子矩阵的最大累加和问题
在数组中找到一个局部最小的位置
第9章
从5随机到7随机及其扩展
一行代码求两个数的最大公约数
有关阶乘的两个问题
判断一个点是否在矩形的内部
判断一个点是否在三角形内部
折纸问题
蓄水池算法
设计有setAll功能的哈希表
最大的leftMax与rightMax之差的绝对值
设计可以变更的缓存结构
设计RandoPool结构
调整[0,x)区间上的数出现的概率
路径数组变为统计数组
正数数组的最小不可组成和
一种字符串和数字的对应关系
1到n中1出现的次数
从N个数中等概率打印M个数
判断一个数是否是回文数
在有序旋转数组中找到一个数
数字的英文表达和中文表达
分糖果问题
一种消息接收并打印的结构设计
设计一个没有扩容负担的堆结构
随时找到数据流的中位数
在两个长度相等的排序数组中找到上中位数
在两个排序数组中找到第K小的数
两个有序数组间相加和的TOP K问题
Manacher算法
KMP算法
丢棋子问题
画匠问题
邮局选址问题
第7章
第8章
转圈打印矩阵
这道题目就是剑指offer中的顺时针打印矩阵,参考剑指offer中的笔记,其实是参考的这本书中的解题思路,利用宏观的调度,不去直接考虑控制移动。
将正方形矩阵顺时针转动90度
之字形打印矩阵
这道题目和上面两个类似,同样也是考虑宏观调度的问题,不要把问题拘泥于从(0,0)开始什么时候向左下方,什么时候向右下方,什么时候越界
采用a、b两点来进行宏观的调度,每次ab之间便是一条对角线,遍历对角线数据即可,方向可以用一个flag来标志
public static void printMarow1ixZigZag(int[][] marow1ix) { //定义初始的两个变量,一开始指向(0,0)位置 int row1 = 0; int col1 = 0; int row2 = 0; int col2 = 0; int endR = marow1ix.length - 1; int endC = marow1ix[0].length - 1; //定义对角线的方向 boolean fromUp = false; while (row1 != endR + 1) { //打印对角线的数值 printLevel(marow1ix, row1, col1, row2, col2, fromUp); //控制方向的移动 row1 = col1 == endC ? row1 + 1 : row1; col1 = col1 == endC ? col1 : col1 + 1; col2 = row2 == endR ? col2 + 1 : col2; row2 = row2 == endR ? row2 : row2 + 1; //控制对角线的方向每次变换 fromUp = !fromUp; } System.out.println(); } private static void printLevel(int[][] m, int row1, int col1, int row2, int col2, boolean f) { if (f) { while (row1 != row2 + 1) { System.out.print(m[row1++][col1--] + " "); } } else { while (row2 != row1 - 1) { System.out.print(m[row2--][col2++] + " "); } } }
找到无须数组中最小的k个数
需要排序的最短子数组长度
在数组中找到出现次数大于N/K的数
在行列都排好序的矩阵中找数
这道题目的详解见剑指offer中:二维数组的查找
最长的可整合子数组的长度
不重复打印排序数组中相加和为给定值的所有二元组和三元组
未排序正数数组中累加和为给定值的
最长子数组长度
未排序数组中累加和为给定值的最长子数组系列问题
未排序数组中累加和小于或等于给定值的最长子数组长度
计算数组的小和
自然数数组的排序
奇数下标都是奇数或者偶数下标都是偶数
子数组的最大累加和问题
子矩阵的最大累加和问题
在数组中找到一个局部最小的位置
关于局部最小的定义要明确:首位置比下一个小,结尾处比上一个小即可,对于中间位置的只需要比左右两个位置小即可,如下图所示:
在查找的时候可以利用二分法进行查找,首先判断开头和结尾处是否为局部最小,若都不是则去中间位置处寻找。
从上图中可以看出,必然可以找到一个局部最小值
public static int getLessIndex(int[] arr) { if (arr == null || arr.length == 0) { return -1; // no exist } if (arr.length == 1 || arr[0] < arr[1]) { return 0; } if (arr[arr.length - 1] < arr[arr.length - 2]) { return arr.length - 1; } int left = 1; int right = arr.length - 2; int mid = 0; while (left < right) { mid = (left + right) / 2; //哪边有减小的趋势就向哪边寻找 if (arr[mid] > arr[mid - 1]) { right = mid - 1; } else if (arr[mid] > arr[mid + 1]) { left = mid + 1; } else { return mid; } } return left; }
第9章
从5随机到7随机及其扩展
一行代码求两个数的最大公约数
有关阶乘的两个问题
判断一个点是否在矩形的内部
判断一个点是否在三角形内部
折纸问题
这道题目很新颖但其本质还是二叉树问题,可以实际动手折一下,就会发现如下图的规律:
public static void printProcess(int i, int N, boolean down) { //递归的终止条件 if (i > N) { return; } //下面就是中序遍历的过程 //没有用实际的节点去遍历,层数i来代表不同的节点,用down这个标志即代表不同的节点值又模拟了遍历节点的左子树还是右子树的过程 printProcess(i + 1, N, true); //打印 System.out.println(down ? "down " : "up "); printProcess(i + 1, N, false); }
注:其实有一些提示的
(1)折了n次,那么会产生2^n-1个折痕,这和一颗二叉树其高度为n对应的节点数目相一致
(2)第一次这产生了1下,第二次折分别在上下两端产生了2下和2上,对照就是二叉树的左右子树问题
蓄水池算法
设计有setAll功能的哈希表
最大的leftMax与rightMax之差的绝对值
设计可以变更的缓存结构
设计RandoPool结构
单独从hash表上而言其增删改查操作都是O(1),这道题目的关键是如何等概率返回任何一个key,这个key可以是数字,也可以是字符串等各种类型。
用两个map来存储输入的key,当没有删除操作时0~25这些数字是连续的,可以很利用Random随机函数随机得到这些数字
当有删除时,便不连续了很难随机得到,那么有没有办法把不连续的变为连续的?当要删除b时,可以考虑把z覆盖b,25变为1,然后map的长度减一,这样即使删除了数据但仍然是连续的。
public static class Pool<K>{ private HashMap<K,Integer> keyIndexMap; private HashMap<Integer,K> indexKeyMap; private int size; public Pool(){ keyIndexMap = new HashMap<>(); indexKeyMap = new HashMap<>(); size = 0; } public void insert(K key){ if (!keyIndexMap.containsKey(key)){ keyIndexMap.put(key,size); indexKeyMap.put(size++,key); } } public void delete(K key){ if (keyIndexMap.containsKey(key)){ int deleteIndex = keyIndexMap.get(key); //因为size是从1开始计数的所以要先自减 int lastIndex = --size; K lastKey = indexKeyMap.get(lastIndex); keyIndexMap.put(lastKey,deleteIndex); indexKeyMap.put(deleteIndex,lastKey); keyIndexMap.remove(key); indexKeyMap.remove(lastIndex); } } public K getRandom(){ if (size == 0){ return null; } int randomIndex = (int)(Math.random()*size); return indexKeyMap.get(randomIndex); } }
调整[0,x)区间上的数出现的概率
路径数组变为统计数组
正数数组的最小不可组成和
一种字符串和数字的对应关系
1到n中1出现的次数
从N个数中等概率打印M个数
判断一个数是否是回文数
在有序旋转数组中找到一个数
数字的英文表达和中文表达
分糖果问题
一种消息接收并打印的结构设计
设计一个没有扩容负担的堆结构
随时找到数据流的中位数
本题与LeetCode295是一样的题目,书中给出的代码稍有繁琐,这里借鉴了LeetCode中评论区代码,但是其实思路是一样的,不同点在于具体操作。
对于数据流中的数,如果能将其存成两部分,分别用最大堆和最小堆存(堆调整时间复杂度为O(logN)),取中位数直接用堆顶元素(O(1))不就可以了
但问题在于,如何把数据分为上面的两部分,这里给出两种方式:以{7,3,4,5}为例
(1)先把第一数据存入最大堆中
(2)当第二个数据小于最大堆堆顶数据时,移动最大堆堆顶元素进入最小堆,并把第二个数据存入最大堆中
(3)当第三个数据大于最大堆堆顶小于最小堆堆顶时,进入最大堆中
(4)当最大堆和最小堆之间size差大于1时,移动多的一方进入另一方中
上面的思路是书中给出的,也是比较容易想到的,但还有一种思路:
(1)先把数据存入最大堆中
(2)把最大堆堆顶数据存入最小堆中
(3)比较二者size,如果最小堆size大,把最小堆的数据存入最大堆中
第二种思路相比第一种更清晰,代码实现也更简单。
class MedianFinder { //定义两个堆,最大堆和最小堆 private PriorityQueue<Integer> maxHeap; private PriorityQueue<Integer> minHeap; /** initialize your data structure here. */ public MedianFinder() { maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator()); minHeap = new PriorityQueue<Integer>(new MinHeapComparator()); } public void addNum(int num) { //每次取出来都先进最大堆,然后将最大堆的堆顶元素取出来放进最小堆中,然后再比较size这样就保证了最大堆和和最小堆数据最多差1且最大堆数据量大于等于最小堆数据量 maxHeap.add(num); minHeap.add(maxHeap.remove()); if (minHeap.size()>maxHeap.size()){ maxHeap.add(minHeap.remove()); } } public double findMedian() { if (maxHeap.size() == minHeap.size()){ return (maxHeap.peek()+minHeap.peek())/2.0; }else { return maxHeap.peek(); } } private class MaxHeapComparator implements Comparator<Integer>{ //可以这样认为,返回正数的在前面或上面,不要去记忆谁大谁小这样容易混的 @Override public int compare(Integer o1, Integer o2) { if (o2 > o1){ return 1; }else { return -1; } } } //在Java中默认是用最小堆的,其实这个可以不用写,但为了方便学习比较还是写上了 private static class MinHeapComparator implements Comparator<Integer>{ //可以这样认为,返回正数的在前面或上面,不要去记忆谁大谁小这样容易混的 @Override public int compare(Integer o1, Integer o2) { if (o2 < o1){ return 1; }else { return -1; } } } }
在两个长度相等的排序数组中找到上中位数
在两个排序数组中找到第K小的数
两个有序数组间相加和的TOP K问题
Manacher算法
KMP算法
丢棋子问题
画匠问题
邮局选址问题
0