算法刷题

O(1)时间实现数组增删查找:

  • 380. 常数时间插入、删除和获取随机元素
  • 710. 黑名单中的随机数

 

380. 常数时间插入、删除和获取随机元素

//O(1)时间可以插入元素:动态数组Arraylist,哈希表hashmap。但是哈希表没法用O(1)实现随机返回元素和删除元素。可以O(1)获得元素,想到动态数组。所以把哈希表和动态数组结合。对于删除元素,把要删除的元素和动态数组中最后一个元素交换位置,然后删除最后元素即可。哈希表中把索引作为值,键是要插入的数字。
import java.util.Random;
class RandomizedSet {

    List list;
    Map hm;
    Random rand=new Random();

    /** Initialize your data structure here. */
    public RandomizedSet() {
        list=new ArrayList();
        hm=new HashMap<>();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(hm.containsKey(val)) return false;
        //插入元素:
        hm.put(val,list.size());
        list.add(val);
        return true;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
       if(!hm.containsKey(val)) return false;
       //删除元素
       //获得最后一个元素
       int last=list.get(list.size()-1);
       //获得被删除元素的索引:
       int index=hm.get(val);
       //交换位置,即在arraylist中把最后一个元素放在被删除元素的位置:
       list.set(index,last);
       list.remove(list.size()-1);
       hm.put(last,index); //可被覆盖掉
       hm.remove(val);
       return true;
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        int i=rand.nextInt(list.size());
        int val=list.get(i);
        return val;
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

 

710. 黑名单中的随机数

//动态数组+hashmap
//注意:这里用的是hashset而不是arraylist的原因是:
//在remove的时候,可能会出现一种情况:N=2, blacklist=[1],如果list.remove(1)会判下标越界,因为arraylist.remove()参数可以是Index可以是val(此时想删1这个数字,系统会以为1是index而出现越界),而Hashset移除元素就是val,所以不会越界。

class Solution {
    Map hm;
    Set list;
    int whitelist;

    public Solution(int N, int[] blacklist) {
        hm=new HashMap<>();
        list=new HashSet<>();
       //获得白名单长度。目的是:[0,whitelist)中都是白名单。需要把这个范围中的黑名单映射到[whitelist,N)中的白名单。
        whitelist=N-blacklist.length;
        for(int i=whitelist;i it=list.iterator();
        for(int i=0;i

 

 

原地修改—利用双指针完成

  • 26.删除排序数组中的重复项
  • 27.移除元素
  • 83.删除排序链表中的重复元素
  • 283.移动零

 

26.删除排序数组中的重复项

//一:原地删除:一想到的方法有:交换待删除的元素和数组中最后一个元素位置然后删除最后一个。第二个就是双指针快慢指针。
class Solution {
    public int removeDuplicates(int[] nums) {
       if(nums==null)
         return 0;
       int slow=0,fast=0;
       while(fast

 

 

27.移除元素

//一:想到的方法:交换元素到最后,但是要注意如果待删除的元素数字在最后要做个判断
//实现的时候各种小问题,后来看了解答。
class Solution {
    public int removeElement(int[] nums, int val) {
         if(nums==null || nums.length==0)
            return 0;
         //想到的方法:交换元素到最后,但是要注意如果待删除的元素数字在最后要做个判断
         int left=0,right=nums.length-1;
         int len=nums.length;
         while(left<=right){
             if(nums[left]==val){
               while(nums[right]==val && right>left ){
                  right--;
                  len--;
                }
               //注意这个If判断!!如果left和right都指在了同一个位置 
               if(nums[right]!=val){
                  int temp=nums[left];
                  nums[left]=nums[right];
                  nums[right]=temp;    
                  right--;    
                }                             
                len--;                   
             }
             left++;            
         }
         return len;
    }
}


//二:快慢指针
class Solution {
    public int removeElement(int[] nums, int val) {
         if(nums==null || nums.length==0)
            return 0;
         //双指针:快慢指针
         int slow=0,fast=0;
         for(;fast

 

 

83.删除排序链表中的重复元素

//双指针:同理,快慢指针
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null)
           return head;
        //双指针:同理,快慢指针
        ListNode slow=head,fast=head;
        while(fast.next!=null){
            if(fast.next.val!=slow.val){
               slow.next=fast.next;
               slow=fast.next;
            } 
            fast=fast.next;
        }
        slow.next=null;
        return head;
    }
}

 

 

283.移动零

//要保证相对顺序,所以用快慢指针
class Solution {
    public void moveZeroes(int[] nums) {
        int slow=0,fast=0;
        for(;fast

 

 

 

数组去重

  • 316 题「去除重复字母」
  • 1081 题「不同字符的最小子序列」

 

316 题「去除重复字母」

//用栈
class Solution {
    public String removeDuplicateLetters(String s) {
        Stack stack=new Stack<>();
        boolean[] flag=new boolean[26];
        int[] count=new int[26];
        //初始化flag=false,然后再计数
        for(int i=0;is.charAt(i)){
                    if(count[stack.peek()-'a']!=0){
                        char c=stack.pop();
                        flag[c-'a']=false;
                    }
                    else{
                        break;
                    }
                }
                flag[index]=true;
                stack.push(s.charAt(i));
                //count[index]--;
            }
            count[index]--;
        }
        
        StringBuilder sb=new StringBuilder();
        while(!stack.isEmpty()){
            sb.append(stack.pop());
        }
        return sb.reverse().toString();
    }
}

 

1081 题「不同字符的最小子序列」

//和316题完全一个意思
class Solution {
    public String smallestSubsequence(String s) {
        Stack stack=new Stack<>();
        boolean[] flag=new boolean[26];
        int[] count=new int[26];

        for(int i=0;is.charAt(i)){
                    char top=stack.peek();
                    if(count[top-'a']!=0){
                        stack.pop();
                        flag[top-'a']=false;
                    }
                    else
                      break;
                }
                flag[index]=true;
                stack.push(s.charAt(i));
            }
            count[index]--;
        }

        StringBuilder sb=new StringBuilder();
        while(!stack.isEmpty()){
            sb.append(stack.pop());
        }
        return sb.reverse().toString();

    }
}

 

 

单调栈

单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。

  • 42. 接雨水
  • 496. 下一个更大元素 I
  • 503. 下一个更大元素 II
  • 84. 柱状图中最大的矩形
  • 739.每日温度
模板:
int res=0;
Stack stack=new Stack<>();
//如果希望栈中所有数据出栈,需要做一些其他操作,比如给数组最后添加一个数。或者给数组前后都添加一个数。
//注意:需要入栈的是索引呢还是值呢,根据题目来选择
for (遍历这个数组)
{
		while (!stack.isEmpty() && 栈顶元素和当前元素比较)
		{
			stack.pop(); //注意:需要判断这个元素需不需要保存,如果需要定义一个变量存储它
			更新结果res;
		}
		stack.push();
	
}

 

496. 下一个更大元素 I

//一:暴力
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] res=new int[nums1.length];
        int flag=0;
        for(int i=0;inums2[j+1] ){
                          j++;  
                }              
                if(j==nums2.length-1){
                        if(nums2[j]<=nums2[index])  res[flag++]=-1;
                        else if(nums2[j]>nums2[index])  res[flag++]=nums2[j];
                        break;
                }
                else{
                    res[flag++]=nums2[j+1];
                    break;
                }
            }
        }
        return res;
    }
}



//二:单调栈
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
       //单调栈,先只看nums2,再用一个hashmap维护每个元素对应的下一个最大元素,最后根据这个哈希表遍历Nums1
       Map hs=new HashMap<>();
       Stack stack=new Stack<>();
       for(int i=0;istack.peek()){
               int temp=stack.pop();
               hs.put(temp,nums2[i]);
           }
           stack.push(nums2[i]);
       }
       while(!stack.isEmpty()){
           hs.put(stack.pop(),-1);
       }

       //获得结果
       int[] res=new int[nums1.length];
       for(int i=0;i

 

 

503. 下一个更大元素 II

//一:顺序遍历,单调栈。本来一开始也想用hashmap,但是不行。因为比如当前1的下一个最大元素是3,之后另一个1最大元素是5,hs中key不能重复。
//关键:stack中不存数字,存索引! 循环数组 index%length
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        if(nums==null || nums.length==0)
           return new int[]{};
        //循环数组,把可比较的长度变成当前长度的2倍
        int[] res=new int[nums.length];
        Arrays.fill(res,-1);
        Stack stack=new Stack<>();
        int len=2*nums.length;
        int index=0;
        for(int i=0;inums[stack.peek()]){
                res[stack.pop()]=nums[i%nums.length];
            }
            stack.push(i%nums.length);            
        }
  
        return res;
    }
}


