61、按之字形顺序打印二叉树
题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
思路1:按上一题的方式,还是用队列,用标记倒叙输出,但是有缺点
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 {
/**
* 缺点:将每层的数据存进ArrayList中,偶数层时进行reverse操作,但是在海量数据时,这样效率太低了。
*/
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if(pRoot == null){
return result;
}
boolean leftToRight = true;
Queue<TreeNode> layer = new LinkedList<TreeNode>();
ArrayList<Integer> layerList = new ArrayList<Integer>();
layer.add(pRoot);
int start = 0, end = 1;
while(!layer.isEmpty()){
TreeNode cur = layer.remove();
layerList.add(cur.val);
start++;
if(cur.left!=null){
layer.add(cur.left);
}
if(cur.right!=null){
layer.add(cur.right);
}
if(start == end){
end = layer.size();
start = 0;
if(!leftToRight){
result.add(reverse(layerList));
}else{
result.add(layerList);
}
leftToRight = !leftToRight;
layerList = new ArrayList<Integer>();
}
}
return result;
}
public ArrayList reverse(ArrayList<Integer> layerList) {
int length = layerList.size();
ArrayList<Integer> reverseList = new ArrayList<Integer>();
for(int i = length-1; i >= 0;i--){
reverseList.add(layerList.get(i));
}
return reverseList;
}
}
思路1改进:用LinkedList,可以双向遍历,
Iterator iter = iter = queue.iterator();//从前往后遍历 Iterator iter = queue.descendingIterator();//从后往前遍历
import java.util.*;
public class Solution {
/**
* 思路:利用Java中的LinkedList的底层实现是双向链表的特点。
* 1)可用做队列,实现树的层次遍历
* 2)可双向遍历,奇数层时从前向后遍历,偶数层时从后向前遍历
*/
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
if (pRoot == null) {
return ret;
}
ArrayList<Integer> list = new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
queue.addLast(null);//层分隔符
queue.addLast(pRoot);
boolean leftToRight = true;
while (queue.size() != 1) {
TreeNode node = queue.removeFirst();//删除第一个,返回删除节点,若此节点不是分层的节点null,则添加左右子节点
if (node == null) {//判断删除的节点是否为null,以此来判断是否到达分层隔符
Iterator<TreeNode> iter = null;
if (leftToRight) {
iter = queue.iterator();//从前往后遍历
} else {
iter = queue.descendingIterator();//从后往前遍历
}
leftToRight = !leftToRight;
while (iter.hasNext()) {
TreeNode temp = (TreeNode)iter.next();
list.add(temp.val);
}
ret.add(new ArrayList<Integer>(list));
list.clear();
queue.addLast(null);//添加层分隔符
continue;//一定要continue
}
if (node.left != null) {
queue.addLast(node.left);
}
if (node.right != null) {
queue.addLast(node.right);
}
}
return ret;
}
}
思路2:用栈,先进后出,但是注意要用两个栈,因为如果用一个的话,本层的节点会压在最底下,此节点的子节点会放在最上边
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList list=new ArrayList<ArrayList<Integer>>();
if(pRoot==null)
return list;
Stack s1=new Stack();//从左到右输出,所以添加时先加右,再加左
Stack s2=new Stack();//从右到左输出,所以添加时先加左,再加右
s1.push(pRoot);
ArrayList arr=new ArrayList<Integer>();
while(true){
while(s1.size()!=0){
TreeNode node=(TreeNode)s1.pop();
arr.add(node.val);
if(node.left!=null)
s2.push(node.left);
if(node.right!=null)
s2.push(node.right);
}
list.add(arr);
arr=new ArrayList<Integer>();
if(s1.size()==0&&s2.size()==0)
break;
while(s2.size()!=0){
TreeNode node1=(TreeNode)s2.pop();
arr.add(node1.val);
if(node1.right!=null)
s1.push(node1.right);
if(node1.left!=null)
s1.push(node1.left);
}
list.add(arr);
arr=new ArrayList<Integer>();
if(s1.size()==0&&s2.size()==0)
break;
}
return list;
}
}
62、序列化二叉树
题目描述 请实现两个函数,分别用来序列化和反序列化二叉树
思路:通过前序遍历的顺序,但是修改了一下,子节点为null的用#表示
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int index = -1;
//序列化
public String Serialize(TreeNode root) {
StringBuffer sb = new StringBuffer();
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();
}
//反序列化
public TreeNode Deserialize(String str) {
index++;//数组指数,每次移下一位
int len = str.length();
if(index >= len){
return null;
}
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;
}
}
63、二叉搜索树的第K个节点
题目描述 给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
思路:中序遍历就是二叉搜索树的排序,不用递归的程序
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k)
{
Stack<TreeNode> stack = new Stack<TreeNode>();
if(pRoot==null||k==0) return null;
int t=0;
while(pRoot!=null ||stack.size()>0){
while(pRoot!=null){
stack.push(pRoot);
pRoot = pRoot.left;
}
if(stack.size()>0){
pRoot= stack.pop();
t++;
if(t==k) return pRoot;
pRoot= pRoot.right;
}
}
return null;
}
}
64、数据流中的中位数
题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
import java.util.*;
public class Solution{
private Heap maxHeap = new Heap(Heap.isMaxHeap);
private Heap minHeap = new Heap(Heap.isMinHeap);
/**
* 插入有两种思路:
* 1:直接插入大堆中,之后若两堆尺寸之差大于1(也就是2),则从大堆中弹出堆顶元素并插入到小堆中
* 若两队之差不大于1,则直接插入大堆中即可。
* 2:奇数个数插入到大堆中,偶数个数插入到小堆中,
* 但是 可能会出现当前待插入的数比小堆堆顶元素大,此时需要将元素先插入到小堆,然后将小堆堆顶元素弹出并插入到大堆中
* 对于偶数时插入小堆的情况,一样的道理。why?
* 因为要保证最大堆的元素要比最小堆的元素都要小。
* @param num
*/
public void Insert(Integer num) {
//若总尺寸为偶数,则插入大顶堆中
if(((maxHeap.size() + minHeap.size()) & 1) == 0){
if(minHeap.size() != 0 && num > minHeap.peek()){
minHeap.add(num);
maxHeap.add(minHeap.pop());
}else{
maxHeap.add(num);
}
}else{
if(maxHeap.size() != 0 && num < maxHeap.peek()){
maxHeap.add(num);
minHeap.add(maxHeap.pop());
}else{
minHeap.add(num);
}
}
}
public Double GetMedian() {
double res = 0.0;
if(((maxHeap.size() + minHeap.size()) & 1) == 0){
res = (maxHeap.peek() + minHeap.peek()) / 2.0;
}else{
res = maxHeap.peek();
}
return res;
}
//堆类,可直接设置最大堆最小堆
class Heap {
public List<Integer> list = null;
public static final boolean isMaxHeap = true;
public static final boolean isMinHeap = false;
private boolean flag = true; //true表示最大堆,false表示最小堆
public Heap(){
this.list = new ArrayList<Integer>();
}
public Heap(boolean flag){
this.list = new ArrayList<Integer>();
this.flag = flag;
}
//获取堆大小
public int size(){
return this.list.size();
}
//获取堆顶元素
public int peek(){
if(list.size() == 0) return 0;
return list.get(0);
}
//插入元素,从插入点开始向上调整堆
public void add(int val){
this.list.add(val);
int i = list.size() - 1, index, parent, cur;
while(i > 0){
index = (i - 1) / 2;
parent = list.get(index);
cur = list.get(i);
if(flag == true && parent < cur){
swap(index, i);
}else if(flag == false && parent > cur){
swap(index, i);
}
i = index;
}
}
/**
* 将堆顶元素取出,并重新调整堆。
* 1>取出堆顶元素
* 2>将最后一个元素放到堆顶
* 3>向下调整堆
*/
public int pop(){
if(list.size() == 0) return -1;
int res = list.get(0);
list.set(0,list.get(list.size() - 1));
list.remove(list.size()-1);
int len = list.size() - 1 , i = 0;
int left , right;
while(i < len){
left = (i << 1) + 1;
right= (i << 1) + 2;
int maxIndex = i;
if(flag == true){
if(left < len && list.get(left) > list.get(maxIndex)) maxIndex = left;
if(right< len && list.get(right)> list.get(maxIndex)) maxIndex = right;
}else{
if(left < len && list.get(left) < list.get(maxIndex)) maxIndex = left;
if(right< len && list.get(right)< list.get(maxIndex)) maxIndex = right;
}
if(maxIndex != i){
swap(maxIndex,i);
i = maxIndex;
}else break;
}
return res;
}
//交换list中两个位置的元素
public void swap(int i, int j){
int temp = list.get(i);
list.set(i, list.get(j));
list.set(j,temp);
}
}
}
思路2:
import java.util.ArrayList;
import java.util.Comparator;
/**
* 数据流中的中位数
*
* @author 过路的守望
*
*/
public class Solution {
/*
* 最大堆
*/
private Heap maxHeap = new Heap(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
/*
* 最小堆
*/
private Heap minHeap = new Heap(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
/*
* 插入元素
*/
public void Insert(Integer num) {
/*
* 如果最大堆中的元素个数大于等于最小堆中的元素个数,则新元素插入到最小堆中
*/
if (maxHeap.getSize() >= minHeap.getSize()) {
/*
* 如果最小堆中未插入元素,则新元素直接插入
*/
if (minHeap.getSize() == 0) {
minHeap.add(num);
}
/*
* 若最小堆中已存在元素,则将带插入元素与最大堆中的堆顶元素相比较,若待插入元素较小,则将最大堆堆顶元素弹出后添加到最小堆中,
* 最大堆中将待插入元素添加进去。
*/
else if (num < maxHeap.peek()) {
minHeap.add(maxHeap.pop());
maxHeap.add(num);
}
/*
* 待插入元素不小于最大堆堆顶元素
*/
else {
minHeap.add(num);
}
} else {
/*
* 如果最大堆中未插入元素,则新元素直接插入
*/
if (maxHeap.getSize() == 0) {
maxHeap.add(num);
}
/*
* 若最大堆中已存在元素,则将带插入元素与最小堆中的堆顶元素相比较,若待插入元素较大,则将最小堆堆顶元素弹出后添加到最大堆中,
* 最小堆中将待插入元素添加进去。
*/
else if (num > minHeap.peek()) {
maxHeap.add(minHeap.pop());
minHeap.add(num);
}
/*
* 待插入元素不大于最小堆堆顶元素
*/
else {
maxHeap.add(num);
}
}
}
/*
* 得到中间值
*/
public Double GetMedian() {
/*
* 判断数据数目奇偶
*/
int size = maxHeap.getSize() + minHeap.getSize();
if ((size & 1) == 1) {
return (double) minHeap.peek();
}
return (maxHeap.peek() + minHeap.peek()) / 2.0;
}
}
/**
* 数据结构-堆
*
* @author 过路的守望
*
*/
class Heap {
/*
* 比较器
*/
private Comparator<Integer> comparator;
private ArrayList<Integer> list;
public Heap() {
list = new ArrayList<Integer>();
}
/*
* 构造器传入比较器
*/
public Heap(Comparator<Integer> comparator) {
this();
this.comparator = comparator;
}
/*
* 弹出堆顶元素
*/
public int pop() {
int data = list.get(0);
list.set(0, list.remove(list.size() - 1));
percolateDown();
return data;
}
/*
* 返回堆顶元素
*/
public int peek() {
return list.get(0);
}
/*
* 添加元素
*/
public void add(int element) {
list.add(element);
percolateUp();
return;
}
/*
* 堆中对象个数
*/
public int getSize() {
return list.size();
}
/*
* 下滤操作
*/
private void percolateDown() {
int size = list.size();
/*
* 从下标为0的节点开始下滤
*/
int i = 0;
/*
* temp保存最大堆或最小堆的堆顶值
*/
int temp = list.get(0);
int leftChild = getLeftChild(i);
while (leftChild < size) {
/*
* 得到左右儿子中较大的下标
*/
if (leftChild < size - 1
&& comparator.compare(list.get(leftChild),
list.get(leftChild + 1)) < 0) {
leftChild++;
}
/*
* 若儿子大于父亲,就把 儿子的值赋给父亲
*/
if (comparator.compare(temp, list.get(leftChild)) < 0) {
list.set(i, list.get(leftChild));
i = leftChild;
leftChild = getLeftChild(i);
continue;
} else {
break;
}
}
/*
* 下滤完成,找到temp所在下标
*/
list.set(i, temp);
}
/*
* 上滤操作
*/
private void percolateUp() {
/*
* 从堆中最后一个元素开始上滤
*/
int i = list.size() - 1;
int temp = list.get(i);
int parent = getParent(i);
while (parent >= 0) {
/*
* 若父亲小于儿子,则把父亲的值赋给儿子
*/
if (comparator.compare(temp, list.get(parent)) > 0) {
list.set(i, list.get(parent));
i = parent;
parent = getParent(parent);
continue;
} else {
break;
}
}
/*
* 上滤完成,找到temp所在下标
*/
list.set(i, temp);
}
/*
* 左儿子下标
*/
private int getLeftChild(int i) {
return 2 * i + 1;
}
/*
* 父亲下标
*/
private int getParent(int i) {
return (int) Math.floor((i - 1) / 2.0);
}
}
65、滑动窗口的最大值
题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
import java.util.*;
/**
* 题目:滑动窗口的最大值
* 思路:滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列。
* 原则:
* 对新来的元素k,将其与双端队列中的元素相比较
* 1)前面比k小的,直接移出队列(因为不再可能成为后面滑动窗口的最大值了!),
* 2)前面比k大的X,比较两者下标,判断X是否已不在窗口之内,不在了,直接移出队列
* 队列的第一个元素是滑动窗口中的最大值
*/
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> ret = new ArrayList<>();
if (num == null) {
return ret;
}
if (num.length < size || size < 1) {
return ret;
}
// 用来保存可能是滑动窗口最大值的数字的下标
LinkedList<Integer> indexDeque = new LinkedList<>();
for (int i = 0; i < size - 1; i++) {
// 如果已有数字小于待存入的数据,
// 这些数字已经不可能是滑动窗口的最大值
// 因此它们将会依次地从队尾删除
while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
indexDeque.removeLast();
}
indexDeque.addLast(i);
}
for (int i = size - 1; i < num.length; i++) {
// 如果已有数字小于待存入的数据,
// 这些数字已经不可能是滑动窗口的最大值
// 因此它们将会依次地从队尾删除
while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
indexDeque.removeLast();
}
indexDeque.addLast(i);
if (i - indexDeque.getFirst() + 1 > size) {
indexDeque.removeFirst();
}
ret.add(num[indexDeque.getFirst()]);
}
return ret;
}
}
声明:此文章为本人原创,如有转载,请注明出处