2453 2018-12-03 2020-06-25
前言:牛客网剑指Offer编程题61~66题,稳住啊,好吧,没稳住。
一、序列化二叉树
1、题目描述
考点树,请实现两个函数,分别用来序列化和反序列化二叉树。
2、我的代码
我笑了。
public class Solution {
TreeNode node;
String Serialize(TreeNode root) {
node = root;
return "haha";
}
TreeNode Deserialize(String str) {
return node;
}
}
3、别人的代码
它的思路还是构造一颗满二叉树,对于空节点用特殊值代替。
int index = -1; //计数变量
String Serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
if (root == null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");
sb.append(Serialize(root.left));
sb.append(Serialize(root.right));
return sb.toString();
}
TreeNode Deserialize(String str) {
index++;
String[] strr = str.split(",");
TreeNode node = null;
if (!strr[index].equals("#")) {
node = new TreeNode(Integer.valueOf(strr[index]));
node.left = Deserialize(str);
node.right = Deserialize(str);
}
return node;
}
二、二叉搜索树的第k个结点
1、题目描述
考点树,给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
2、别人的代码
看到这题我的第一思路就是,中序遍历存进ArrayList,取第k个节点。看下别人的代码。
// 思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。
// 所以,按照中序遍历顺序找到第k个结点就是结果。
public class Solution {
int index = 0; //计数器
TreeNode KthNode(TreeNode root, int k) {
if(root != null){ //中序遍历寻找第k个
TreeNode node = KthNode(root.left,k);
if(node != null)
return node;
index ++;
if(index == k)
return root;
node = KthNode(root.right,k);
if(node != null)
return node;
}
return null;
}
}
三、数据流中的中位数
1、题目描述
考点树,如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
2、别人的代码
看下别人的代码吧。
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
/***********方式一、用两个优先队列来模拟两个堆---主要思路************************
1.先用java集合PriorityQueue来设置一个小顶堆和大顶堆,大顶堆需要先重写一下里面的比较器
2.主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
小顶堆存较大的数,从小到大的顺序排序,
显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
3.当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
这样就可以保证,每次插入新值时,都保证小顶堆中值大于大顶堆中的值,并且都是有序的。
4.由于第一个数是插入到小顶堆中的,所以在最后取中位数的时候,若是奇数,就从小顶堆中取即可。
这样,当count为奇数的时候,中位数就是小顶堆的根节点;当count为偶数的时候,中位数为大顶堆和小顶堆两个根节点之和的平均数
5.例如,传入的数据为:[5,2,3,4,1,6,7,0,8],那么按照要求,输出是"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
a.那么,第一个数为5,count=0,那么存到小顶堆中,
步骤是:先存到大顶堆;然后弹出大顶堆root,就是最大值给小顶堆,第一次执行完,就是小顶堆为5,count+1=1;
此时若要输出中位数,那么就是5.0,因为直接返回的是小顶堆最小值(第一次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
b.继续传入一个数为2,那么先存到小顶堆中,将小顶堆最小值弹出给大顶堆,即2,那么这次执行完,小顶堆为5,大顶堆为2,count+1=2
此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(5+2)/2=3.5(第二次塞入到大顶堆中,是从小顶堆中找到最小的给他的)
c.继续传入一个数为3,那么此时count为偶数,那么执行第一个if,先存到大顶堆中,大顶堆弹出最大值,那么3>2,就是弹出3
3存到小顶堆中,那么此时小顶堆为3,5,大顶堆为2,count+1=3(第三次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
此时若要输出中位数,因为是奇数,那么取小顶堆的最小值,即3.0
d.继续传入一个数为4,先存到小顶堆中,小顶堆此时为3,4,5,弹出最小值为3,给大顶堆
此时大顶堆为3,2,小顶堆为4,5,(第四次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(3+4)/2=3.5
e.依次类推。。。
******************************************/
/***************方式二、ArrayList***********************
用ArrayList来存输入的数据流,然后每次用Collections.sort(list)来保证数据流有序,然后再取中位数
思想非常简单,但是每次都要进行排序,时间复杂度可想而知
****************************************/
/***************方式三、插入排序,插入到对应的位置***********************
LinkedList<Integer> data = new LinkedList<Integer>();
public void Insert(Integer num) {
for (int i = data.size() - 1; i >= 0 ; i--) {
if (num >= data.get(i)){
data.add(i+1,num);
return;
}
}
data.addFirst(num);
}
****************************************/
int count = 0;
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();//默认是小根堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
public void Insert(Integer num) {
if(count%2 == 0){
//数目为偶数时,插入到小根堆中
maxHeap.offer(num);
int filteredMaxNum = maxHeap.poll();
minHeap.offer(filteredMaxNum);
}else{
//数目为奇数时,插入到大根堆中
minHeap.offer(num);
int filteredMinNum = minHeap.poll();
maxHeap.offer(filteredMinNum);
}
count++;
}
public Double GetMedian() {
if (count %2 == 0) {
return new Double((minHeap.peek() + maxHeap.peek())) / 2;
} else {
return new Double(minHeap.peek());
}
}
}
我还是比较倾向于插入排序,虽然....
四、滑动窗口的最大值
1、题目描述
考点栈和队列,给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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、别人的代码
感觉也不是很难,就跳过了。
public ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> res = new ArrayList<>();
if (size == 0) return res;
int begin;
ArrayDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < num.length; i++) {
begin = i - size + 1;
if (q.isEmpty())
q.add(i);
else if (begin > q.peekFirst())、、
q.pollFirst();
while ((!q.isEmpty()) && num[q.peekLast()] <= num[i])
q.pollLast();
q.add(i);
if (begin >= 0)
res.add(num[q.peekFirst()]);
}
return res;
}
五、矩阵中的路径
1、题目描述
考点回溯法,请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
2、别人的代码
/**
用一个状态数组保存之前访问过的字符,然后再分别按上,下,左,右递归
*/
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;
}
}
感觉还是孰能生巧吧。
六、机器人的运动范围
1、题目描述
考点回溯法,地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
2、别人的代码
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;
}
终于到了第66题,一路下路,前面都感觉还好,只是后面这两篇略显敷衍。以后多多回顾吧,总的来说,还是学到了不少东西的,不虚此行。
总访问次数: 290次, 一般般帅 创建于 2018-12-03, 最后更新于 2020-06-25
欢迎关注微信公众号,第一时间掌握最新动态!