//二:逆序。官方题解是逆序,不太想的明白。有时间记得再写一下吧。

 

 

739.每日温度

//单调栈,栈中存的是下一个更高温度的索引
//和503一样
class Solution {
    public int[] dailyTemperatures(int[] T) {
       int[] res=new int[T.length];
       Arrays.fill(res,0);
       int index=0;
       Stack stack=new Stack<>();
       for(int i=0;iT[stack.peek()]){
              res[stack.peek()]=i-stack.peek();
              stack.pop();
           }
           stack.push(i);
       }
       return res;
    }
}

 

84. 柱状图中最大的矩形

//先暴力.思路是两个指针,一个往左,一个往右,找到连续的小于等于当前元素的
//但结果会超出时间限制,只能通过91/96个用例。暴力不太行。
class Solution {
    public int largestRectangleArea(int[] heights) {
        int res=0;

        for(int i=0;i=0 && heights[left-1]>=heights[i]){
                left--;
            } 
            while(right+1=heights[i]){
                right++;
            } 
            int temp=(right-left+1)*heights[i];
            if(res stack=new Stack<>();
       for(int i=0;i

 

42. 接雨水

//单调栈。比当前栈顶元素小,那么就入栈。否则的话,开始找边界计算面积了。
//综合来看,是个单调递减栈
//栈内还是存索引,方便计算左右边界
class Solution {
    public int trap(int[] height) {
       int res=0;
       Stack stack=new Stack<>();
       for(int i=0;iheight[stack.peek()]){
                int temp=stack.pop();
                if(stack.isEmpty()) break;
                //寻找左右边界。
                int left=stack.peek();
                int right=i;
                int h=Math.min(height[left],height[right])-height[temp];
                res+=h*(right-left-1);
           }
           stack.push(i);
       }
       return res;
    }
}

 

 

滑动窗口

  • 76.最小覆盖子串
  • 3.无重复字符的最长子串
  • 567.字符串的排列
  • 438.  找到字符串中所有字母异位词

 

模板:

模板:
//如果一个字符进入窗口,应该增加window计数器;如果一个字符将移出窗口的时候,应该减少window计数器;当valid满足need时应该收缩窗口;应该在收缩窗口的时候更新最终结果。
    public String minWindow(String s, String t) {
        HashMap need=new HashMap<>();
        HashMap window=new HashMap<>();
        for(int i=0;i


 

76.最小覆盖子串

class Solution {
    //如果一个字符进入窗口,应该增加window计数器;如果一个字符将移出窗口的时候,应该减少window计数器;当valid满足need时应该收缩窗口;应该在收缩窗口的时候更新最终结果。
    public String minWindow(String s, String t) {
        HashMap need=new HashMap<>();
        HashMap window=new HashMap<>();
        for(int i=0;i

 

3.无重复字符的最长子串

//一:滑动窗口,注意判断窗口要左移时候怎么做的
//思路有但是代码总有问题,最后参考了别人的答案。
class Solution {
    public int lengthOfLongestSubstring(String s) {
    //滑动窗口
    int left=0,right=0;
    int len=0;
    HashMap window=new HashMap<>();
    while(right1){
            char lefttemp=s.charAt(left);
            left++;
            window.put(lefttemp,window.getOrDefault(lefttemp,0)-1);
         } 
         if(len

 

438.  找到字符串中所有字母异位词

class Solution {
    public List findAnagrams(String s, String p) {
    //滑动窗口
    List res=new ArrayList<>();
    HashMap need=new HashMap<>();
    HashMap window=new HashMap<>();
    int left=0,right=0;
    int value=0;
    for(int i=0;i=p.length()){
            if(value==need.size()) res.add(left);
            char lefttemp=s.charAt(left);
            left++;
            if(need.containsKey(lefttemp)){
                if(need.get(lefttemp).equals(window.get(lefttemp)))
                value--;  
                window.put(lefttemp,window.getOrDefault(lefttemp,0)-1);
            }
        }
    }
    return res;
    }
}

 

 

567.字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //这个题和438一个意思,用滑动窗口
        HashMap window=new HashMap<>();
        HashMap need=new HashMap<>();
        //记录 满足条件的字符数
        int valid=0;
        int left=0,right=0;
        for(int i=0;i=s1.length()){
                if(need.size()==valid)  return true;
                char lefttemp=s2.charAt(left);
                left++;
                if(need.containsKey(lefttemp)){
                    if(need.get(lefttemp).equals(window.get(lefttemp))){
                        valid--;
                    }
                    window.put(lefttemp,window.getOrDefault(lefttemp,0)-1);
                }
            }
        }
        return false;

    }
}

 

 

 

二叉树系列

  • 226. 翻转二叉树
  • 114. 将二叉树展开为链表
  • 116. 填充二叉树节点的右侧指针
  • 654.最大二叉树
  • 105.从前序与中序遍历序列构造二叉树
  • 106.从中序与后序遍历序列构造二叉树
  • 652.寻找重复子树(Medium
  • 230. BST第K小的元素(Medium)
  • 538. 二叉搜索树转化累加树(Medium)
  • 450. 删除二叉搜索树中的节点(Medium)
  • 701.二叉搜索树中的插入操作(Medium)
  • 700.二叉搜索树中的搜索(Easy)
  • 98.验证二叉搜索树(Medium)
  • 297.二叉树的序列化与反序列化

 

 

226. 翻转二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//一:相当于一个前序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return null;
        //相当于一个前序遍历
        //交换两个节点的值
        TreeNode tmp=root.left;
        root.left=root.right;
        root.right=tmp;

        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}


 //二:用后序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return null;
        invertTree(root.left);
        invertTree(root.right);

        TreeNode tmp=root.left;
        root.left=root.right;
        root.right=tmp;
        return root;

    }
}



//中序不可以,因为中序是左根右,到根的时候交换左右节点,再遍历右的时候相当于把交换过来的左边的再进行一次操作。最终会导致左边节点交换了两次,右边不变。想要解决这个问题,可以翻转两次左边的树。
//三:中序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return null;
        invertTree(root.left);
        
        TreeNode tmp=root.left;
        root.left=root.right;
        root.right=tmp;
        
        //注意:这里还是left
        invertTree(root.left);
        return root;    
    }
}

 

 

116. 填充二叉树节点的右侧指针

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

//一:递归
//二叉树的问题难点重点:如何把题目的要求细化成每个节点需要做的事情
//简单的按照前序递归不行,因为没办法连接到在同一层但不是同一个父节点的两个节点。
//所以现在把每个节点做的事细化成:将每两个相邻节点连接起来

class Solution {
    public Node connect(Node root) {
       if(root==null) return null;
       connectTwoNode(root.left,root.right);
       return root;
    }

    public void connectTwoNode(Node node1,Node node2){
        if(node1==null || node2==null) return;
        node1.next=node2;

        //递归的每两个进行连接
        connectTwoNode(node1.left,node1.right);
        connectTwoNode(node2.left,node2.right);
        //继续连接不是同一个父节点但是在同一层且相邻的节点
        connectTwoNode(node1.right,node2.left);
    }

}



//二:不满足常量级的一个方法,学一下思路。使用层次遍历,然后用一个队列把每一层节点存好,然后进行连接。
//用linkedlist,它继承了queue的接口(ArrayList没有继承queue接口).
//注意,不能直接用queue,它和栈不一样,java api里queue是一个接口,stack是个类。
class Solution {
    public Node connect(Node root) {
       if(root==null) return null;
       Queue queue=new LinkedList<>();
       queue.offer(root);
       while(queue.size()>0){
          int len=queue.size();
          for(int i=0;i

 

 

114. 将二叉树展开为链表

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//一:笨方法。想到的是用一个队列,直接先序遍历把节点存到队列里。然后再转换成链表。
class Solution {
    Queue queue=new LinkedList<>();
    public void flatten(TreeNode root) {
       if(root==null)  return;
       //先序遍历存节点,然后再构建一个新的
       FirstTraverse(root);
       if(queue.size()==1) return;
       TreeNode cur=root;
       while(queue.size()>0){
           cur.right=queue.remove();
           cur.left=null;
           cur=cur.right;
       }
       return;
    }

    public void FirstTraverse(TreeNode root){
        if(root==null) return;
        queue.offer(root);
        FirstTraverse(root.left);
        FirstTraverse(root.right);
    }
}


//二:递归,没写出来。看了别人的答案。
class Solution {
    public void flatten(TreeNode root) {
       if(root==null)  return;
       
       flatten(root.left);
       flatten(root.right);

       TreeNode left=root.left;
       TreeNode right=root.right;

       root.left=null;
       root.right=left;

       TreeNode p=root;
       while(p.right!=null){
           p=p.right;
       }
       p.right=right;

       return;
    }
}

 

 

654.最大二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
 
 //用递归
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
         return build(nums,0,nums.length-1);
    }

    public TreeNode build(int[] nums,int left,int right){
         if(left>right) return null;
         int max=-1;
         int index=-1;
         for(int i=left;i<=right;i++){
             if(nums[i]>max){
                max=nums[i];
                index=i;
             } 
         }
         TreeNode root=new TreeNode(max);
         root.left=build(nums,left,index-1);
         root.right=build(nums,index+1,right);
         return root;
    }
    
}

 

 

 

105.从前序与中序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//一:递归:
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
         //根据前序遍历的第一个 ,找到其在中序遍历的位置。然后该点左边是其左子树节点,右边是其右子树节点。
         return build(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
    }

    public TreeNode build(int[] preorder,int preStart,int preEnd,int[] inorder,int inStart,int inEnd){
        if(preStart>preEnd) return null;
        int rootval=preorder[preStart];
        int index=-1;
        for(int i=inStart;i<=inEnd;i++){
            if(inorder[i]==rootval){
                index=i;
                break;
            }
        }
        //rootval是根节点的值
        TreeNode root=new TreeNode(rootval);
        root.left=build(preorder,preStart+1,preStart+index-inStart,inorder,inStart,index-1);
        root.right=build(preorder,preStart+index-inStart+1,preEnd,inorder,index+1,inEnd);
        
        return root;
    }
}



 //对于一的优化:由于每次是遍历一遍inorder数组然后找到对应的索引下标。由于树中没有重复的元素,可以直接用hashmap把索引下标存起来。
class Solution {
     Map map;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
         map=new HashMap<>();
         for(int i=0;ipreEnd) return null;
        int rootval=preorder[preStart];
        int index=map.get(rootval);
        
        //rootval是根节点的值
        TreeNode root=new TreeNode(rootval);
        root.left=build(preorder,preStart+1,preStart+index-inStart,inorder,inStart,index-1);
        root.right=build(preorder,preStart+index-inStart+1,preEnd,inorder,index+1,inEnd);
        
        return root;
    }
}





//二:看别人的题解还有迭代法,没看明白。有时间再看。

 

 

106.从中序与后序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

 //解法和思路应该和中序前序差不多。同样用递归。
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        //区间是左右都闭合。
        //根节点是后序的最后一个。
        return build(inorder,0,inorder.length-1,postorder,0,postorder.length-1);    
    }

    public TreeNode build(int[] inorder,int inStart,int inEnd,int[] postorder,int postStart,int postEnd){
        if(postStart>postEnd) return null;
        int rootval=postorder[postEnd];
        int index=-1;
        for(int i=inStart;i<=inEnd;i++){
              if(inorder[i]==rootval){
                index=i;
                break;
            }
        }

        TreeNode root=new TreeNode(rootval);
        root.left=build(inorder,inStart,index-1,postorder,postStart,postStart+index-inStart-1);
        root.right=build(inorder,index+1,inEnd,postorder,postStart+index-inStart,postEnd-1);
        return root;
    }
}

 

 

652.寻找重复子树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

 
//本来的思路是:哈希表存,key是节点,值是节点先序遍历得到的字符串。然后比较值,res存结果。
//但是对于如果得到每个节点及其先序遍历的字符串没想出来,看了别人的解答得到了一点启发:存序列化的二叉树。
//注意,res存的节点是地址。所以可能会出现res中结果重复。所以最开始的思路不行。哈希表的键改成string,值改成其出现的次数。
//重点难点:如何把二叉树序列化 
class Solution {
    Map map=new HashMap<>();
    List res=new ArrayList<>();
    public List findDuplicateSubtrees(TreeNode root) {
        //哈希表存,key是节点,值是节点先序遍历得到的字符串,res存结果
        traverse(root);
        return res;
    }

