剑指刷题

一、数组与矩阵

  • 3. 数组中重复的数字
  • 4. 二维数组中的查找
  • 5. 替换空格
  • 29. 顺时针打印矩阵
  • 50. 第一个只出现一次的字符位置

 

    3. 数组中重复的数字

//一:首先想到:暴力解法,两个for循环,一个从前,一个从后。但是时间复杂度太高了
class Solution {
    public int findRepeatNumber(int[] nums) {
       
       int n=nums.length;
       int result=-1;
       for(int i=0;i<nums.length;i++)
       {
           for(int j=n-1;j>i;j--)
           {
               if(nums[i]==nums[j])
               {
                   result=nums[i];
                   return result;
               }
           }
       }
       return result;
    }
}


// 二:使用HashSet,它是一个不允许有重复元素的集合。遍历数组,如果集合里有该元素,就返回结果,如果没有,把该元素加入集合里。
class Solution {    
     public int findRepeatNumber(int[] nums) {
        //使用HashSet,是一个不允许有重复元素的集合。
        Set<Integer> hashset=new HashSet<Integer>();
        int result=-1;
        for(int i=0;i<nums.length;i++)
        {
            if(hashset.contains(nums[i])){
                result=nums[i];
                return result;
            }
            else
                hashset.add(nums[i]);
        }
        return result; 
        }
}


//三:相比于二优化了一点,用数组代替HashSet.
//新建一个数组,把其所有值先都赋值为-1。然后for遍历旧数组,把其填入新数组中。由于数组长度为n,且其元素要求0~n-1,因此可设定newnus[temp]=temp,即newnums[0]=0,newnums[2]=2,如果在遍历时发现要填入的元素在新数组中已被填入。那么久返回它。

//但是使用数组绝对会有性能的提高,主要表现在如下的两个方面:

//哈希表 (HashSet) 底层是使用数组 + 链表或者红黑树组成的,而且它的数组也是用不满的,有加载因子的。所以使用数组来代替哈希表,能节省空间
//哈希表在判重的时候需要经过哈希计算,还可能存在哈希冲突的情况,而使用数组则可以直接计算,所以使用数组访问性能更好。


 
class Solution {    
     public int findRepeatNumber(int[] nums) {
        //使用数组代替HashSet,新建一个数组,把其所有值都先赋值为-1
        int[] newnums=new int[nums.length];
        Arrays.fill(newnums,-1);

        int result=-1;
        for(int i=0;i<nums.length;i++)
        {
           int temp=nums[i];
           if(temp==newnums[temp]){
                result=nums[i];
                return result;
            }
            else{
                newnums[temp]=temp;
            }           
        }
        return result;       
    }
}


//四:最优算法,原地算法。思想和三一样,就是让nums[i]=i,通过交换来实现。
class Solution {    
     public int findRepeatNumber(int[] nums) {
     //原地算法:
     for(int i=0;i<nums.length;i++)
     {
          while(nums[i]!=i){
             int temp=nums[i];
             if(temp==nums[temp])
                return temp;

              //交换:
              nums[i]=nums[temp];
              nums[temp]=temp;
          }
     }
     return -1;
    }
}

 

4. 二维数组中的查找

//一:暴力解法,遍历二维数组,时间复杂度高了,且没有利用到题目每行每列递增的条件。   
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
         int row=matrix.length;
         if(row==0)
               return false;
         int col=matrix[0].length;
         if(col==0)
               return false;
         for(int i=0;i<row;i++)
         {
             for(int j=0;j<col;j++)
             {
                 if(target==matrix[i][j])
                    return true;
             }
         }
         return false; 
    }
}




//二:利用题目条件,行列都是递增的。类似二叉搜索树。可选择的点要满足一边递减,一边递增。因此可选点:左下,右上。
//如若关键点选择左下:
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {

         int row=matrix.length;
         if(row==0)
               return false;
         int col=matrix[0].length;
         if(col==0)
               return false;
         int i=row-1,j=0;
         while(i>=0 && j<=col-1){
             int start=matrix[i][j];
             if(target<start) i--;
             else if(target>start) j++;
             else 
               return true;
         }
         return false;
    }
}

 

5. 替换空格

//一:遍历,用stringbuilder,注意为了防止下标越界,要判断i为sb中倒数第二个字符的时候。
class Solution {
    public String replaceSpace(String s) {
      StringBuilder sb=new StringBuilder(s);
      for(int i=0;i<sb.length();i++)
      {
          char temp=sb.charAt(i);
          if(i!=sb.length()-1){
             if(temp==' ' ){
              sb.replace(i,i+1,"%20");
             }    
          }
          else{
              if(temp==' '){
                  sb.deleteCharAt(i);
                  sb.append("%20");
              }
          }          
      }
      return sb.toString();    
    }
}



//二:遍历,用stringbuilder,建立的是空的stringbuilder,然后一个一个添加即可:
//两种方法用时内存一样
class Solution {
    public String replaceSpace(String s) {
      StringBuilder sb=new StringBuilder();
      for(int i=0;i<s.length();i++)
      {
          char temp=s.charAt(i);
          if(temp!=' ')
            sb.append(temp);
          else
            sb.append("%20");
      }
      return sb.toString();    
    }
}

 

50. 第一个只出现一次的字符位置

//一:从前的索引和lastIndexOf最后出现该字符的索引,为了防止“cc”这样的字符串,要使用一个stringbuilder类型的记录已经被筛选过的字符。
class Solution {
    public char firstUniqChar(String s) {
       if(s==null)
         return ' ';
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<s.length();i++){
            int right=s.lastIndexOf(s.charAt(i)+"");
            if(i==right){
               if(sb.indexOf(s.charAt(i)+"")==-1)
                  return s.charAt(i);
            }
            else{
                sb.append(s.charAt(i));
            }
        }
        return ' ';
    }
}


// 一.2:用IndexOf和lastIndexOf,比前面的好一点
class Solution {
    public char firstUniqChar(String s) {
        if(s==null)
         return ' ';
        for(int i=0;i<s.length();i++){
            if(s.indexOf(s.charAt(i))==s.lastIndexOf(s.charAt(i)))
              return s.charAt(i);
        }
        return ' ';
    }
}



