Day6.牛客网剑指offer 67题之55-67(java实现单队列打印之字形二叉树!)

Day6.牛客网剑指offer 67题之55-67(java代码)

又是起一个大早而且面试一整天的节奏,早上计网面试给大家5分钟时间答题大部分人还是虚的很,40多个人只有几个人是能完整答下来的,面试到12点半然后下午1点又开始基础技能面试,各种题都有,主观题也挺多,我发现只有一位同学在有主观题的情况下选择了客观题,并且答得还不错,主观题还是比较容易答的,我印象最深刻的题就是今年发生了什么大事情…?但也说明大部分考生还是基础不牢靠,我自己也是很多基础知识也快还给老师了,今天确实让我思考了许多问题,尤其是基础这个问题,之后要是毕业工作经常在这方面吃亏就不好了,珍惜时间吧,再也不想学半天刷半天小视频了。
卑微打工仔5点多结束之后是倒头就睡,明天还有最后一天,不过今天算是终于把题刷完了,也要总结自己的一些不足,队列的变形以及动态规划仍然还是弱项,下来会继续攻破这几点难点,二叉树栈这一块还是比较熟悉了,慢慢来吧,每天会多在力扣上刷题,时间也不是特别多,还需要补补基础知识。

55.链表中环的入口结点

题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
用两个指针,一个指针每次走一步,一个每次走两步,如果两个指针相遇了,说明有环,相遇之后让一个指针回到头结点,然后两个指针每次走一步直到相遇就是环的入口结点(可以自己画一下)。

public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead==null||pHead.next==null) return null;
        ListNode p1=pHead,p2=pHead;
        while(p1!=null&&p2!=null){
            p1=p1.next.next;
            p2=p2.next;
            if(p1==p2){
                break;
            }
        }
        if(p1!=null&&p2!=null){
            p1=pHead;
            while(p1!=p2){
                p1=p1.next;
                p2=p2.next;
            }
            return p1;
        }
        return null;
    }

56.删除链表中重复的结点

题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路
可以考虑使用一个头结点(防止第一个数和第二个数就相等的情况),一个指针指向头结点,一个指针指向头结点的下一结点,然后开始右移比较。

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null) return null;
        ListNode dummyList=new ListNode(-1);
        dummyList.next=pHead;
        ListNode pre=dummyList;
        ListNode q=pre.next;

        while(q!=null){
            if(q.next!=null&&q.next.val==q.val){
                while(q.next!=null&&q.val==q.next.val){
                    q=q.next;
                }
                pre.next=q.next;
                q=q.next;
            }else{
                pre=pre.next;
                q=q.next;
            }
        }
        return dummyList.next;
    }
}

57.二叉树的下一结点

题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路
中序遍历的顺序是左根右,当前结点若有右子树,则直接遍历右子树,若没有右子树则需要考虑下一结点的问题,考虑父结点。

    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;
        }else{//无右子树
            while(pNode.next!=null){
                if(pNode.next.left==pNode){
                    return pNode.next;
                }else{
                    pNode=pNode.next;
                }
            }
        }
        return null;
        
    }

58.对称的二叉树

题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路
判断是否对称只需递归判断其左右子树是否也对称。

public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null) return true;
        return isSymmetricalTree(pRoot.left,pRoot.right);
    }
    boolean isSymmetricalTree(TreeNode left,TreeNode right){
        if(left==null&&right==null) return true;
        if(left==null||right==null) return false;
        return left.val==right.val &&
            isSymmetricalTree(left.left,right.right)&&
            isSymmetricalTree(left.right,right.left);
    }
}

59.按之字形打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
思路
杠一下这是之吗,这明明是S好吧…借鉴广度优先搜索思想,之前想着用一个队列一个栈去实现,发现是有逻辑问题的,无奈先把两个栈实现的写出来吧.

public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        if(pRoot==null) return res;
        Stack<TreeNode> s1=new Stack<>();
        Stack<TreeNode> s2=new Stack<>();
        s1.add(pRoot);
        while(!s1.isEmpty()||!s2.isEmpty()){
            if(!s1.isEmpty()){
                ArrayList<Integer> list=new ArrayList<>();
                while(!s1.isEmpty()){
                    TreeNode node=s1.pop();
                    list.add(node.val);
                    if(node.left!=null) s2.push(node.left);
                    if(node.right!=null) s2.push(node.right);
                }
                res.add(list);
            }else{
                ArrayList<Integer> list1=new ArrayList<>();
                while(!s2.isEmpty()){
                    TreeNode node=s2.pop();
                    list1.add(node.val);
                    if(node.right!=null) s1.push(node.right);
                    if(node.left!=null) s1.push(node.left);
                   // count--;
                }
                res.add(list1);
            }
            
        }
        return res;
    }

