剑指Offer(30-39)

emm最近这段时间回学校复习了,时间相对也比较充裕,继续向前,保持激情 ~~
顺便附带亮剑经典语录 四:
什么他他娘的精锐,老子打的就是精锐!什么武士道,老子打的就是武士道!

30. 栈的压入、弹出序列

题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路:
借用一个辅助的栈,遍历压栈顺序,然后判断栈顶元素是不是出栈顺序的第一个元素,等到最后如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

import java.util.Stack;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack = new Stack<>();
        int index = 0;    //index 为弹出序列下标
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            while (!stack.isEmpty() && stack.peek() == popA[index]) {
                stack.pop();
                index++;
            }
        }
        return stack.isEmpty();
    }
}

31. 从上往下打印二叉树

题目描述:
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

解题思路:
第一种 :二叉树的层次遍历,类似BFS,使用一个队列来解决。

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 {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            int length = queue.size();
            while (length-- > 0) {
                TreeNode t = queue.poll();
                if (t == null) continue;
                list.add(t.val);
                queue.offer(t.left);
                queue.offer(t.right);
            }
            
        }
        return list;        
    }
}

第二种:递归按顺序添加。

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private ArrayList<Integer> list = new ArrayList<>();
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        if (root != null) {
            list.add(root.val);
        }
        print(root);
        return list;
    }
    private  void print(TreeNode node) {
        if (node != null) {    
            if (node.left != null) {
                list.add(node.left.val);
            }
            if (node.right != null) {
                list.add(node.right.val);
            }
            print(node.left);
            print(node.right);
        }
    }
}
32. 把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解题思路:这道题其实跟上面一道很类似,可以用BFS思想,只是要注意 同一层的时候是在第二个while循环那边添加 小list。

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<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> BigArrayList = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(pRoot);
        while (!queue.isEmpty()) {
            int cnt = queue.size();
            ArrayList<Integer> list = new ArrayList<>();
            //同一层
            while (cnt-- > 0) {
                TreeNode t = queue.poll();  
                if (t != null) {      
                    list.add(t.val);
                    if (t.left != null) {
                        queue.offer(t.left);
                    }
                    if (t.right != null) {
                        queue.offer(t.right);
                    }
                    
                }
            }
            
           if (list.size() > 0) {
               BigArrayList.add(list);
           }
        }
        return BigArrayList;
    }
    
}
33. 按之字形顺序打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

解题思路:
第一种:依旧是层次遍历,这里我们还是跟上题一样的方法,不过当遇到偶数层是,我们反转链表,再进行添加,牛客的评论中说这样的时间复杂度太大了,可以考虑使用两个栈来实现,下面第二种就是用两个栈来实现。

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 {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > bigList = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        int len = 0;
        if (pRoot != null) {
            queue.offer(pRoot);
            len++;    //层数
        }
        while (!queue.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<>();
            int cnt = queue.size();
            while (cnt-- > 0) {
                TreeNode t = queue.poll();
                if (t == null) continue;
                list.add(t.val); 
                queue.offer(t.left);
                queue.offer(t.right);
                
            }
            if (list.size() > 0) {
                if (len % 2 == 0) {
                    Collections.reverse(list);		// 偶数层反转
                }
                bigList.add(list);
                len++;
            }      
        }
        return bigList;

    }

}

第二种:使用两个栈,利用栈后进先出的特点。
leftStack 存储奇数层,从左到右。当遇到奇数层时在rightStack中添加元素。
rightStack存储偶数层,从右到左。当遇到偶数层时在leftStack 中添加元素。并注意顺序

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 {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > bigList = new ArrayList<>();
        Stack<TreeNode> leftStack = new Stack<>();
        Stack<TreeNode> rightStack = new Stack<>();
        int len = 1;
        if (pRoot != null) {
           leftStack.push(pRoot); 
        }
        while (!leftStack.isEmpty() || !rightStack.isEmpty()) {
            // 从左到右
            if (len % 2 != 0) {
                ArrayList<Integer> list = new ArrayList<>();
                while (!leftStack.isEmpty()) {
                    TreeNode t = leftStack.pop();
                    if (t == null) continue;
                    list.add(t.val);
                    rightStack.push(t.left);    利用栈的特性可以实现下一层从右到左
                    rightStack.push(t.right);
                }
                if (list.size() > 0) {
                    bigList.add(list);
                    len++;
                }
            } 
            // 从右到左
            else {
                ArrayList<Integer> list = new ArrayList<>();
                while (!rightStack.isEmpty()) {
                    TreeNode t = rightStack.pop();
                    if (t == null) continue;
                    list.add(t.val);
                    leftStack.push(t.right);    //利用栈的特性可以实现下一层从左到右
                    leftStack.push(t.left);
                }
                if (list.size() > 0) {
                    bigList.add(list);
                    len++;
                }
            }
        }
 
        return bigList;

    }

}
34. 二叉搜索树的后序遍历序列