//二:使用hashMap,第一次for循环,把字符串s中的值和其出现的次数一一存入hashset中。第二次for循环来找到值为1对应的Key
class Solution {
    public char firstUniqChar(String s) {
        if(s==null)
         return ' ';
        //使用HashMap
        HashMap<Character,Integer> hs=new HashMap<Character,Integer>();
        for(int i=0;i<s.length();i++){
            if(!hs.containsKey(s.charAt(i)))
                hs.put(s.charAt(i),1);
            else{
                int val=hs.get(s.charAt(i));
                hs.replace(s.charAt(i),val,val+1);
            }              
        }
        //遍历字符串,对应的找到hashSet中val为1的第一个key
        for(int i=0;i<s.length();i++){
            int val=hs.get(s.charAt(i));
            if(val==1)
               return s.charAt(i);
        }
        return ' ';
    }
}



//三:最优解法,用数组代替hashset,利用ascii值
class Solution {
    public char firstUniqChar(String s) {
        if(s==null)
         return ' ';
        //使用数组代替HashMap
        //注意,小写字母a对应的ascii值为97,z对应的是122.  
        //把每个字母对应的次数放进去
        int count[]=new int[26];
        for(int i=0;i<s.length();i++){
            int index=s.charAt(i)-'a';
            count[index]++;
        }
        //查找值为1的
        for(int i=0;i<s.length();i++){
            int index=s.charAt(i)-'a';
           if(count[index]==1)
             return s.charAt(i);
        }
        return ' ';
    }
}

 

 

29. 顺时针打印矩阵

//第一次没写出来看解答了,记得再回顾。
//思路:设定边界,然后收缩边界。

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        int row=matrix.length;
        if(row==0)
          return new int[0];
        int col=matrix[0].length;
        int res[]=new int[row*col];
         //设定初始边界
         int left=0,right=col-1,top=0,bottom=row-1;
         //设定打印遍历的数组索引
         int index=0;

         while(true){
            for(int i=left;i<=right;i++){
                res[index++]=matrix[top][i];
            }
            if(++top>bottom)
                break;
             
             for(int i=top;i<=bottom;i++){
                 res[index++]=matrix[i][right];
             }
             if(--right<left)
                 break;

             for(int i=right;i>=left;i--){
                 res[index++]=matrix[bottom][i];
             }
             if(--bottom<top) 
                 break;

             for(int i=bottom;i>=top;i--){
                 res[index++]=matrix[i][left];
             }
             if(++left>right)
                 break;
        }
        return res;

    }
}

 

 

 

二、双指针

  • 57.1 和为 S 的两个数字
  • 57.2 和为 S 的连续正数序列
  • 58.1 翻转单词顺序列
  • 58.2 左旋转字符串

 

  • 57.1 和为 S 的两个数字
//一:看到数组有序,想到双指针——左右双指针
class Solution {
    public int[] twoSum(int[] nums, int target) {
     int left=0,right=nums.length-1;
     int res[]=new int[2];
     while(left<right){
         int sum=nums[left]+nums[right];
         if(sum>target){
             right--;
         }
         else if(sum<target){
             left++;
         }
         else{
            res[0]=nums[left];
            res[1]=nums[right];
            break;
         }
     }
     return res;
    }
}

 

  • 57.2 和为 S 的连续正数序列
//一:想到了双指针:
//注意的问题:想怎么处理结果存储即二维数组长度花了好久,用list<int[]>即可
class Solution {
    public int[][] findContinuousSequence(int target) {        
        List<int[]> res=new ArrayList<int[]>();
        int left=1,end=target/2;
        while(left<=end){
             int j=left+1;
             int sum=left+j;
             while(sum<target){
                j++;
                sum+=j;
             }
             if(sum>target){
                 left++;
             }
             else if(sum==target){
                 int len=j-left+1;
                 int[] temp=new int[len];
                 for(int i=0,v=left;i<len && v<=j ;i++,v++){
                     temp[i]=v;
                 }
                 res.add(temp);
                 left++;
             }
        }
        //可优化:
        int sequence[][]=new int[res.size()][];
        for(int i=0;i<res.size();i++){
           sequence[i]=res.get(i);
        }
        return sequence;
    }
}


//对一的优化:主要在于如何把list转换为二维数组,前面写的时候没想到,就用的for
class Solution {
    public int[][] findContinuousSequence(int target) {        
        List<int[]> res=new ArrayList<int[]>();
        int left=1,end=target/2;
        while(left<=end){
             int j=left+1;
             int sum=left+j;
             while(sum<target){
                j++;
                sum+=j;
             }
             if(sum>target){
                 left++;
             }
             else if(sum==target){
                 int len=j-left+1;
                 int[] temp=new int[len];
                 for(int i=0,v=left;i<len && v<=j ;i++,v++){
                     temp[i]=v;
                 }
                 res.add(temp);
                 left++;
             }
        }
        //改进:
        //知识点:Arraylist转换为一维数组:res.toArray();  res.toArray(new int[]);
        //Arraylist转换为二维数组:res.toArray(new int[0][]);
        return res.toArray(new int[0][]);
    }
}

 

  • 58.1 翻转单词顺序列
//一:先想到的是用空格分隔字符串,然后倒序遍历
//注意知识点:如果用一个或多个空格分隔字符串:\\s+
class Solution {
    public String reverseWords(String s) {
      //去掉首尾空格
      s=s.trim();
      //把s转换为string类型的数组,然后从后遍历
      //分割一个空格:\\s,分隔一个或多个空格:\\s+      
      String[] res=s.split("\\s+");
      StringBuilder result=new StringBuilder();
      
      for(int i=res.length-1;i>=0;i--){
         result.append(res[i]).append(" ");
      }
      result.deleteCharAt(result.length()-1);
      return result.toString();
    }
  
}