    public String traverse(TreeNode root){
         if(root==null) return "#";
         String left=traverse(root.left);
         String right=traverse(root.right);

         String tmp=left+","+right+","+root.val;

         if(map.containsKey(tmp)){
               int val=map.get(tmp);
               if(val==1)
                  res.add(root);                       
         }
         map.put(tmp,map.getOrDefault(tmp,0)+1); 
         return tmp;
    }

}

 

 

 

230. BST第K小的元素

 //一:二叉搜索树特性,可以知道其中序遍历是升序的。所以可以用中序遍历走一遍,然后再找第k小的结果;
class Solution {
    List list=new ArrayList<>();
    public int kthSmallest(TreeNode root, int k) {
        traverse(root);
        int val=list.get(k-1);
        return val;

    }

    public void traverse(TreeNode root){
        if(root==null) return;
        traverse(root.left);
        list.add(root.val);
        traverse(root.right);
        return;
    }
}

//基于一的改进:这种方法最坏情况下时间复杂度是O(N),就是二叉树每个节点都访问过一遍了。
//如果想要达到对数级别复杂度的方法,需要改进二叉搜索树的结构,让其多维护一个信息,即每个节点的左孩子个数。
class Solution {
    int index=0;
    int res=-1;
    public int kthSmallest(TreeNode root, int k) {
        traverse(root,k);
        return res;

    }

