算法面试题

  • 算法面试题
  • 链表
  • 66. 加一
  • 2. 两数相加
  • 160. 相交链表
  • 141. 环形链表
  • 142. 环形链表 II
  • 剑指 Offer 22. 链表中倒数第k个节点
  • 147. 对链表进行插入排序
  • 148. 排序链表
  • 二叉树
  • 144. 二叉树的前序遍历
  • 94. 二叉树的中序遍历
  • 145. 二叉树的后序遍历
  • 102. 二叉树的层序遍历
  • 101. 对称二叉树
  • 146. LRU 缓存
  • 双指针
  • LCP 18. 早餐组合
  • 26. 删除有序数组中的重复项
  • 27. 移除元素
  • 143. 重排链表
  • 88. 合并两个有序数组
  • 283. 移动零
  • 滑动窗口
  • 3. 无重复字符的最长子串
  • 76. 最小覆盖子串
  • 239. 滑动窗口最大值
  • 位运算
  • 231. 2 的幂


算法面试题

链表

66. 加一


class Solution {
    public int[] plusOne(int[] digits) {
        int len=digits.length;
        //进位1
        int carry=1;

        for(int i=len-1;i>=0;i--){
            //主要是进位的问题   计算出数
            int digit=digits[i]+carry;
            digits[i]=digit%10;
            //看看还需不需要进位
            carry=digit/10;
        }

        if(carry==1){
        //这种是[9,9,9]的情况
            digits=new int[len+1];
            digits[0]=1;
        }
        return digits;
    }
}

2. 两数相加

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return add(l1,l2,0);
    }

    public ListNode add(ListNode l1,ListNode l2 ,int carry){
    //两个链表同时往下走,如果两个都为空,并且没有进位的话,终止
         if(l1==null&&l2==null&&carry==0)return null;
        //可能会有一个链表走完,一个没走完的情况
         int a=l1==null?0:l1.val;
         int b=l2==null?0:l2.val;
         //计算出值
         int temp=a+b+carry;
         //总的值
         int c=temp%10;
         //进位值
         carry=temp/10;
         ListNode node=new ListNode(c);
         //下一层
         node.next=add(l1==null?null:l1.next,l2==null?null:l2.next,carry);
         return node;
    }
}

160. 相交链表

public class Solution {
    //相交的话 a1+a2+c1+c2+c3+b1+b2+b3=b1+b2+b3+c1+c2+c3+a1+a2
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
          ListNode node1=headA;
          ListNode node2=headB;

          while(node1!=node2){
             node1= node1==null?headB:node1.next;
             node2= node2==null?headA:node2.next;
          }
          return node1;

    }
}

141. 环形链表

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;

        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow)return true;
        }
        return false;
    }
}

142. 环形链表 II

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=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;
        //把快指针放头位置,同时移动即可
        fast=head;
        while(fast!=slow){
            slow=slow.next;
            fast=fast.next;
           
        }
        return slow;
        
    }
}

剑指 Offer 22. 链表中倒数第k个节点

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast=head;
        ListNode slow=head;
        //先走k步
        for(int i=0;i<k;i++)fast=fast.next; 

        while(fast!=null){
             fast=fast.next;
             slow=slow.next;
        }
        return slow;
    }
}

147. 对链表进行插入排序

class Solution {
    public ListNode insertionSortList(ListNode head) {
        if(head==null)return null;

        ListNode dummyHead=new ListNode(-1);
        dummyHead.next=head;

        ListNode lastSorted=head;
        ListNode cur=head.next;

      while(cur!=null){
          //如果比较大,顺序的不用调整往后移动
           if(lastSorted.val<=cur.val){
               lastSorted=lastSorted.next;
           }else{
               //说明需要调整顺序了
               ListNode pre=dummyHead;
               //插入的话,比较后面一个元素的大小,如果小了,就需要插这个元素前面
               //dummy  ->   1 ->  5   ->  3  -> 7     要插入的是3
               //            pre    lastSort  cur
               while(pre.next.val<=cur.val)pre=pre.next;
               // 1的位置停下来  把5指向7,3指向5,1指向3
               lastSorted.next=cur.next;
               cur.next=pre.next;
               pre.next=cur;
           }
           //调整位置
           cur=lastSorted.next;
      }
      return dummyHead.next;

    }
}

148. 排序链表

通过快慢指针确定中间值,进行拆分,再用归并