//二:前后指针来交换位置
//双指针:一开始的思路是一个左一个右,再分别找到其单词交换位置,但是实现起来有点麻烦。
//看了看别人的解答,改了一下思路:从最右边开始,往前找到单词边界加入结果集。注意边界判断
class Solution {
    public String reverseWords(String s) {
      s=s.trim();
      StringBuilder sb=new StringBuilder();
      int i=s.length()-1;
     // int j=i-1;
      while(i>=0){
         int j=i-1;
    
         while(j>=0 && s.charAt(j)!=' ')  j--;
                
         String temp=s.substring(j+1,i+1);
         sb.append(temp).append(" ");
         
         i=j-1;
         while(i>=0 && s.charAt(i)==' ') i--;
                 
      }
      if(sb.length()!=0)
        sb=sb.deleteCharAt(sb.length()-1);
      return sb.toString();
      
    } 
}


//二的优化:
//看了一下别人的双指针TAT实现起来没有这么麻烦,从最右边开始,直接i寻找单词首个开头,让i=j,往前找到单词边界加入结果集。

class Solution {
    public String reverseWords(String s) {
      s=s.trim();
      StringBuilder sb=new StringBuilder();
      //改进1:
      int i=s.length()-1,j=i;
      
      while(i>=0){
         while(i>=0 && s.charAt(i)!=' ') i--;
         sb.append(s.substring(i+1,j+1)).append(" ");
         //找下个单词的尾巴
         while(i>=0 && s.charAt(i)==' ') i--;
         j=i;
                 
      }
      //改进2:
      return sb.toString().trim();
      
    } 
}

 

  • 58.2 左旋转字符串
//思路:直接用substring
class Solution {
    public String reverseLeftWords(String s, int n) {
      //思路:直接用substring
      String front=s.substring(0,n);
      String end=s.substring(n);
      return end+front;
    }
}




//看了一下别人的解答,还有遍历这个思路.但是效率和内存消耗比不了第一个
//注意,遍历最好用其他的代替字符串,因为JAVA中字符串是一个不可变对象,每次遍历添加一个字符时候都会新申请一次内存
class Solution {
    public String reverseLeftWords(String s, int n) {
      
      StringBuilder sb=new StringBuilder();
      for(int i=n;i<s.length();i++) sb.append(s.charAt(i));
      for(int i=0;i<n;i++) sb.append(s.charAt(i));
      return sb.toString();
    }
}

 

三、二分查找

  • 11. 旋转数组的最小数字
  • 53.1. 数字在排序数组中出现的次数
  • 53.2  0~n-1中缺失的数字

 

 

  • 11. 旋转数组的最小数字
//一:线性查找的思路,可以把这个数组分成两部分,左边和右边分别有序,所以要先找出mid 
class Solution {
    public int minArray(int[] numbers) {
    int left=0,right=numbers.length-1;
    int res=-1;
    for(int i=0;i<numbers.length;i++){
            while(i+1<numbers.length && numbers[i]<numbers[i+1]) i++;
            if(i+1<numbers.length && numbers[i]>numbers[i+1]){
                res=numbers[i+1];
                return res;
            }
            if(i+1==numbers.length){
                res=numbers[0];
            }
    }
    return res;
 }
}


//二:没想到这种怎么用二分查找,看了一下别人的思路。
//左右都闭寻找
class Solution {
    public int minArray(int[] numbers) {
    //二分查找: 
    int left=0,right=numbers.length-1;   
    while(left<=right){
        int mid=left+(right-left)/2;
        if(numbers[mid]>numbers[right]){
            left=mid+1;
        }
        else if(numbers[mid]<numbers[right]){
            //注意:这里是right=mid而不是mid-1,这是为了避免出现[3,1,3]把中间mid值丢失
            right=mid;
        }
        else if(numbers[mid]==numbers[right]){
            right--;
        }
    }
    return numbers[left];
 }
}

 

 

 

  • 53.1  数字在排序数组中出现的次数
//一:二分查找:找到左边界之后,从左边界开始计数来找个数
class Solution {
    public int search(int[] nums, int target) {
      int left=0,right=nums.length-1;
      int count=0;
      while(left<=right){
          int mid=left+(right-left)/2;
          if(nums[mid]<target){
              left=mid+1;
          }
          else if(nums[mid]>target){
              right=mid-1;
          }
          else if(nums[mid]==target){
              right=mid-1;
          }
      }
      if(left>=nums.length || nums[left]!=target )
        return 0;
      for(int i=left;i<nums.length;i++){
          if(nums[i]==target)
             count++;
      }
      return count;
    }
}



//思路二:两次二分,找一次左边界找一次右边界。就不写了。

 

 

  • 53.2   0~n-1中缺失的数字
 //二分查找,target其实是index,如果index!=i,那么就是缺失的
class Solution {
    public int missingNumber(int[] nums) {       
        int left=0,right=nums.length-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(nums[mid]>mid){
                right=mid-1;
            }
            else if(nums[mid]<mid){
                left=mid+1;
            }
            else if(nums[mid]==mid){
                left=mid+1;
            }
        }
        return left;
    }
}

 

四、链表

  • 6. 从尾到头打印链表
  • 18. 删除链表的节点(LEETCODE)
  • 18.1 在 O(1) 时间内删除链表节点
  • 18.2 删除链表中重复的结点
  • 22. 链表中倒数第 K 个结点
  • 23. 链表中环的入口结点(leetcode没有23,但是有类似的题:141. 判断是否是环形链表,142. 环形链表中环的起点)
  • 24. 反转链表(反转链表扩展题:92.反转从位置 m 到 n 的链表)(反转链表扩展题:25. K 个一组翻转链表 )
  • 25. 合并两个排序的链表
  • 35. 复杂链表的复制
  • 52. 两个链表的第一个公共结点
  • (扩展:判断是否是回文链表——面试题 02.06. 回文链表)

 

