27.二叉树的镜像
思路:左右子节点交换
class Solution {
public void mirror(TreeNode root) {
if(root == null) return;
if(root.left!=null || root.right!=null){
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
mirror(root.left);
mirror(root.right);
}
}
}
28.对称的二叉树
思路:根左右=根右左
class Solution {
public boolean isSymmetric(TreeNode root) {
return isSymmetric(root, root);//镜像左==右
}
public boolean isSymmetric(TreeNode root1, TreeNode root2){
if(root1 == null && root2 == null) return true; //都空相等
if(root1 == null || root2 == null) return false; //存在一个空,不相等
if(root1.val == root2.val){//根节点相等,判断左右节点是否镜像
return isSymmetric(root1.left, root2.right) && isSymmetric(root1.right, root2.left);
}
return false;
}
}
29.顺时针打印链表
思路:定义四个方向对应的矩阵行列的加减,每次碰壁转弯90度。
class Solution {
public int[] printMatrix(int[][] matrix) {
if(matrix.length == 0 || matrix[0].length == 0) return new int[0];
int m = matrix.length;
int n = matrix[0].length;
int[] printArray = new int[m*n];//注意写法
boolean[][] visited = new boolean[m][n];
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
visited[i][j]=false;
int[] dx = {0, 1, 0, -1}, dy= {1, 0, -1, 0};//注意方向,与坐标轴无关,判断矩阵!!!
int x=0, y=0, d=0;
for(int i=0; i<m*n; i++){
printArray[i] = matrix[x][y];
visited[x][y] = true;
int a = x+dx[d], b = y+dy[d];
if(a<0 || a>=m || b<0 || b>=n || visited[a][b]==true){
d = (d+1)%4;
a = x+dx[d];
b = y+dy[d];
}
x=a;
y=b;
}
return printArray;
}
}
30.包含min函数的栈
思路:最小值栈
class MinStack {
Stack<Integer> numStack = new Stack<Integer>();
Stack<Integer> min = new Stack<Integer>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
numStack.push(x);
if(min.isEmpty()) min.push(x);
else{
if(x<min.peek()) min.push(x);
else min.push(min.peek());
}
}
public void pop() {
if(!numStack.isEmpty()){
numStack.pop();
min.pop();
}
}
public int top() {
if(!numStack.isEmpty()) return numStack.peek();
return -1;
}
public int getMin() {
if(!numStack.isEmpty()) return min.peek();
return -1;
}
}
31.栈的压入弹出序列
思路:按照压入序列入栈,如果弹出序列当前值相等则弹出,判断最后栈是否为空。
class Solution {
public boolean isPopOrder(int [] pushV,int [] popV) {
Stack<Integer> stack = new Stack<Integer>();
if(popV.length>pushV.length) return false;//弹出一定比压入短
for(int pushId=0, popId=0; pushId<pushV.length; pushId++){
stack.push(pushV[pushId]);//入栈
while(popId<popV.length && !stack.isEmpty() && stack.peek()==popV[popId] ){//如果相等,并且可以弹出,一直弹出
stack.pop();
popId++;//下一个弹出的值得索引
}
}
if(stack.isEmpty()) return true;
return false;
}
}
32.从上到下打印二叉树
32-1 不分行打印
思路:利用队列存储树节点
class Solution {
public List<Integer> printFromTopToBottom(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<TreeNode>();//存储节点的队列
List<Integer> printArray = new ArrayList<Integer>();//存储输出的list
if(root==null) return printArray;
queue.add(root);
while(!queue.isEmpty()){//不空
TreeNode cur = queue.poll();
if(cur!=null){
queue.add(cur.left);//左右放到队列里,先进先出
queue.add(cur.right);
printArray.add(cur.val);
}
}
return printArray;
}
}
32-2 分行打印
思路:记录当前队列中节点的个数,bfs
public List<List<Integer>> printFromTopToBottom(TreeNode root) {
List<List<Integer>> printList = new ArrayList<>();
if(root==null) return printList;//空
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
int k = queue.size();//每次都把队列排空,类似bfs的思想,所以记录当前队列个数。
while(!queue.isEmpty()){
List<Integer> tmp = new ArrayList<>();
while(k>0){
TreeNode cur = queue.poll();
if(cur!=null){
tmp.add(cur.val);
if(cur.left!=null) queue.add(cur.left);
if(cur.right!=null) queue.add(cur.right);
}
k--;
}
printList.add(tmp);
k = queue.size();
}
return printList;
}
32-3 之字形打印二叉树:
思路:reverse
class Solution {
public List<List<Integer>> printFromTopToBottom(TreeNode root) {
List<List<Integer>> printList = new ArrayList<>();
if(root==null) return printList;//空
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
int k = queue.size();//每次都把队列排空,类似bfs的思想,所以记录当前队列个数。
int level = 1;
while(!queue.isEmpty()){
List<Integer> tmp = new ArrayList<>();
while(k>0){
TreeNode cur = queue.poll();
if(cur!=null){
tmp.add(cur.val);
if(cur.left!=null) queue.add(cur.left);
if(cur.right!=null) queue.add(cur.right);
}
k--;
}
if((level&1)==0) Collections.reverse(tmp);//reverse
printList.add(tmp);
k = queue.size();
level++;
}
return printList;
}
}
33. 二叉搜索树的后序遍历序列
思路:后序遍历最后一个为根节点,前面一部分都比根小,后面一部分都比根大。
class Solution {
public boolean verifySequenceOfBST(int [] sequence) {
if(sequence==null) return false;
if(sequence.length==0) return true;
int root= sequence[sequence.length-1];
int i = 0;
for(i=0; i<sequence.length-1;i++){
if(sequence[i]>root)//找到第一个大于根节点的节点,左边是左子树,右边是右子树
break;
}
int[] left = new int[i];
for(int k=0;k<i;k++){
left[k]=sequence[k];
}
int j=i;
for(j=i; j<sequence.length-1;j++){
if(sequence[j]<root)
return false;//重点,如果遍历不到倒数第二个,直接returnfalse
}
int[] right = new int[j-i];
for(int k=i;k<j;k++){
right[k-i]=sequence[k];
}
boolean leftbool = true;
if(i>0) leftbool=verifySequenceOfBST(left);
boolean rightbool = true;
if(j>0) rightbool = verifySequenceOfBST(right);
return leftbool && rightbool;
}
}
34.二叉树中和为某一值的路径
思路:开list记录path,dfs左子节点,dfs右子节点。
class Solution {
public List<List<Integer>> ans= new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> findPath(TreeNode root, int sum) {
dfs(root,sum);
return ans;
}
public void dfs(TreeNode root, int sum){
if(root==null) return;
path.add(root.val);
sum = sum-root.val;
if(root.left==null && root.right==null && sum==0) ans.add(new ArrayList<>(path));//如果相等,ans里面加上path答案(new一个)
dfs(root.left, sum);//左子树
dfs(root.right, sum);//右子树
path.remove(path.size()-1);//算完当前节点移除
}
}
35.复杂链表的复刻
思路:原链表的下一个节点为复制节点。注意new复制的节点。并在最后,复原原链表。
class Solution {
public ListNode copyRandomList(ListNode head) {
ListNode p = head;
while(p!=null){//插入
ListNode tmp = p.next;
ListNode copyNode = new ListNode(p.val);
p.next = copyNode;
copyNode.next = tmp;
p = tmp;
}
p=head;
while(p!=null){//random域
if(p.random!=null){
p.next.random = p.random.next;
}
p = p.next.next;
}
ListNode dummy = new ListNode(-1);//虚拟头结点
ListNode cur = dummy;
p = head;
while(p!=null){//拆出复制的链表,复原原来的链表
cur.next = p.next;
p.next = p.next.next;
cur = cur.next;
p = p.next;
}
return dummy.next;
}
}
36.二叉搜索树与双向链表
思路:在中序递归的基础上改,用一个pre指针保存中序遍历的前一个结点。因为是中序遍历,遍历顺序就是双线链表的建立顺序;每一个结点访问时它的左子树肯定被访问过了,所以放心大胆的改它的left指针,不怕树断掉;同理,pre指向的结点保存的数肯定小于当前结点,所以其左右子树肯定都访问过了,所以其right指针也可以直接改。最后需要一直向左找到双向链表的头结点。**
class Solution {
TreeNode pre = null;
public TreeNode convert(TreeNode root) {
dfs(root);
while(root!=null && root.left!=null) root = root.left;//返回root最左边
return root;
}
public void dfs(TreeNode root){
while(root==null) return;
dfs(root.left);//递归左侧
root.left=pre;//root的上一个为pre
if(pre!=null) pre.right = root;//pre的下一个为root
pre = root;
dfs(root.right);//递归右边
}
}
37. 序列化二叉树
思路:序列化主要是对二叉树的广度优先遍历,可以每序列化一个节点,就把他的节点按照先左后右的顺序压入队列,然后从队列前面取出,再将其子树的左右子树压入队列。空为#,各个节点之间用!来分开反序列化将序列化的字符串按!分割即可。然后放进队列,重建即可。
class Solution {
// Encodes a tree to a single string.
String serialize(TreeNode root) {
if(root==null)
return "#!";
String res = root.val+"!";
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
root = q.poll();
if(root.left!=null){
res+=root.left.val+"!";
q.offer(root.left);
}else{
res+="#!";
}
if(root.right!=null){
res+=root.right.val+"!";
q.offer(root.right);
}else{
res+="#!";
}
}
return res;
}
// Decodes your encoded data to tree.
TreeNode deserialize(String data) {
String[] values = data.split("!");
int index = 0;
TreeNode root = generateNodeByString(values[index++]);
Queue<TreeNode> q = new LinkedList<>();
if(root != null){
q.offer(root);
}
TreeNode node = null;
while(!q.isEmpty()){
node = q.poll();
node.left = generateNodeByString(values[index++]);
node.right = generateNodeByString(values[index++]);
if(node.left != null){
q.offer(node.left);
}
if(node.right != null){
q.offer(node.right);
}
}
return root;
}
public TreeNode generateNodeByString(String val){
if(val.equals("#"))
return null;
return new TreeNode(Integer.valueOf(val));
}
}
38. 字符串的排列
思路:O(n!)
由于有重复元素的存在,先将所有数从小到大排序,这样相同的数会排在一起;从左到右依次枚举每个数,每次将它放在一个空位上;对于相同数,我们人为定序,就可以避免重复计算:我们在dfs时记录一个额外的状态,记录上一个相同数存放的位置 start,我们在枚举当前数时,只枚举start+1,start+2…,nstart+1,start+2,…,n 这些位置。不要忘记递归前和回溯时,对状态进行更新。
时间复杂度分析:搜索树中最后一层共 n! 个节点,前面所有层加一块的节点数量相比于最后一层节点数是无穷小量,可以忽略。且最后一层节点记录方案的计算量是O(n),所以总时间复杂度是 O(n×n!)。
class Solution {
public List<List<Integer>> permutation(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i - 1])) continue;
used[i] = true;
tempList.add(nums[i]);
backtrack(list, tempList, nums, used);
used[i] = false;
tempList.remove(tempList.size() - 1);
}
}
}
}