题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路:利用二叉树后序遍历的性质,后序遍历 的序列中,最后一个数字是树的根节点 ,数组中前面的数字可以分为两部分:第一部分是左子树节点 的值,都比根节点的值小第二部分 是右子树 节点的值,都比 根 节点 的值大,后面用递归分别判断前后两部分 是否 符合以上原则。

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if (sequence == null || sequence.length == 0) {
            return false;
        }
        return verify(sequence,0,sequence.length - 1);
    }
    private boolean verify(int [] arr, int first, int last) {
        
        if (first >= last) return true;    //递归出口
        int index = first;
        while (index < last && arr[index] <= arr[last]) {
            index++;    //找到二叉树顶点
        }
        for (int i = index; i < last; i++) {
            if (arr[i] < arr[last]) {
                return false;
            }
        }
        return verify(arr,first,index - 1) && verify(arr,index,last - 1);
    }
}
35. 二叉树中和为某一值的路径

题目描述
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

解题思路:
这里使用深度优先遍历,需要注意的是如果递归到叶子节点如果还没有找到路径,就要回退到父节点继续寻找,然后最终需要对链表进行排序,因为递归不能确保第一个就是最长的链表。不过好像牛客的测试用例没有排序也能ac。。。

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 { 
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        ArrayList<ArrayList<Integer>> bigList = new ArrayList<>();
        ArrayList<Integer> list = new ArrayList<>();
        dfs(root,target,bigList,list);
        
        //需要对bigList里面的list进行排序
        Collections.sort(bigList,new Comparator<ArrayList<Integer>>(){
            public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2){
                return o2.size() - o1.size();
            }
        });  
        return bigList;
        
    }
    private void dfs(TreeNode root,int target,ArrayList<ArrayList<Integer>> bigList,ArrayList<Integer> list){
        if (root == null) return;
        list.add(root.val);
        target -= root.val;
        if (target == 0 && root.left == null && root.right == null) {
            bigList.add(new ArrayList<Integer>(list));    //这里需要重新new链表,这样大链表才会指向不同链表
        }
        dfs(root.left,target,bigList,list);
        dfs(root.right,target,bigList,list);
        
        list.remove(list.size() - 1);    //到叶子节点如果还没有找到路径,就要回退到父节点继续寻找
    }
    
}
36. 复杂链表的复制

题目描述
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

解题思路:
1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
3、拆分链表,将链表拆分为原链表和复制后的链表

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead){
        if (pHead == null) return null;
        // 插入新节点,但是不赋随即方向
        RandomListNode cur = pHead;
        while (cur != null) {
            RandomListNode tmp = new RandomListNode(cur.label);
            tmp.next = cur.next;
            cur.next = tmp;
            cur = tmp.next;
        }
        //重新引用到头
        cur  = pHead;
        // 为兄弟节点赋值
        while (cur != null) {
            RandomListNode tmp = cur.next;
            if (cur.random != null) {
                tmp.random = cur.random.next;
            }
            cur = tmp.next;
        }
        // 拆分
        cur = pHead;
        RandomListNode res = pHead.next;
        while (cur.next != null) {
            RandomListNode next = cur.next;
            cur.next = next.next;
            cur = next;
        }
        return res;
        
    }
}
37. 二叉搜索树与双向链表

题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解题思路:
结合画图比较好理解,使用递归二叉树的中序遍历,用pLast用于记录当前链表的末尾节点,然后将这些小段链表一个接一个地加到末尾。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private TreeNode pLast = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null) return null;
        //如果左子树为空,根节点为双向链表的头节点
        TreeNode head = Convert(pRootOfTree.left);
        if (head == null) {
            head = pRootOfTree;
        }
        //将小段链表一个接一个地加到末尾
        pRootOfTree.left = pLast;
        if (pLast != null) {
            pLast.right = pRootOfTree;
        }
        pLast = pRootOfTree;
        
        Convert(pRootOfTree.right);
        return head;
    }
}
38. 序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

解题思路:
这里使用前序遍历序列来序列化二叉树,当在遍历二叉树时碰到Null指针时,这些Null指针被序列化为一个特殊的字符“#”。结点之间的数值用逗号隔开。全局变量index用来反序化的时候被分割数组的下标。

import java.lang.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private 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 [] strArr = str.split(",");
        TreeNode node = null;
        if (!strArr[index].equals("#")) {
            node = new TreeNode(Integer.parseInt(strArr[index]));
            node.left = Deserialize(str);
            node.right = Deserialize(str);
        }
        return node;
       
  }
}
39. 字符串的排列

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

解题思路:固定第一个字符,递归取得首位后面的各种字符串组合,再把第一个字符与后面每一个字符交换,并同样递归获得首位后面的字符串组合,递归的出口,就是只剩一个字符的时候。

import java.util.ArrayList;
import java.util.Collections;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> resList = new ArrayList<>();
        if (str.length() == 0) return resList;
        backtracking(str.toCharArray(),resList,0);
        Collections.sort(resList);
        return resList;      
    }
    private void backtracking(char[] arr,ArrayList<String> list,int i) {
        if (i == arr.length - 1){
            if (!list.contains(new String(arr))) {
                list.add(new String(arr));
                return;
            }
        } else {
            for (int j = i; j < arr.length; j++) {
                swap(arr,i,j);
                backtracking(arr,list,i+1);
                //字符数组的顺序回到进入递归前的状态,这样才不会影响外部的遍历顺序。
                swap(arr,i,j);    
            }
        }
    }
    private void swap(char[]arr,int i, int j) {
        char t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值