使用单队列层次遍历也是可以实现的,设定一个层数标志位,如果在偶数层,那么使用list.add(0,val)的方法就可以实现倒插!学习了
单队列实现

public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        if(pRoot==null) return res;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(pRoot);
        int temp=1;
        while(!queue.isEmpty()){
            int count=queue.size();
            ArrayList<Integer> list=new ArrayList<>();
            while(count>0){
                TreeNode node=queue.poll();
                if(temp%2==0){
                    list.add(0,node.val);
                }else{
                    list.add(node.val);
                }
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
                count--;
            }
            res.add(list);
            temp++;
        }
        return res;
    }

60.把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路
这一题跟上一题单队列实现类似,使用队列对二叉树进行层次遍历,比上一题使用单队列实现更简单。

ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        if(pRoot==null) return res;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(pRoot);
        while(!queue.isEmpty()){
            int count=queue.size();
            ArrayList<Integer> list=new ArrayList<>();
            while(count>0){
                TreeNode node=queue.poll();
                list.add(node.val);
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
                count--;
            }
            res.add(list);
        }
        return res;
    }

61.序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树
思路
层序遍历二叉树,转值时加入一个","分隔符,更易操作。

import java.util.*;
import java.io.*;
public class Solution {
    private int index;
    public String Serialize(TreeNode root) {
        if(root==null) return "";
        StringBuffer sb=new StringBuffer();
        printTree(root,sb);
        return sb.toString();
  }
    public void printTree(TreeNode root,StringBuffer sb){
        if(root==null){
            sb.append("$,");
            return;
        }
        sb.append(root.val);
        sb.append(",");
        printTree(root.left,sb);
        printTree(root.right,sb);
    }
    public TreeNode Deserialize(String str) {
       if(str==null||str.length()==0) return null;
        String[] strs=str.split(",");
        index=0;
        return reverse(strs);
  }
    public TreeNode reverse(String[] strs){
        if(!strs[index].equals("$")) {
			TreeNode node = new TreeNode(Integer.parseInt(strs[index]));
			index ++;
			node.left = reverse(strs);
			node.right = reverse(strs);
			return node;
		} else {
			index ++;
		}
		return null;
    }
}

62.二叉搜索树的第k个结点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
思路
leetcode之前刷过,使用中序遍历可以返回一个递增序列,设置一个计数标志,遍历到第k个结点就输出。

import java.util.ArrayList;
public class Solution {
    private int count =0;
    public TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot==null||k<=0) return null;
        ArrayList<TreeNode> list=new ArrayList<>();
        helper(pRoot,list);
        TreeNode kthNode=null;
        if(k<=list.size()){
            kthNode=list.get(k-1);
        }
        return kthNode;
    }
    public void helper(TreeNode root,ArrayList<TreeNode> list){
        //if(root==null) return ;
        if(root!=null){
            if(root.left!=null){
                helper(root.left,list);
            }
            list.add(root);
            helper(root.right,list);
        }
    }
}

63.数据流中的中位数

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路
中位数需要判断两种情况,奇数长度和偶数长度,所以最主要的是对数据流进行一个排序,链表跟优先级队列应该都可以实现(优先级队列个人不是特别熟练所以用了链表,可以参考别人的优先级队列思路)

import java.util.LinkedList;
public class Solution {
    private LinkedList<Integer> list=new LinkedList<>();
    public void Insert(Integer num) {
        if(list.size()==0||num<list.getFirst()){
            list.addFirst(num);
        }else{
            boolean flag=false;
            for(Integer ele:list){
                if(num<ele){
                    int index=list.indexOf(ele);
                    list.add(index,num);
                    flag=true;
                    break;
                }
            }
            if(!flag){//当前num值最大
                list.addLast(num);
            }
        }
    }

    public Double GetMedian() {
        if(list.size()==0) return null;
        if(list.size()%2==0){
            int midIndex=list.size()/2;
            Double mid=Double.valueOf(list.get(midIndex-1)+list.get(midIndex));
            return mid/2;
        } 
        list.get(0);
        Double b = Double.valueOf(list.get((list.size() + 1) / 2 - 1));
        return Double.valueOf(list.get((list.size() + 1) / 2 - 1));
    }
}

