目录
剑指Offer31~40
栈的压入,弹出序列
题链
题解:定义两个指针i1,i2分别指向压入数组arr1和弹出数组arr2首元素,定义一个栈s,当s中栈顶元素等于arr2[i2],就弹出栈顶元素,i2++;否则当arr1[i1]不等于arr2[i2]时,将arr1[i1]压入栈s中直到arr1[i1]等于arr2[i2],重复整个过程直到两个数组中的一个数组被遍历完.如果数组arr2遍历完,说明弹出序列是可行的,否则判断arr2中剩余的每个元素和栈s中的元素是否匹配.
public boolean validateStackSequences(int[] pushed, int[] popped) {
int n = pushed.length;
if(n == 0){
return true;
}
Deque<Integer> s = new LinkedList<>();
int i1 = 0,i2 = 0;
while(i1 < n && i2 < n){
if(s.size() != 0 && s.peek() == popped[i2]){
s.pop();
i2++;
continue;
}
while(i1 < n && pushed[i1] != popped[i2]){
s.push(pushed[i1]);
i1++;
}
i1++;
i2++;
}
if(i2 == n){
return true;
}else{
for(;i2 < n; i2++){
if(s.peek() != popped[i2]){
return false;
}
s.pop();
}
}
return s.isEmpty();
}
从上到下打印二叉树
题链
题解:就是二叉树的层序遍历.利用一个队列去存储每层的节点然后存储到数组中即可.
public int[] levelOrder(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null){
return new int[0];
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
while(size != 0){
TreeNode cur = q.poll();
list.add(cur.val);
if(cur.left != null){
q.offer(cur.left);
}
if(cur.right != null){
q.offer(cur.right);
}
size--;
}
}
int[] ans = new int[list.size()];
for(int i = 0; i < ans.length; i++){
ans[i] = list.get(i);
}
return ans;
}
从上到下打印二叉树II
题链
题解:和上面一道题不能说一模一样…只不过存储节点的结构换了一下.
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if(root == null){
return list;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
List<Integer> l = new ArrayList<>();
while(size != 0){
TreeNode cur = q.poll();
l.add(cur.val);
if(cur.left != null){
q.offer(cur.left);
}
if(cur.right != null){
q.offer(cur.right);
}
size--;
}
list.add(l);
}
return list;
}
从上到下打印二叉树III
题链
题解:其实总的思路还是相同的,只不过需要记录层数的奇偶来控制每层是从左到右存储还是从右到左存储
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if(root == null){
return list;
}
Queue<TreeNode> q = new LinkedList<>();
int flag = 1;
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
List<Integer> l = new ArrayList<>();
while(size != 0){
TreeNode cur = q.poll();
l.add(cur.val);
if(cur.left != null){
q.offer(cur.left);
}
if(cur.right != null){
q.offer(cur.right);
}
size--;
}
//偶数层翻转一下存储的顺序.
if(flag % 2 == 0){
reverse(l);
}
flag++;
list.add(l);
}
return list;
}
public void reverse(List<Integer> list){
int n = list.size();
int l = 0,r = n-1;
while(l < r){
int tmp = list.get(l);
list.set(l,list.get(r));
list.set(r,tmp);
l++;
r--;
}
}
二叉搜索树的后序遍历序列
题链
题解:这道题我认为还是比较难的,考察的是二叉搜索树的定义:一个节点的左子树的所有节点的值都小于该节点的值且该节点的右子树的所有节点的值都大于该节点的值.因此二叉搜索树的后续萹遍历序列从后往前依次是根节点,右子树,左子树.基于这个特点递归进行判断.
public boolean verifyPostorder(int[] postorder) {
if(postorder == null){
return true;
}
return getRes(postorder,0,postorder.length-1);
}
public boolean getRes(int[] post,int i,int j){
if(i >= j){
return true;
}
int p = i;
while(post[p] < post[j]){
p++;
}
int m = p;
while(post[p] > post[j]){
p++;
}
return p == j && getRes(post,i,m-1) && getRes(post,m,j-1);
}
二叉树中和为某一值的路径
题链
题解:一道非常经典的回溯题.先序递归遍历.
public List<List<Integer>> list = new ArrayList<>();
public List<Integer> cur = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
if(root == null){
return list;
}
dfs(root,target);
return list;
}
public void dfs(TreeNode root,int t){
if(root == null){
return;
}
if(root.left == null && root.right == null){
if(t == root.val){
List<Integer> tmp = new ArrayList<>(cur);
tmp.add(root.val);
list.add(tmp);
}
return;
}
cur.add(root.val);
dfs(root.left,t-root.val);
dfs(root.right,t-root.val);
cur.remove(cur.size()-1);
}
复制链表的复制
题链
题解:这道题的难点就是如何去处理random节点.对于next节点和节点的值,我们只需要依次创造新节点去连接起来即可,而我们无法直接让新创建的节点的random指向原链表中的某个节点.解决方法就是用哈希表存储原节点和新节点的映射关系,key是原节点,value是新节点.这样我们就可以通过原节点的random节点映射到对应的新节点.
public Node copyRandomList(Node head) {
if(head == null){
return head;
}
Map<Node,Node> map = new HashMap<>();
Node yummy = new Node(-1);
Node cur = yummy;
Node cur1 = head;
while(cur1 != null){
Node tmp = new Node(cur1.val);
cur.next = tmp;
cur = tmp;
map.put(cur1,tmp);
cur1 = cur1.next;
}
cur = yummy.next;
cur1 = head;
while(cur != null){
if(cur1.random == null){
cur.random = null;
}else{
cur.random = map.get(cur1.random);
}
cur1 = cur1.next;
cur = cur.next;
}
return yummy.next;
}
二叉搜索树与双向链表
题链
题解:定义一个prev节点来表示某个节点的前驱节点.中序遍历二叉搜索树,每次更新过程中更新prev节点和root节点的指向.prev.right=root,root.left=prev.同时更新prev为旧的root
Node prev = null;
Node head;
public Node treeToDoublyList(Node root) {
if(root == null){
return root;
}
func(root);
head.left = prev;
prev.right = head;
return head;
}
public void func(Node root){
if(root == null){
return;
}
func(root.left);
if(prev != null){
prev.right = root;
}else{
head = root;
}
root.left = prev;
prev = root;
func(root.right);
}
序列化二叉树
题链
题解:首先我们选择先序来序列化二叉树(这个顺序是无要求的,只要保证序列化和反序列化的顺序一致就行).为了便于反序列化,我们在序列化的时候还要记录每个null节点.这样在反序列化的时候我们就可以保证每个节点的左右节点不会因为空节点而发生错位.
int index = 0;
public String serialize(TreeNode root) {
if (root == null) {
return "null,";
}
String s = "";
s += root.val + ",";
s += serialize(root.left);
s += serialize(root.right);
return s;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] strs = data.split(",");
return des(strs);
}
public TreeNode des(String[] data) {
if (index == data.length) {
return null;
}
TreeNode root = new TreeNode();
if ("null".equals(data[index])) {
index++;
return null;
}
root.val = Integer.parseInt(data[index]);
index++;
root.left = des(data);
root.right = des(data);
return root;
}
字符串的排列
题链
题解:全排列问题(回溯).递归交换两两节点的值.同时为了简单处理不重复的存储,我们选取HashSet来存储每个排列.
String[] ans;
Set<String> set = new HashSet<>();
int n;
public String[] permutation(String s) {
n = s.length();
int len = 1;
for(int i = 2; i <= n; i++){
len *= i;
}
dfs(s,0);
int k = 0;
ans = new String[set.size()];
for(String str : set){
ans[k++] = str;
}
return ans;
}
public void dfs(String s,int index){
if(index == n){
String cur = new String(s);
set.add(cur);
return;
}
for(int i = index; i < n; i++){
s = swap(s,i,index);
dfs(s,index+1);
s = swap(s,i,index);
}
}
public String swap(String s,int i,int j){
StringBuilder ss = new StringBuilder(s);
char ch = ss.charAt(i);
ss.setCharAt(i,ss.charAt(j));
ss.setCharAt(j,ch);
return new String(ss);
}