6. 从尾到头打印链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//一:就顺序访问链表,用一个List存储。 执行用时:1 ms, 在所有 Java 提交中击败了72.08%的用户.内存消耗:39.1 MB, 在所有 Java 提交中击败了46.67%的用户
class Solution {
    public int[] reversePrint(ListNode head) {
       if(head==null) return new int[0];
       List<Integer> list=new ArrayList<>();
       while(head.next!=null){
           list.add(head.val);
           head=head.next;           
       }
       list.add(head.val);
       int[] res=new int[list.size()];
       int index=0;
       for(int i=list.size()-1;i>=0;i--){
           res[index++]=list.get(i);
       }
       return res;

    }
}


//二:用栈
class Solution {
    public int[] reversePrint(ListNode head) {
       if(head==null) return new int[0];
       //从尾到头打印链表,这个性质满足栈的性质
       Stack<Integer> stack=new Stack<>();
       while(head.next!=null){
           stack.push(head.val);
           head=head.next;
       }
       stack.push(head.val);
       int[] res=new int[stack.size()];
       int index=0;
       while(!stack.isEmpty()){
          res[index++]=stack.pop();
       }
       return res;
    }
}



//三:用递归,时间和内存比前两个都差
class Solution {
    List<Integer> list=new ArrayList<>();
    public int[] reversePrint(ListNode head) {
       if(head==null) return new int[0];
       //从尾到头打印链表,用递归
       reverse(head);
       int[] res=new int[list.size()];
       for(int i=0;i<list.size();i++){
           res[i]=list.get(i);
       }
       return res;       
    }

    public void reverse(ListNode head){
        if(head==null) return;
        reverse(head.next);
        list.add(head.val);
        return;
    }
}

 

 

18. 删除链表节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
//一:因为头指针指向的就是第一个节点,所以考虑了普通情况,要删除的元素在链表最后一个和要删除的是链表第一个元素。
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
       if(head==null) return head;
       ListNode flag=head,front=head;
       //删除节点要找的是待删除节点的前一个节点
       //flag为待删除节点,front为前一个节点
       while(flag!=null && flag.val!=val){
            front=flag;
            flag=flag.next;           
       }
       if(flag.val==val && flag.next!=null && flag!=head){
           front.next=flag.next;
           flag.next=null;
       }
       else if(flag.next==null && flag.val==val && flag!=head){
           //要删除的元素在链表尾巴
           front.next=null;
       }
       else if(flag==head){
           //要删除的是第一个元素
           head=head.next;
           //flag.next=null;
       }
       return head;
    }
}


//二:因为考虑的太多了,可以增加一个虚节点不存储数字的,指向头结点head
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
       if(head==null) return head;
       //增加一个虚指针,指向头结点head
       ListNode top=new ListNode(0);
       top.next=head;
       //删除节点要找的是待删除节点的前一个节点
       //flag为待删除节点,front为前一个节点
       ListNode flag=head,front=top;
       while(flag!=null && flag.val!=val){
            front=flag;
            flag=flag.next;           
       }
       if(flag.val==val && flag.next!=null){
           front.next=flag.next;
           flag.next=null;
       }
       else if(flag.next==null && flag.val==val){
           //要删除的元素在链表尾巴
           front.next=null;
       }

       return top.next;
    }
}

//三:最方便的。一个虚指针,一个标记指针。
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
       if(head==null) return head;
       //只用一个指针,且增加一个虚指针
       ListNode top=new ListNode(0);
       top.next=head;
       ListNode front=top;
       while(front.next!=null && front.next.val!=val){
           front=front.next;
       }
       front.next=front.next.next;
       return top.next;
    }
}

 

 

22. 链表中倒数第 K 个结点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
//一:只想到一个笨方法,遍历两边,第一遍获取链表长度,第二遍遍历找到节点
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
       if(head==null)  return head;
       int len=1;
       ListNode flag=head;
       while(flag.next!=null){
           len++;
           flag=flag.next;           
       }
       flag=head;
       for(int i=1;i<len-k+1;i++){
           flag=flag.next;
       }
       return flag; 
    }
}




//二:快慢指针!!让快指针先走K步,然后再同步一起走
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
       //快慢指针!!让快指针先走K步,然后再同步一起走
       ListNode slow=head,fast=head;
       for(int i=0;i<k;i++){
           fast=fast.next;
       }
       while(fast!=null){
           fast=fast.next;
           slow=slow.next;
       }
       return slow;      
    }
}

 

 

24. 反转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

 //一:用递归
class Solution {
    public ListNode reverseList(ListNode head) {
        //用递归
        if(head==null || head.next==null) return head;
        ListNode last=reverseList(head.next);
        head.next.next=head;
        head.next=null;  
        return last;
    }

}




//迭代,用三个指针,一个记录当前的前一个,一个记录当前,一个记录后一个
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null || head.next==null) return head;
        //迭代,用三个指针,一个记录当前的前一个,一个记录当前,一个记录后一个
        ListNode pre=null;
        ListNode cur=head;
        ListNode tmp=head;
        while(cur.next!=null){
           tmp=cur.next;
           cur.next=pre;
           pre=cur;
           cur=tmp;           
        }
        cur.next=pre;
        return cur;
    }

}

 

(反转链表扩展题:92.反转从位置 m 到 n 的链表)