64.滑动窗口的最大值

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
思路
使用ArrayList每次可以存储一个滑动窗口,找到每个滑动窗口最大值存入list,这里主要就是找滑动窗口以及最大值,应该可以实现,看很多人也有队列,自己会下去补一下其他队列。

public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        if(num==null||size<0){
            return null;
        }
        ArrayList<Integer> list=new ArrayList<Integer>();
        if(size==0){
            return list;
        }
        ArrayList<Integer> temp=null;
        int length=num.length;
        if(length<size){
            return list;
        }else{
            for(int i=0;i<length-size+1;i++){//滑动窗口初始位置在0-length-size+1之间;
                temp=new ArrayList<Integer>();
                for(int j=i;j<size+i;j++){//用于存储滑动窗口的值
                    temp.add(num[j]);
                }
                Collections.sort(temp);//调用内部排序
                list.add(temp.get(temp.size()-1));//每次窗口的最后一个数为最大值
            }
        }
        return list;
    }

65.矩阵中的路径

在这里插入图片描述
思路
又是动态规划的题,不用重复访问就需要定义一个辅助数组标志,判断该位置是否已经被访问,还有巴拉巴拉一堆判定条件,最后对于某个点如果匹配成功就需要递归判断下一次其周围四个点是否可以匹配。

    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++){
                int index=i*cols+j;
                if(matrix[index]==str[0]){
                    if(findPath(matrix, rows, cols, i, j, str, 0, flag)) {
						return true;
                }
            }
        }
    }
        return false;
    }
    public boolean findPath(char[]matrix,int rows,int cols,int i,int j,char[]str,int strIndex,int[]flag){
        int index=i*cols+j;
        if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[strIndex]||flag[index]==-1){
            return false;
        }
        if(strIndex==str.length-1){
            return true;
        }
        flag[index]=-1;//表示已访问
        if(findPath(matrix,rows,cols,i-1,j,str,strIndex+1,flag)||
           findPath(matrix,rows,cols,i+1,j,str,strIndex+1,flag)||
           findPath(matrix,rows,cols,i,j+1,str,strIndex+1,flag)||
           findPath(matrix,rows,cols,i,j-1,str,strIndex+1,flag)){
            return true;
        }
        flag[index]=0;
        return false;
            
    }

66.机器人的运动范围

题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路
仍然是动态规划的题,比上一题实现应该还简单一些,因为这里递归的话只需要走到它的右边和下边。

public int movingCount(int threshold, int rows, int cols)
    {
        int flag[]=new int[rows*cols];
        return moving(threshold,rows,cols,0,0,flag);
    }
    public int moving(int thr,int rows,int cols,int i,int j,int []flag){
        int index=i*cols+j;
        if(i<0||i>=rows||j<0||j>=cols||flag[index]==-1){
            return 0;
        }
        int sum=0;//存储该位置位数的和
        int temp=i;
        while(temp!=0){
            sum+=temp%10;
            temp/=10;
        }
        temp=j;
        while(temp!=0){
            sum+=temp%10;
            temp/=10;
        }
        if(sum<=thr){
            flag[index]=-1;
            int steps=1;
            steps+=moving(thr,rows,cols,i+1,j,flag);
            steps+=moving(thr,rows,cols,i,j+1,flag);
            return steps;
        }
        return 0;
    }

67.剪绳子

题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路
这一题其实总结一下规律会发现把绳子剪成2和3的长度乘积会最大,而且3要更多,因为222<33,5<23,6<3*3等等,所以只需要把长度把握好,数学方法题。

public int cutRope(int target) {
        int aLen = 0;
        int cLen = 0;
        int maxValue = 2;
       if (target == 2) {
           return 1;
       }
       if (target == 3) {
           return 2;
       }
       if (target % 3 == 0) {//都剪成3长度
            maxValue = (int)Math.pow(3, target / 3);
       } else{
            aLen = target - 2;//先剪一段2长度的,初始值即为2
            cLen = a % 3;//在这余下的只能是2或者0,注意是没有1的。
            maxValue = maxValue * (int)Math.pow(3, aLen / 3);
            if (0 != cLen) {
                maxValue = maxValue * cLen;
            }
       }
        return maxValue;
    }

上一篇:牛客网剑指offer 67题之43-54题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值