    public void traverse(TreeNode root,int k){
        if(root==null) return;
        traverse(root.left,k);
        
        index++;
        if(index==k){
            res=root.val;
            return;
        }
        traverse(root.right,k);
        return;
    }
}



//二:分治法。其本质就是多计算一个每个节点的左孩子个数,然后和K比较。
class Solution {
    int res=-1;
    public int kthSmallest(TreeNode root, int k) {
        int left=count(root.left);
        if(left+1==k)
          return root.val;
        else if(left+1>k){
            res=kthSmallest(root.left,k);
        }
        else
           res=kthSmallest(root.right,k-left-1);
        return res;
    }

    public int count(TreeNode root){
        if(root==null) return 0;
        int left=count(root.left);
        int right=count(root.right);
        return (left+right+1);
    }
}

 

 

538. 二叉搜索树转化累加树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

 //BST相关的问题常见思路:BST 左小右大的特性提升算法效率/利用中序遍历的特性满足题目的要求
 //本题用中序遍历,让其降序排列,就是右中左
class Solution {
    int sum=0;
    public TreeNode convertBST(TreeNode root) {
        if(root==null) return null;
        convert(root);
        return root;
    }

   //注意这个逆序降序怎么打印的!写的时候脑子浆糊了半天递归不出来。
    public void convert(TreeNode root){
        if(root==null) return;
        convert(root.right);
        //真正累加的关键
        sum+=root.val;
        convert(root.left);
        return;
    }

}

 

 

450. 删除二叉搜索树中的节点

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
//一:递归,想半天没写对。看了一下别人的解答。重点:如果待删节点有两个孩子:将欲删除节点的左子树成为其右子树的最左节点的左子树
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root==null) return null;
        
        if(root.valkey){
            root.left=deleteNode(root.left,key);
        }
        else if(root.val==key){
            if(root.left==null && root.right==null){
                return null;
            }
            else if(root.left==null || root.right==null){
                if(root.left==null)
                   return root.right;
                else 
                   return root.left;
            }
            //注意这里怎么写的!!!!!一直没想出来!!!!
            //找到待删节点右子树的最小节点,待删节点的左子树一定比最小节点小,所以直接把其左子树放到最小节点的左子树,返回带删节点的右子树就行。
            else if(root.left!=null && root.right!=null){
                TreeNode node=root.right;
                while(node.left!=null){
                   node=node.left;
                }
                node.left=root.left;
                return root.right;
            }
        }
        return root;

    }
}

 