/**
 * 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 {
    //定义一个后继节点
    ListNode succsor;
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode res=head;
        if(m==1) return (reverseN(head,n));
        res.next=reverseBetween(head.next,m-1,n-1);
        return res;
    }
    
    //反转前n个节点
    public ListNode reverseN(ListNode head, int n){
        if(n==1){
            succsor=head.next;
            return head;
        }
        ListNode last=reverseN(head.next,n-1); 
        head.next.next=head;
        head.next=succsor;
        return last;
    }

}



//二:看了别人的一个解答,方法特别秒。双指针,头插法
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
         if(m==n || head.next==null) return head;
         if(head==null) return null;

         //定义一个虚指针,指向头结点
         ListNode top=new ListNode();
         top.next=head;
         //定义一个指向要开始反转的一个指针指向m的位置———>cur,在定义一个m前一个位置的指针pre
         ListNode pre=top;
         ListNode cur=head;
         for(int i=1;i<m;i++){
             pre=pre.next;
             cur=cur.next;
         }

         //开始用头插法开始进行链表的反转了
         for(int i=m;i<n;i++){
             ListNode tmp=cur.next;
             cur.next=cur.next.next;
             tmp.next=pre.next;
             pre.next=tmp;
         }
         return top.next;
    }
}

 

(反转链表扩展题:25. K 个一组翻转链表)

/**
 * 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 reverseKGroup(ListNode head, int k) {
       if(head==null) return null;
       ListNode a=head,b=head;
       for(int i=0;i<k;i++){
           //如果不足k个,直接返回当前的头结点
           if(b==null) return head;
           //如果足k个,b最终会指向下一组的第一个。也就是翻转的节点:[a,b) 不包含b
           b=b.next;
       }
       //进行翻转。翻转[a,b)也就是前k个元素
       ListNode newHead=reverse(a,b);   
       a.next=reverseKGroup(b,k);
       return newHead;
    }

    public ListNode reverse(ListNode a,ListNode b){
        ListNode cur=a,pre=null,nxt=cur;        
        while(cur!=b){
           nxt=cur.next;
           cur.next=pre;
           pre=cur;     
           cur=nxt;               
        }
        return pre;
    }
}

 

 

 

  • 25. 合并两个排序的链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//一: 把l2的每个节点记做要增加的新节点,且l2的第一个节点值一定大于等于l1的第一个节点值
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l2==null) return l1;
        else if(l1==null) return l2;
        
        //把l2的每个节点记做要增加的新节点,且l2的第一个节点值一定大于等于l1的第一个节点值
        if(l1.val>l2.val){
           ListNode temp=l1;
           l1=l2;
           l2=temp;
        }

        ListNode front=l1;
        while(l2!=null){
            while(front.next!=null && l2.val>front.next.val){
               // System.out.println(front.val);
                front=front.next;         
            }
            ListNode add=new ListNode(l2.val);
            add.next=front.next;
            front.next=add;
            l2=l2.next;         
           // System.out.println(l2.val); 
        }
        return l1;
    }
}




//二: 用两个指针分别指向l1,l2,比较其值的大小,新建一个新的头节点,记录最终结果,相当于最后有一个新的链表
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l2==null) return l1;
        else if(l1==null) return l2;
        
        //用两个指针分别指向l1,l2,比较其值的大小
        //新建一个新的头节点,记录最终结果,相当于最后有一个新的链表
        ListNode top=new ListNode(0);
        ListNode cur=top;

        while(l1!=null && l2!=null){
            if(l1.val>=l2.val){
                cur.next=l2;
                l2=l2.next;
                cur=cur.next;
            }
            else{
                cur.next=l1;
                l1=l1.next;
                cur=cur.next;
            }
        }
        cur.next= l1==null?l2:l1;
        return top.next;
    }
}

 

 

  • 35. 复杂链表的复制
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

//一开始的思路是:两遍遍历,第一遍复制Next指针,第二遍复制random指针。但是无法实现,具体在复制random指针的时候,newnode.random=oldnode.random; 不能这样处理,因为新链表的random指的是原来旧链表的节点。所以这个方法不行。

//看了别人的解答。
//一:用哈希表

class Solution {
    public Node copyRandomList(Node head) {
        if(head==null) return head;
        //用哈希表,键是旧节点,值是新节点(且只存数值)
        HashMap<Node,Node> hashmap=new HashMap<>();
        Node cur=head;
        while(cur!=null){
            hashmap.put(cur,new Node(cur.val));
            cur=cur.next;
        }

        cur=head;
        while(cur!=null){
            hashmap.get(cur).next=hashmap.get(cur.next);
            hashmap.get(cur).random=hashmap.get(cur.random);
            cur=cur.next;
        }

        return hashmap.get(head);

    }
}



二:

 

  • 52. 两个链表的第一个公共结点(这个有点像找带有环的链表的环的起点)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

//一:双指针!这个思路和找环的起点很像!做的时候啥思路都没有,要记住!
//假设第一个链表长度是L1+C,第二个链表的长度是L2+C,C是公共的部分。如果第一个链表最终走了L1+L2+C,第二个链表也走了L1+L2+C它们就能相遇,即交点。所以遍历的时候,哪个链表走到了结尾,那么它就重新指向另一个链表的起始。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //一:双指针!这个思路和找环的起点很像!做的时候啥思路都没有,要记住!
        //假设第一个链表长度是L1+C,第二个链表的长度是L2+C,C是公共的部分。如果第一个链表最终走了L1+L2+C,第二个链表也走了L1+L2+C它们就能相遇,即交点。所以遍历的时候,哪个链表走到了结尾,那么它就重新指向另一个链表的起始。
        ListNode a=headA;
        ListNode b=headB;
        while(a!=b){
            a= a==null?headB:a.next;
            b= b==null?headA:b.next;
        } 
        return a;
    }
}




//二:哈希集合,存的是ListNode!
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
       if(headA==null || headB==null) return null;
       //用哈希集合,存的是节点!
       Set<ListNode> set=new HashSet<>();
       ListNode cur=headA;
       while(cur.next!=null){
           set.add(cur);
           cur=cur.next;
       }
       set.add(cur);

       //遍历headB,找是不是在集合中
       cur=headB;
       while(cur!=null){
           if(set.contains(cur))
             return cur;
           cur=cur.next;
       }
       return null;
    }
}

 

 

141. 判断是否是环形链表

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

//显然用快慢指针,让慢的每次走一步,快的每次走两步,如果最后二者能相遇就说明有环,快的比慢的多走的就是环的长度。假设慢指针走了k步,那么快指针走了2k步,也就是说比慢指针多走了k步(环的长度)。

public class Solution {
    public boolean hasCycle(ListNode head) {
        //快慢指针,让慢的每次走一步,快的每次走两步,如果最后二者能相遇就说明有环。
        //慢的走了n步,快的走了2n步,则快的比慢的多走的就是环的长度。
        if(head==null || head.next==null) return false;
        ListNode slow=head;
        ListNode fast=head.next.next;
        while(slow!=fast){
            if(slow==null || fast==null || fast.next==null) return false;
            fast=fast.next.next;
            slow=slow.next;
        }
        return true;
    }
}



//二:看了看别人的解答,还有哈希集合!存节点!
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null || head.next==null) return false;
        //哈希集合存节点!
        Set<ListNode> set=new HashSet<>();
        ListNode cur=head;
        while(cur!=null){
            if(set.contains(cur))  return true;
            set.add(cur);
            cur=cur.next;
        }
        return false;
    }
}

 

 

142. 环形链表中环的起点(和剑指offer52类似,也没想起来!)

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

//注意这个实现,双指针问题。相遇后让慢指针回到起点
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null || head.next==null) return null;
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) break; 
        }
        if(fast==null || fast.next==null) return null;
        //一定有环,让slow指向起点,然后同样的速度前进
        slow=head;
        while(slow!=fast){
            slow=slow.next;
            fast=fast.next;
        }
        return slow;
    }
}




//二:哈希集合
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null || head.next==null) return null;
        //哈希集合
        Set<ListNode> set=new HashSet<>();
        ListNode cur=head;
        while(cur!=null){
            if(set.contains(cur)){
                return cur;
            }
            set.add(cur);
            cur=cur.next;
        }
        return null;
    }
}

 

 

(扩展:判断是否是回文链表——面试题 02.06. 回文链表)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//第一个思路就是:遍历链表,用栈存。和链表比较
class Solution {
    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> stack=new Stack<>();
        ListNode cur=head;
        while(cur!=null){
            stack.push(cur);
            cur=cur.next;
        }
 
        cur=head;
        while(!stack.isEmpty()){
            if(stack.pop().val!=cur.val)
               return false;
            cur=cur.next;
        }
        return true;
    }
}



//二:用递归,本质和栈一样——————先实现一个逆序打印链表的值,然后比较
//相当于用了递归的栈。因此它时间和空间复杂度还是都是O(N)
class Solution {
    ListNode left;
    public boolean isPalindrome(ListNode head) {
        //用递归,本质和栈一样——————先实现一个逆序打印链表的值,然后比较
        left=head;
        boolean res=reverse(left);
        return res;
    }

    public boolean reverse(ListNode head){
        ListNode right=head;
        if(head==null) return true;
        boolean res=reverse(right.next);
        res= res && (right.val==left.val);
        left=left.next;
        return res;
    }
}





//三:最佳:
//空间复杂度O(1):先用快慢指针找到链表的中点,然后翻转后半部分链表,和前半部分进行比较。
//注意:如果链表的个数是偶数个,慢指针最后会指到需要开始翻转的那个节点,如果链表的个数是奇数个,则指向的是正中点,此时需要让慢指针再前进一位,使其刚好指到需要开始进行翻转的节点。
//注意:进行翻转会破坏原有的链表结构,如果想要恢复链表结构,最后再对后半部分翻转一次即可。
class Solution {
    public boolean isPalindrome(ListNode head) {
       if(head==null || head.next==null) return true; 
       ListNode slow=head;
       ListNode fast=head;
       while( fast!=null && fast.next!=null){
           fast=fast.next.next;
           slow=slow.next;
       }
       //链表的个数是奇数个的话,fast指向最后一个指针,slow需要前进一步
       if(fast!=null) slow=slow.next;
       //对链表后半部分翻转
       slow=reverse(slow); 
       //进行比较
       while(slow!=null){
           if(head.val!=slow.val) return false;
           slow=slow.next;
           head=head.next;
       }
       return true;
    }


    public ListNode reverse(ListNode head){
        ListNode cur=head,nxt=head,pre=null;
        while(cur!=null){
            nxt=cur.next;
            cur.next=pre;
            pre=cur;
            cur=nxt;
        }
        return pre;
    }
}

 

 

 

 

 

 

 

五、滑动窗口:

  • 59 - I. 滑动窗口的最大值

 

59 - I. 滑动窗口的最大值

//一:滑动窗口
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
         ArrayList<Integer> list=new ArrayList<>();
         int left=0,right=0;
         while(right<nums.length){                     
             int temp=nums[right];
             right++;
             //if(temp>max) max=temp; 
             if(right-left==k){
                 int max=Integer.MIN_VALUE;;
                 for(int j=left;j<right;j++){
                    if(nums[j]>max)  max=nums[j];
                 }
                 list.add(max);
                 left++;
             }
         }
        int[] res=new int[list.size()];
        for(int i=0;i<list.size();i++){
             res[i]=list.get(i);
        }
        return res;
    }
}


//二:看题解答案还有队列。待看。

 

 

 

六、树

  • 7. 重建二叉树
  • 8. 二叉树的下一个结点
  • 26. 树的子结构                    拓展(待写):「101. 对称二叉树」 「1367. 二叉树中的列表」「572. 另一个树的子树」 「面试题 04.10. 检查子树」
  • 27. 二叉树的镜像
  • 28. 对称的二叉树
  • 32.1 从上往下打印二叉树
  • 32.2 把二叉树打印成多行
  • 32.3 按之字形顺序打印二叉树
  • 33. 二叉搜索树的后序遍历序列
  • 34. 二叉树中和为某一值的路径
  • 36. 二叉搜索树与双向链表
  • 37. 序列化二叉树
  • 54. 二叉查找树的第 K 个结点
  • 55.1 二叉树的深度
  • 55.2 平衡二叉树
  • 68. 二叉搜索树中两个节点的最低公共祖先
  • 68 - II. 二叉树的最近公共祖先

 

 

 

7. 重建二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
//首先想到:递归
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder==null || inorder==null)
           return null;
        //左右都闭
        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;
       
          //找到preorder第一个在inorder的索引
          int start=preorder[preStart];
          int index=-1;
          for(int i=inStart;i<=inEnd;i++){
              if(inorder[i]==start){
                  index=i;
                  break;
              }
          }

          TreeNode root=new TreeNode(start);
          root.left=build(preorder,preStart+1,index-inStart+preStart,inorder,inStart,index-1);
          root.right=build(preorder,index-inStart+preStart+1,preEnd,inorder,index+1,inEnd);
          return root;
    }
}


 

 

26. 树的子结构

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 //普通二叉树,想到的方法:前序遍历两棵树,遍历结果看B是否包含在A内。
 //注意:对于A[10,12,6,8,3,11]  B[10,12,6,8]这两个结果。预期结果也是正确。所以不应该看B的前序遍历字符串是否完全是A的子串,但是看相对顺序也不对。所以这个方法行不通。

 //思路:还是用前序框架,但是不看遍历结果的字符串,而是根据前序比较树。
class Solution {

    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A==null || B==null) return false;
        return contains(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
       
        
    }

    //以A为根的树是否包含B
    //如果B为空的话,表示是子树,可以返回。如果B还没完但是A完了A为空,那么则错。
    public boolean contains(TreeNode A,TreeNode B){
        if(B==null) return true;
        if(A==null || A.val!=B.val) return false;
        return contains(A.left,B.left) && contains(A.right,B.right);
    }
}

 

 

27. 二叉树的镜像

 //左右子树交换位置
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
       if(root==null) return null;
       TreeNode temp=mirrorTree(root.left);
       root.left=mirrorTree(root.right);
       root.right=temp;
       return root;
    }
}

 

28. 对称的二叉树

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

//一:先想到的是构建一个新的镜像树,然后再拿新的树和旧的树比较。
class Solution {
    public boolean isSymmetric(TreeNode root) {
       if(root==null) return true;
       TreeNode nw=build(root);
       return isSame(root,nw);
       
    }

    public TreeNode build(TreeNode root){
        if(root==null) return null;
        TreeNode newroot=new TreeNode(root.val);
        TreeNode tmp=build(root.left);
        newroot.left=build(root.right);
        newroot.right=tmp;
        return newroot;
    }

    public boolean isSame(TreeNode oldtree,TreeNode newtree){
        if(oldtree==null && newtree==null)
          return true;
        if(oldtree==null || newtree==null || oldtree.val!=newtree.val)
          return false;
        return isSame(oldtree.left,newtree.left)&&isSame(oldtree.right,newtree.right);
    }
}


 //二:利用对称二叉树的性质!
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null) return true;
        return compare(root.left,root.right);        
    }

    public boolean compare(TreeNode left,TreeNode right){
        if(left==null && right==null)
          return true;
        if(left==null || right==null || left.val!=right.val)
          return false;
        return compare(left.left,right.right)&&compare(left.right,right.left);
    }
}

 

32.1 从上往下打印二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
//这个从上到下打印二叉树,想到的是队列。然后用ArrayList存结果。
class Solution {
    public int[] levelOrder(TreeNode root) {
        if(root==null) return new int[]{};
        Queue<TreeNode> queue=new LinkedList<>();
        List<Integer> ls=new ArrayList<>();
        queue.offer(root);
        ls.add(root.val);
        //TreeNode tmp=root;
        while(!queue.isEmpty()){
            TreeNode tmp=queue.remove();
            if(tmp.left!=null){
                queue.offer(tmp.left);
                ls.add(tmp.left.val);
            }
            if(tmp.right!=null){
               queue.offer(tmp.right);
               ls.add(tmp.right.val);
            }
        }
        //return new int[]{0,1};
        int[] res=new int[ls.size()];
        for(int i=0;i<ls.size();i++){
            res[i]=ls.get(i);
        }
        return res;
    }
}

 

 

32.2 把二叉树打印成多行(层次遍历,每层作为一行输出)

//层次遍历
 //显然用队列,但是有一个实现的问题没想出来:就是怎么保证每次结果能输出一层的数据。
 //看了别人的解答,可以用一个len来记住每一层的节点个数。然后for即可。
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
       List<List<Integer>> res=new ArrayList<>();
       if(root==null) return res;

       Queue<TreeNode> queue=new LinkedList<>();
       queue.offer(root);
    
       while(!queue.isEmpty()){
           List<Integer> cur=new ArrayList<>();
           //关键:
           int len=queue.size();
           for(int i=0;i<len;i++){
                TreeNode tmp=queue.remove();
                cur.add(tmp.val);
                if(tmp.left!=null){
                   queue.offer(tmp.left);
                }
                if(tmp.right!=null){
                   queue.offer(tmp.right);
                }
           }
           res.add(cur);
       }
       return res;
    }
}

 

 

32.3 按之字形顺序打印二叉树

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

 //还是层次遍历,在定义一个boolean类型的做标记,奇数行从左到右,偶数行从右到左
//注意逆序的实现:用Collections.reverse(arraylist);
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null)  return res;
        //flag=false 表示奇数行
        boolean flag=false;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int len=queue.size();
            List<Integer> cur=new ArrayList<>();
            for(int i=0;i<len;i++){
                TreeNode tmp=queue.remove();
                cur.add(tmp.val);
                if(tmp.left!=null){
                    queue.offer(tmp.left);
                }
                if(tmp.right!=null){
                    queue.offer(tmp.right);
                }
            }
            if(flag){
                //把cur元素逆序存
                Collections.reverse(cur);       
            }
            res.add(cur);
            flag=!flag;
        }
        return res;
    }
}

 

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


//一:用递归,postorder最后一个就是根,先找到第一个比它大的,后面的应该都比根大,如果不是就错。
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder==null) return true;
        return verify(postorder,0,postorder.length-1);
    }
    public boolean verify(int[] postorder,int start,int end){
         //递归终止条件:
         if(start>=end) return true;
         int root=postorder[end];
         int i=start;
         //从前遍历,找到第一个大于根节点的值,则[i,end-1]应该都比root大
         while(postorder[i]<root) i++;
         int j=i;
         //验证是否[i,end-1]都比root大,如果是,最后j应该等于root=>验证本棵树
         while(postorder[j]>root) j++;

         return j==end && verify(postorder,start,i-1) && verify(postorder,i,end-1);

    }
}


//二:有一个单调栈的解法,不是特别理解。

 

 

34. 二叉树中和为某一值的路径

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

 //回溯,没写出来,看了别人的答案。
class Solution {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> cur=new ArrayList<>(); 
    int tmp=0;  
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root==null) return res;
        recure(root,sum);
        return res;

    }
    public void recure(TreeNode root,int sum){
        if(root==null) return;
        cur.add(root.val);
        tmp+=root.val;
        //System.out.println(tmp);
        if(root.left==null && root.right==null){
            //到达叶子节点且路径和等于目标值
            if(tmp==sum){
                //这里重点!不能直接res.add(cur),因为cur是个引用传递,实际存的是一个对象。如果后期cur变化,则res中的cur也在变。所以这里要复制一份传递到结果。
                res.add(new ArrayList<>(cur));
            }
        }
        recure(root.left,sum);
        recure(root.right,sum);
        cur.remove(cur.size()-1);
        tmp-=root.val;
        return;
    }
}

 

36. 二叉搜索树与双向链表

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

    public Node() {}

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

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

//中序框架
class Solution {
    Node pre=null;
    Node head=new Node(-1);
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;
        traverse(root);

        //最后pre会指向最后一个节点
        pre.right=head.right;
        head.right.left=pre;
        return head.right;

       
    }
    public Node traverse(Node root){
        if(root==null) return null;
        Node cur=root;
        traverse(root.left);
        if(pre!=null){
            //有前驱节点
            cur.left=pre;
            pre.right=cur;
        }
        else{
            head.right=cur;
        }
        pre=cur;
        traverse(root.right);
        return root;                                                                                                                                                   
    }
}

 

37. 序列化二叉树

 

55.1 二叉树的深度

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

 //一:首先想到的是用回溯算法,设两个全局变量
class Solution {
    int max=0;
    int count=0;
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        recure(root);
        return max;

    }
    public void recure(TreeNode root){
        if(root==null) return;
        count++;
        if(max<count)
           max=count;
        recure(root.left);
        recure(root.right);
        count--;
        return;
    }
}



 //二:深度优先!居然没想到一时间
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        int left=maxDepth(root.left);
        int right=maxDepth(root.right);
        return Math.max(left,right)+1;

    }
}


 //三:BFS,广度优先。树的层序遍历/广度优先搜索往往用队列。
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        int level=0;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int len=queue.size();
            for(int i=0;i<len;i++){
                TreeNode tmp=queue.remove();
                if(tmp.left!=null) queue.offer(tmp.left);
                if(tmp.right!=null) queue.offer(tmp.right);
            }
            level++;
        }
        return level;
    }
}

 

55.2 平衡二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        int left=count(root.left);
        int right=count(root.right);
        if(Math.abs(left-right)>1) return false;
        return isBalanced(root.left) && isBalanced(root.right);        
    }
    public int count(TreeNode root){
        if(root==null) return 0;
        int left=count(root.left);
        int right=count(root.right);
        return Math.max(left,right)+1;
    }
}

 

68.二叉搜索 树中两个节点的最低公共祖先

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

 //没思路,看了解答的思路。要根据二叉搜索树的性质。本质递归。
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        if(root.val>p.val && root.val<q.val)
           return root;
        if(root.val<p.val && root.val>q.val)
           return root;
        if(root.val==p.val) return p;
        if(root.val==q.val) return q;
        if(root.val>p.val && root.val>q.val){
            return lowestCommonAncestor(root.left,p,q);
        }
        else{
            return lowestCommonAncestor(root.right,p,q);
        }
    }
}


//由于前面写了大量的If语句,可进行优化:
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        if(root.val>p.val && root.val>q.val){
            return lowestCommonAncestor(root.left,p,q);
        }
        else if(root.val<p.val && root.val<q.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        
        //都不满足的话,则要么p,q在root两侧,要么p,q有一个值和root相等,返回root即可。
        return root;
    }
}



 //二:看了其他解析,还可以用迭代:
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        while(root!=null){
            if(root.val>p.val && root.val>q.val)
               root=root.left;
            else if(root.val<p.val && root.val<q.val)
               root=root.right;
            else
               break;
        }
        return root;
    }
}

 

68 - II. 二叉树的最近公共祖先

 //68-1是二叉搜索树,68-2是二叉树。 
//看了解析
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
         if(root==null) return null;
         if(root==p || root ==q) return root;
         TreeNode left=lowestCommonAncestor(root.left,p,q);
         TreeNode right=lowestCommonAncestor(root.right,p,q);
         if(left==null && right==null) return null;
         if(left==null)  return right;
         if(right==null) return left;
         //right和left都不为空
         return root;
    }
}

 

 

54. 二叉查找树的第 K 个结点

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
//一:直接用list存中序打印的结果。但是不好,时间复杂度是O(n)
class Solution {
    List<Integer> ls=new ArrayList<>();
    public int kthLargest(TreeNode root, int k) {
         traverse(root);
         return ls.get(ls.size()-k);
         
    }

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


//对于一的改进:因为要打印大的,所以把中序的左中右,这里实现为右中左
class Solution {
    int count=0;
    int res=0;
    public int kthLargest(TreeNode root, int k) {
         traverse(root,k);
         return res;
         
    }

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



//二:分治法
class Solution {
    public int kthLargest(TreeNode root, int k) {
        int rightNode=count(root.right);
        if(k==rightNode+1) return root.val;
        else if(k>rightNode+1)
            return kthLargest(root.left,k-rightNode-1);
        else
           return kthLargest(root.right,k);     
    }

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

 

 

 

 

七、栈队列堆

  • 9. 用两个栈实现队列
  • 30. 包含 min 函数的栈
  • 31. 栈的压入、弹出序列
  • 40. 最小的 K 个数
  • 41.1 数据流中的中位数
  • 41.2 字符流中第一个不重复的字符
  • 59. 滑动窗口的最大值(滑动窗口处已解)

 

9. 用两个栈实现队列

30. 包含 min 函数的栈

31. 栈的压入、弹出序列

40. 最小的 K 个数

41.1 数据流中的中位数

41.2 字符流中第一个不重复的字符

59. 滑动窗口的最大值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值