牛客网
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过
某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树
public static String pre(TreeNode root){
if(root==null)
return "#";
return root.val+"!"+pre(root.left)+"!"+pre(root.right);
}
public static TreeNode preBuildTree(String str){
LinkedList<String> list=new LinkedList<>();
if(str==null)
return null;
String[] s=str.split("!");
for(int i=0;i<s.length;i++)
list.add(s[i]);
return preBuildTree(list);
}
public static TreeNode preBuildTree(LinkedList<String> list){
String str=list.removeFirst();
if(str.equals("#"))
return null;
TreeNode root=new TreeNode(Integer.valueOf(str));
root.left=preBuildTree(list);
root.right=preBuildTree(list);
return root;
}
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
int cnt=0;
TreeNode ret;
TreeNode KthNode(TreeNode pRoot, int k)
{
orderselect( pRoot, k);
return ret;
}
public void orderselect(TreeNode pRoot, int k){
if(pRoot==null||cnt>=k)//等于的话可以少循环一次
return ;
orderselect(pRoot.left,k);
cnt++;
if(k==cnt)
ret= pRoot;
orderselect(pRoot.right,k);
}
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
static ArrayList<ArrayList<Integer> > Print(TreeNode pRoot){
ArrayList<ArrayList<Integer> > lists= new ArrayList<ArrayList<Integer> >();
Queue<TreeNode> queue=new LinkedList<>();
if(pRoot==null)
return lists;
queue.add(pRoot);
while (!queue.isEmpty()){
LinkedList<TreeNode> linkedList=new LinkedList<>();
ArrayList<Integer> list=new ArrayList<>();
while (!queue.isEmpty()){
TreeNode treeNode=queue.poll();
if(treeNode==null)
continue;
linkedList.add(treeNode);
list.add(treeNode.val);
}
TreeNode treeNode=linkedList.poll();
while (treeNode!=null){
queue.add(treeNode.left);
queue.add(treeNode.right);
treeNode=linkedList.poll();
}
lists.add(list);
}
return lists;
}
//可以看出上面相当繁琐,原因在于不会使用标志位(cnt)
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(pRoot);
while (!queue.isEmpty()) {
ArrayList<Integer> list = new ArrayList<>();
int cnt = queue.size();
while (cnt-- > 0) {
TreeNode node = queue.poll();
if (node == null)
continue;
list.add(node.val);
queue.add(node.left);
queue.add(node.right);
}
if (list.size() != 0)
ret.add(list);
}
return ret;
}
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer> > ret=new ArrayList<ArrayList<Integer> >();
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.add(pRoot);
boolean flag=false;
while(!queue.isEmpty()){
int size=queue.size();
ArrayList<Integer> list=new ArrayList<Integer>();
while(size-->0){
TreeNode p=queue.poll();
if(p!=null){
list.add(p.val);
queue.add(p.left);
queue.add(p.right);
}
}
//比上面一题,多了一个判断是否逆序
if(flag)
Collections.reverse(list);
flag=!flag;
if(list.size()>0)
ret.add(list);
}
return ret;
}
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot==null)
return true;
return isSymmetrical(pRoot.left,pRoot.right);
}
public boolean isSymmetrical(TreeNode t1,TreeNode t2){
//试着思考一下,为什么没有写if(t1.val==t2.val) return true;
if(t1==null&&t2==null)
return true;
if(t1==null||t2==null)
return false;
if(t1.val!=t2.val)
return false;
return isSymmetrical(t1.left,t2.right)&&isSymmetrical(t1.right,t2.left);
}
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode.right!=null)
{
TreeLinkNode p=pNode.right;
while(p.left!=null)
p=p.left;
return p;
}
else{
TreeLinkNode p=pNode;
while(p.next!=null){
if(p.next.left==p)
return p.next;
p=p.next;
}
return null;//如果其父节点为空,那么就表示到了顶端了。证明这个节点是这棵树最右边的一个节点
}
HashMap<Integer,Integer> list=new HashMap<>();
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length==0)
return null;
for(int i=0;i<in.length;i++)
list.put(in[i],i);
return reConstructBinaryTree(pre,0,pre.length-1,0);
}
public TreeNode reConstructBinaryTree(int[] pre,int preL,int preR,int inL){
if(preL>preR)
return null;
TreeNode root =new TreeNode(pre[preL]);//先序的首位
int inindex=list.get(pre[preL]);//找到先序首位在中序中的位置
int lefttreesize=inindex-inL;//左子树的长度
root.left=reConstructBinaryTree(pre,preL+1,preL+lefttreesize,inL);
root.right=reConstructBinaryTree(pre,preL+lefttreesize+1,preR,inL+treesize+1);
return root;
}
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list =new ArrayList<>();
if(root==null)
return list;
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode p=queue.poll();
list.add(p.val);
if(p.left!=null)
queue.add(p.left);
if(p.right!=null)
queue.add(p.right);
}
return list;
}
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
backtracking(root,target,new ArrayList<>());
return ret;
}
public void backtracking(TreeNode root,int target,ArrayList<Integer> path){
if(root==null||target<0)//优化
return;
path.add(root.val);
target-=root.val;
if(target==0&&root.left==null&&root.right==null){
ret.add(new ArrayList<>(path));//每次add都是不会更改的
}
else
{
backtracking(root.left,target,path);
backtracking(root.right,target,path);
}
path.remove(path.size()-1);//递归的精髓所在
}
关于ret.add(new ArrayList<>(path))为什么这样使用?
ret.add(new ArrayList<>(path))
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
private TreeNode pre;
private TreeNode head;
public TreeNode Convert(TreeNode pRootOfTree) {
inorder(pRootOfTree);
return head;
}
public void inorder(TreeNode root){
if(root==null)
return;
inorder(root.left);
root.left=pre;
if(pre!=null)
pre.right=root;
pre=root;
if(head==null)
head=root;
inorder(root.right);
}
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
//层次遍历
public int TreeDepth(TreeNode root) {
if(root==null)
return 0;
Queue queue=new LinkedList<>();
queue.add(root);
int k=0;
while(!queue.isEmpty()){
k++;
int size=queue.size();
while(size-->0){
TreeNode p=queue.poll();
if(p.left!=null)
queue.add(p.left);
if(p.right!=null)
queue.add(p.right);
}
}
return k;
}
// 递归实现
public int TreeDepth(TreeNode root) {
return root==null?0:1+Math.max(TreeDepth(root.left),TreeDepth(root.right));
}
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
private boolean isbalance=true;
public boolean IsBalanced_Solution(TreeNode root) {
height(root);
return isbalance;
}
public int height(TreeNode root){
if(root==null||!isbalance)
return 0;
int left=height(root.left);
int right=height(root.right);
if(Math.abs(left-right)>1)
isbalance=false;
return 1+Math.max(left,right);
}
输入一个链表,输出该链表中倒数第k个结点。
public ListNode FindKthToTail(ListNode head,int k) {
int i=0;
ListNode q=head;
ListNode p=head;
while(p!=null){
if(i>=k){
p=p.next;
q=q.next;
}
else
p=p.next;
i++;
}
if(i>=k)
return q;
else
return null;
}
输入一个链表,反转链表后,输出新链表的表头。
public ListNode ReverseList(ListNode head) {
if(head==null||head.next==null)
return head;
ListNode p=new ListNode(-1);
ListNode q;
while(head!=null){
q=head.next;
head.next=p.next;
p.next=head;
head=q;
}
return p.next;
}
// 以上是O(1)的空间复杂度
//下面这是直接使用指针来计算
public ListNode ReverseList(ListNode head) {
if(head==null||head.next==null)
return head;
ListNode p=null;
ListNode q;
while(head!=null){
q=head.next;
head.next=p;
p=head;
head=q;
}
return p;
}
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
//递归实现
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null)
return list2;
if(list2==null)
return list1;
if(list1.val<list2.val){
list1.next=Merge(list1.next,list2);
return list1;
}else{
list2.next=Merge(list1,list2.next);
return list2;
}
}
// 遍历实现
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head=new ListNode(-1);
ListNode p=head;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
p.next=list1;
list1=list1.next;
}else{
p.next=list2;
list2=list2.next;
}
p=p.next;
}
if(list1!=null){
p.next=list1;
}
if(list2!=null){
p.next=list2;
}
return head.next;
}
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead==null)
return pHead;
RandomListNode head=new RandomListNode(pHead.label);
RandomListNode temp=head;
while(pHead.next!=null){
temp.next=new RandomListNode(pHead.next.label);
if(pHead.random!=null)
temp.random=new RandomListNode(pHead.random.label);
temp=temp.next;
pHead=pHead.next;
}
return head;
}
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode p=pHead1;
ListNode q=pHead2;
ListNode ret=null;
while(p!=q ){//这个循环仅仅针对,链表无环。
p=p==null?pHead2:p.next;
q=q==null?pHead1:q.next;
}
return q;
}
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
-思路
快慢指针,但是要注意空指针异常的问题。
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead==null)
return null;
ListNode fast=pHead;
ListNode slow=pHead;
do{
if(fast.next==null||fast.next.next==null)//避免空指针异常的情况
return null;
fast=fast.next.next;
slow=slow.next;
}while(slow!=fast);
fast=pHead;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;
}
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
public static ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null||pHead.next==null)
return pHead;
ListNode head=new ListNode(-1);
ListNode p=head;
while(pHead!=null&&pHead.next!=null){//保证pHead在循环过后一定是一个没有重复的节点
ListNode temp= pHead.next;
if(temp.val==pHead.val){
while(temp!=null&&temp.val==pHead.val){
temp=temp.next;
}
}else{
p.next= pHead;
p=p.next;
p.next=null;
}
pHead=temp;
}
if(pHead!=null)
p.next=pHead;
return head.next;
}
简单的递归实现
public ListNode deleteDuplication(ListNode pHead)
{
if (pHead == null || pHead.next == null)
return pHead;
ListNode next = pHead.next;
if (pHead.val == next.val) {
while (next != null && pHead.val == next.val)
next = next.next;
return deleteDuplication(next);
} else {
pHead.next = deleteDuplication(pHead.next);
return pHead;
}
}
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> stack=new Stack<>();
int cnt=0;
for(int i=0;i<pushA.length;i++){
stack.push(pushA[i]);
while(cnt<=pushA.length&&!stack.isEmpty()&&stack.peek()==popA[cnt]){//cnt<=pushA.length也可以不写,加上这个是防止两个数组的长度不一致
stack.pop();
cnt++;
}
}
if(stack.isEmpty())
return true;
else
return false;
}