701.二叉搜索树中的插入操作

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root==null){
            return new TreeNode(val);
        }
        if(root.val>val){
            root.left=insertIntoBST(root.left,val);
        }
        else if(root.val

 

700.二叉搜索树中的搜索

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//一:递归
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null) return null;
        if(root.val==val){
            return root;
        }
        else if(root.val>val)
            return searchBST(root.left,val);      
        else
            return searchBST(root.right,val);
    }
}


 //二:迭代
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null) return null;
        while(root!=null){
            if(root.val==val){
                return root;
            }
            else if(root.val>val){
                root=root.left;
            }
            else if(root.val

 

98.验证二叉搜索树(Medium)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
 
 //一:还是利用二叉搜索树中序遍历递增的性质。中序遍历看看是不是升序的
class Solution {
    List ls=new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
         traverse(root);
         for(int i=0;i=ls.get(i+1))
                   return false;
             }
         }
         return true;
    }

    public void traverse(TreeNode root){
       if(root==null) return;
       traverse(root.left);
       ls.add(root.val);         
       traverse(root.right);
       return;
    }
}


 //同样利用中序遍历的性质,但是比上一个优化,这里多记录一个上一个数字,pre
class Solution {
    long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
         if(root==null)  return true;
         if(!isValidBST(root.left))
           return false;
         
         if(pre>=root.val) return false;
         pre=root.val;

        return isValidBST(root.right);

    }
}



//二:递归,一个常见的思路是:通过使用辅助函数,增加函数参数列表,在参数中携带额外信息,将这种约束传递给子树的所有节点
class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root==null || (root.left==null && root.right==null)) return true;
        return traverse(root,null,null);

    }

    public boolean traverse(TreeNode root,TreeNode min,TreeNode max){
           if(root==null) return true;
           if(min!=null && min.val>=root.val) return false;
           if(max!=null && max.val<=root.val) return false;

           return traverse(root.left,min,root) && traverse(root.right,root,max);

    }

}

 

 

297.二叉树的序列化与反序列化

 

 

 

 

回文系列的判断

 

你可能感兴趣的:(算法)