/**
 * 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 sortList(ListNode head) {
        //如果链表为空,或者只有一个节点,直接返回即可,不用排序
        if (head == null || head.next == null)
            return head;

        //快慢指针移动,以寻找到中间节点
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next!=null && fast.next.next !=null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //找到中间节点,slow节点的next指针,指向mid
        ListNode mid = slow.next;
        //切断链表
        slow.next = null;

        //排序左子链表
        ListNode left = sortList(head);
        //排序左子链表
        ListNode right = sortList(mid);

        //合并链表
        return merge(left,right);
    }

    public ListNode merge(ListNode left, ListNode right) {
        ListNode head = new ListNode(0);
        ListNode temp = head;
        while (left != null && right != null) {
            if (left.val <= right.val) {
                temp.next = left;
                left = left.next;
            } else {
                temp.next = right;
                right = right.next;
            }
            temp = temp.next;
        }
        if (left != null) {
            temp.next = left;
        } else if (right != null) {
            temp.next = right;
        }
        return head.next;
    }
}

二叉树

144. 二叉树的前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer>list=new ArrayList<>();
        dfs(root,list);
        return list;
    }

    public void dfs(TreeNode root,List<Integer>list){
        if(root==null)return;
        list.add(root.val);
        dfs(root.left,list);
        dfs(root.right,list);
         
    }
}
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
       List<Integer>list=new ArrayList<>();
        if(root==null)return list;
       
        LinkedList<TreeNode>stack=new LinkedList<>();
        TreeNode cur=root;
         //栈空,或者cur遍历完了,说明结束了
         while(!stack.isEmpty()||cur!=null){
             //先向左下遍历,前序是中左右的顺序。
            while(cur!=null){
                list.add(cur.val);
                stack.push(cur);
                cur=cur.left;
            }
            //如果左边遍历完后,最下面一个树弹栈,看右边有没有
          TreeNode temp = stack.pop();
          if(temp.right!=null) {
             cur=temp.right;
           }
         }
        return list;
    }
}

94. 二叉树的中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer>res=new ArrayList<>();
        if(root==null)return res;

        LinkedList<TreeNode>stack=new LinkedList();
        TreeNode cur=root;
     
        while(!stack.isEmpty()||cur!=null){
            while(cur!=null){
                stack.push(cur);
                cur=cur.left;
            }
           
            TreeNode temp=stack.pop();
             res.add(temp.val);
            if(temp.right!=null)cur=temp.right;
        }
        return res;

    }
}

145. 二叉树的后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
    //后序遍历是左右中,而这种写法是中右左,反转就好
       List<Integer>list=new ArrayList<>();
        if(root==null)return list;
       
        LinkedList<TreeNode>stack=new LinkedList<>();
        TreeNode cur=root;
         //栈空,或者cur遍历完了,说明结束了
         while(!stack.isEmpty()||cur!=null){
             //先向左下遍历,前序是中左右的顺序。
            while(cur!=null){
                list.add(cur.val);
                stack.push(cur);
                cur=cur.right;
            }
            //如果左边遍历完后,最下面一个树弹栈,看右边有没有
          TreeNode temp = stack.pop();
          if(temp.left!=null) {
             cur=temp.left;
           }
         }
         Collections.reverse(list);
        return list;
    }
}

102. 二叉树的层序遍历

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.add(root);

        while(!queue.isEmpty()){
            int size=queue.size();
            List<Integer>list=new ArrayList();
            for(int i=0;i<size;i++){
                TreeNode node=queue.poll();
                list.add(node.val);
                if(node.left!=null)queue.add(node.left);
                if(node.right!=null)queue.add(node.right);
            }
            res.add(list);
        }
        return res;

    }
}
class Solution {
    List<List<Integer>>res=new ArrayList();
    public List<List<Integer>> levelOrder(TreeNode root) {
           BFS(root,0);
           return res;
    }

    public void BFS(TreeNode root,int level){
          if(root==null)return;
          if(level>=res.size()){
              res.add(new ArrayList<Integer>());
          }
          List<Integer>list=res.get(level);
          list.add(root.val);
          BFS(root.left,level+1);
          BFS(root.right,level+1);
    }
}

101. 对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null||(root.left==null&&root.right==null))return true;
        LinkedList<TreeNode>stack=new LinkedList();
        stack.add(root.left);
        stack.add(root.right);

        while(stack.size()>0){
            TreeNode left= stack.poll();
            TreeNode right= stack.poll();
            if(left==null&&right==null)continue;
            if(left==null||right==null)return false;
            if(left.val!=right.val)return false;

            stack.offer(left.left);
            stack.offer(right.right);

            stack.offer(left.right);
            stack.offer(right.left);
        }
        return true;

    }
}
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null||(root.left==null&&root.right==null))return true;
         return cur(root.left,root.right);
    }

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

146. LRU 缓存

class LRUCache {
//双向链表+hashmap
    int capacity;
    int size;
    Map<Integer,Node>hashTable;

    Node head;
    Node tail;
    
    public LRUCache(int capacity) {
        this.capacity=capacity;
        this.size=0;
        hashTable=new HashMap();
 
        head=new Node();
        tail=new Node();
        head.next=tail;
        tail.pre=tail;
    }
    
    public int get(int key) {
      Node node = hashTable.get(key);
      if(node==null)return -1;
      //将node移动到头部
      moveToHead(node);
      return node.value;
    }

    public void moveToHead(Node node){
        //将node从双向链表删除
        removeNode(node);
        //将node添加到头节点后面
        addToHead(node);
    }

    public Node removeNode(Node node){
        Node preNode=node.pre;
        Node nextNode=node.next;

        preNode.next=nextNode;
        nextNode.pre=preNode;
        node.next=null;
        node.pre=null;
        return node;
    }

    public void addToHead(Node node){
        Node nextNode=head.next;
        head.next=node;
        node.pre=head;

        node.next=nextNode;
        nextNode.pre=node;
    }

    
    public void put(int key, int value) {

         Node node= hashTable.get(key);
         if(node!=null){
            node.value=value;
            moveToHead(node);
            return;
         }
         //添加到缓存
         node=new Node(key,value);
         hashTable.put(key,node);
         //新节点添加到头节点
         addToHead(node);
         //
         this.size++;
         if(this.size>this.capacity){
             Node rnode=removeTail();
             hashTable.remove(rnode.key);
             this.size--;
         }
    }

    public Node removeTail(){
        Node rnode=tail.pre;
       return removeNode(rnode);
    }
    

    class Node{
        int key;
        int value;
        Node next;
        Node pre;

        Node(){};
        Node(int key,int value){
            this.key=key;
            this.value=value;
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

双指针

LCP 18. 早餐组合

class Solution {
    public int breakfastNumber(int[] staple, int[] drinks, int x) {
        //原来  [10,20,5]  [5,5,2]  
        Arrays.sort(staple);
        Arrays.sort(drinks);
        //[5,10,20]  [2,5,5]
        int count=0;
        int left=0;
        int right=drinks.length-1;
        while(left<staple.length&&right>=0){
            int number=staple[left]+drinks[right];
            if(number<=x){
                //如果满足的话,说明right之前的都满足
                count=(count+right+1)%1000000007;
                left++;
            }else {
                right--;
            }
        }
       return count;
    }
}

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

class Solution {
    public int removeDuplicates(int[] nums) {
        int len=nums.length;
        if(len<2)return len;
        //用快慢指针,快指针往前一点,如果重复覆盖给慢指针
        //慢指针用来记录不重复元素位置
        int slow=1;
        int fast=1;

        while(fast<len){
            if(nums[fast]!=nums[fast-1]){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;

    }
}

27. 移除元素

class Solution {
    public int removeElement(int[] nums, int val) {
        int len=nums.length;

        int fast=0;
        int slow=0;

        while(fast<len){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;

    }
}

143. 重排链表

/**
 * 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 void reorderList(ListNode head) {
        if(head==null)return ;
       //用空间换时间 数组好操作
        ArrayList<ListNode>list=new ArrayList();
        ListNode cur=head;
        while(cur!=null){
            list.add(cur);
            cur=cur.next;
        }

        int size=list.size();
        int i=0;
        int j=size-1;
         
        while(i<j){
              list.get(i).next=list.get(j);
              i++;
              list.get(j).next=list.get(i);
              j--;
        }
        //防止成环,如果是单数的,因为本身3->4的 而操作后 4->3 导致成环
        list.get(i).next=null;
    }
}
/**
 * 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 void reorderList(ListNode head) {
        //取中间,断开,后面半段逆序后合并
        if(head==null)return ;
        
        ListNode mid=midNode(head);
        ListNode l1=head;
        ListNode l2=mid.next;
        mid.next=null;
         l2= reverse(l2);
           merge(l1,l2);

    }

    public ListNode midNode(ListNode head){
       ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public ListNode reverse(ListNode node){
        ListNode pre=null;
        ListNode cur=node;
        while(cur!=null){
            ListNode nextNode=cur.next;
            cur.next=pre;
            pre=cur;
            cur=nextNode;
        }
        return pre;
    }

    public void merge(ListNode l1,ListNode l2){
  
         while(l1!=null&&l2!=null){
         //记录后一个节点,防止指针断开后,丢失找不到后一个节点
               ListNode next1=l1.next;
               ListNode next2=l2.next;
              //指向后面另外一边的节点,然后往后移动
               l1.next=l2;
               l1=next1;

               l2.next=l1;
               l2=next2;
         }
    }
}

88. 合并两个有序数组

添加备注


class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
          int i=m-1;
          int j=n-1;
          int p=m+n-1;

          while(i>=0&&j>=0){
      nums1[p--]=nums1[i]>nums2[j]?nums1[i--]:nums2[j--];
         }
         // nums2没有遍历完。
         if(j>=0){
         System.arraycopy(nums2, 0, nums1, 0, j+1);
         }
    }
}

283. 移动零

class Solution {
    public void moveZeroes(int[] nums) {
        if(nums.length==1)return;
        int left=0;
        int index=0;
        //如果出现非0的,交换下
        while(left<nums.length){
           if(nums[left]!=0){
               swap(nums,left,index);
               index++;
           }
           left++;
        }

    }

    public void swap(int[]nums,int x,int y){
        int temp=nums[x];
        nums[x]=nums[y];
        nums[y]=temp;
    }
}

滑动窗口

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

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len=s==null?0:s.length();
        if(len<2)return len;

        Set<Character>set=new HashSet();
        int left=0;
        int right=0;
        int count=0;
       
        while(right<len){
            char c=s.charAt(right);
            //窗口中存在左侧元素,就不断缩小
            while(set.contains(c)){
                set.remove(s.charAt(left));
                left++;
            }
            //加进去
            set.add(c);
            right++;
            count=Math.max(count,right-left);           
     }
     return count;

    }
}

76. 最小覆盖子串

用两个数组来记录目前存储的字符状态,用count来记录目前有效添加的有几个。

class Solution {
    public String minWindow(String s, String t) {
          if(s==null||t==null||s.length()<t.length()){
              return "";
          }
          //构造两个哈希表
          int[]dict=new int[128]; //字典:记录t中所有字符及出现的次数
          int[]window=new int[128];//记录滑动窗口中每个字符出现的次数
          //记录s
          for(int i=0;i<t.length();i++){
              dict[t.charAt(i)]++;
          }
         //定义滑动窗口左右边界指针left,right
         int left,right;
         left=right=0;
         //定义窗口内已有字典中字符的个数
         int count=0;
         int min=s.length()+1;//+1是为了后续判断比这个小,因为有种极端情况是最小窗口就是s的长度
         //定义返回的字符串
         String res="";

         while(right<s.length()){
             char cr=s.charAt(right);
             window[cr]++;
            //判断进入窗口的这个字符是否是t中的
            if(dict[cr]>0&&dict[cr]>=window[cr]){
                //dict[cr]>=window[cr]避免了窗口中进入了过多的重复字符导致误判,比如t="ABC"窗口中进入了"AAA"
                count++;
            }
            right++;
            //当窗口内已完全包含t中所有字符时窗口开始收缩
            while(count==t.length()){
                 //缩小窗口求最小窗口
                 char cl=s.charAt(left);
                 //如果要移除窗口的这个字符时字典中,则窗口不能再收缩了,这是底线
                 if(dict[cl]>0&&dict[cl]>=window[cl]){
                     //dict[cl]>=window[cl]这个地方避免了移出过去的重复字符导致误判,比如t=ABC,窗口中有AABC
                     count--;
                     if(right-left<min){
                         min=right-left;
                         res=s.substring(left,right);
                     }
                 }
                 window[cl]--;
                 left++;
            }
         }
         return res;
    }
}

239. 滑动窗口最大值

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length < 2) return nums;
        // 双向队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList();
        // 结果数组
        int[] result = new int[nums.length-k+1];
        // 遍历nums数组
        for(int i = 0;i < nums.length;i++){
            // 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            // 添加当前值对应的数组下标
            queue.addLast(i);
            // 判断当前队列中队首的值是否有效
            if(queue.peek() <= i-k){
                queue.poll();   
            } 
            // 当窗口长度为k时 保存当前窗口中最大值
            if(i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }
}

位运算

231. 2 的幂

class Solution {
    public boolean isPowerOfTwo(int n) {
          if(n==0)return false;
          while((n&1)==0){
              n=n>>1;
          }
          return n==1;
    }
}
class Solution {
    public boolean isPowerOfTwo(int n) {
    
          return n > 0 && (n & (n - 1)) == 0;
    }
}