zoukankan      html  css  js  c++  java
  • 面试常见高频算法题总结

    一、链表相关

    1.链表反转

    class Solution {
        public ListNode reverseList(ListNode head) {
            // base case
            if(head == null || head.next == null) return head;  
            
            ListNode first = head;
            ListNode result = null;//建立一个新的节点用来存放结果
            ListNode second = null;
            while(first != null){ //遍历输入链表,开始处理每一个节点
                second = first.next;  //先处理第一个节点first,所以需要一个指针来存储first的后继
                first.next = result; //将first放到新链表头节点的头部
                result = first; //移动新链表的头指针,让它始终指向新链表头部
                first = second; //继续处理原链表的节点,即之前指针存放的后继,循环往复
            }
            return result;
        }
    }

     2.k个一组翻转链表(力扣 25) --------比较难

    3.判断链表是否有环

    /**
     * Definition for singly-linked list.
     * class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
       public boolean hasCycle(ListNode head) {//设置快慢指针,若有环一定会在环里面相遇。否则会有null值
        if(head==null) return false;
        ListNode walker = head;
        ListNode runner = head;
        while(runner.next!=null && runner.next.next!=null) {
            walker = walker.next;
            runner = runner.next.next;
            if(walker==runner) return true;
        }
        return false;
      }
    }

    //也可以采用hashmap把每一个节点存储起来,如果地址相同则存在节点

    二、数组、字符串巧妙解法相关

    1.不使用除法实现除自身外数组元素的乘积(力扣 238)

    public class Solution {
        
       public int[] productExceptSelf(int[] nums) {
           int n = nums.length;
           int[] res = new int[n];
           res[0] = 1;
           for (int i = 1; i < n; i++) {
              res[i] = res[i - 1] * nums[i - 1];
           }
           int right = 1;
           for (int i = n - 1; i >= 0; i--) {
              res[i] *= right;
              right *= nums[i];
          }
           return res;
        }
    }

    二、树相关

    1.给定一颗二叉搜索树,返回该二叉搜索树第K大的节点

    //思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。
    //     所以,按照中序遍历顺序找到第k个结点就是结果。
    public class Solution {
       int index = 0; //计数器
        TreeNode KthNode(TreeNode root, int k)
        {
            if(root != null){ //中序遍历寻找第k个
                TreeNode node = KthNode(root.left,k);
                if(node != null)
                    return node;
                index ++;
                if(index == k)
                    return root;
                node = KthNode(root.right,k);
                if(node != null)
                    return node;
            }
            return null;
        }
    }

     2.二叉树最小深度

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    //为什么采用分治的思想而不是求数最大深度思想,因为可能为单链表(链表是特殊树)没法处理
    class Solution {
         public int minDepth(TreeNode root) {
            if(root == null) return 0;
            int left = minDepth(root.left);
            int right = minDepth(root.right);
            return (left == 0 || right == 0) ? left + right + 1//为什么可以写成left + right + 1
                : Math.min(left,right) + 1;         //因为left 跟right 必有一个为0,所以..
           
        }
    }

    3.二叉树最大深度

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
       public int maxDepth(TreeNode root) {
            if(root == null) {
                return 0;
            } else{
                return 1+ Math.max(maxDepth(root.left), maxDepth(root.right));
            }
        }
    }

    三、动态规划相关

    1.两个字符串的最长公共子序列长度

    class Solution {
    //dp[i][j]表示 0-(i-1) 0-(j-1)之间最长子序列
    public int longestCommonSubsequence(String s1, String s2) { int[][] dp = new int[s1.length() + 1][s2.length() + 1]; for (int i = 0; i < s1.length(); ++i) for (int j = 0; j < s2.length(); ++j) if (s1.charAt(i) == s2.charAt(j)) dp[i + 1][j + 1] = 1 + dp[i][j]; else dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); return dp[s1.length()][s2.length()]; } }

    2.求连续子数组的最大和

    class Solution {
       public static int maxSubArray(int[] A) {
        int maxSoFar=A[0], maxEndingHere=A[0];
        for(int i=1;i<A.length;++i){
            maxEndingHere = Math.max(A[i], maxEndingHere+A[i]);
            maxSoFar = Math.max(maxEndingHere, maxSoFar);
        }
        return maxSoFar;
      }
    }

    四、排序

    1.快排

    public static void quickSort(int[] list, int left, int right) {
            if (left < right) {
                // 分割数组,找到分割点
                int point = partition(list, left, right);
    
                // 递归调用,对左子数组进行快速排序
                quickSort(list, left, point - 1);
                // 递归调用,对右子数组进行快速排序
                quickSort(list, point + 1, right);
            }
        }
    
        /**
         * 分割数组,找到分割点
         */
        public static int partition(int[] list, int left, int right) {
            // 用数组的第一个元素作为基准数
            int first = list[left];
            while (left < right) {
                while (left < right && list[right] >= first) {
                    right--;
                }
                // 交换
                swap(list, left, right);
    
                while (left < right && list[left] <= first) {
                    left++;
                }
                // 交换
                swap(list, left, right);
            }
            // 返回分割点所在的位置
            return left;
        }
    
        /**
         * 交换数组中两个位置的元素
         */
        public static void swap(int[] list, int left, int right) {
            int temp;
            if (list != null && list.length > 0) {
                temp = list[left];
                list[left] = list[right];
                list[right] = temp;
            }
        }

     2.二路归并排序

    public class MergeSort {
        /**
         * 归并排序(Merge Sort)与快速排序思想类似:将待排序数据分成两部分,继续将两个子部分进行递归的归并排序;然后将已经有序的两个子部分进行合并,最终完成排序。
         * 其时间复杂度与快速排序均为O(nlogn),但是归并排序除了递归调用间接使用了辅助空间栈,还需要额外的O(n)空间进行临时存储。从此角度归并排序略逊于快速排序,但是归并排序是一种稳定的排序算法,快速排序则不然。
         * 所谓稳定排序,表示对于具有相同值的多个元素,其间的先后顺序保持不变。对于基本数据类型而言,一个排序算法是否稳定,影响很小,但是对于结构体数组,稳定排序就十分重要。例如对于student结构体按照关键字score进行非降序排序:
         */
        public static void main(String[] args) {
            int[] list = {50, 10, 90, 30, 70};
            System.out.println("************归并排序************");
            System.out.println("排序前:");
            display(list);
            System.out.println("排序后:");
            mergeSort(list, new int[list.length], 0, list.length - 1);
            display(list);
        }
    
        /**
         * 归并排序算法
         * @param list     待排序的列表
         * @param tempList 临时列表
         * @param head     列表开始位置
         * @param rear     列表结束位置
         */
        public static void mergeSort(int[] list, int[] tempList, int head, int rear) {
            if (head < rear) {
                // 取分割位置
                int middle = (head + rear) / 2;
                // 递归划分列表的左序列
                mergeSort(list, tempList, head, middle);
                // 递归划分列表的右序列
                mergeSort(list, tempList, middle + 1, rear);
                // 列表的合并操作
                merge(list, tempList, head, middle + 1, rear);
            }
        }
    
        /**
         * 合并操作(列表的两两合并)
         * @param list
         * @param tempList
         * @param head
         * @param middle
         * @param rear
         */
        public static void merge(int[] list, int[] tempList, int head, int middle, int rear) {
            // 左指针尾
            int headEnd = middle - 1;
            // 右指针头
            int rearStart = middle;
            // 临时列表的下标
            int tempIndex = head;
            // 列表合并后的长度
            int tempLength = rear - head + 1;
    
            // 先循环两个区间段都没有结束的情况
            while ((headEnd >= head) && (rearStart <= rear)) {
                // 如果发现右序列大,则将此数放入临时列表
                if (list[head] < list[rearStart]) {
                    tempList[tempIndex++] = list[head++];
                } else {
                    tempList[tempIndex++] = list[rearStart++];
                }
            }
    
            // 判断左序列是否结束
            while (head <= headEnd) {
                tempList[tempIndex++] = list[head++];
            }
    
            // 判断右序列是否结束
            while (rearStart <= rear) {
                tempList[tempIndex++] = list[rearStart++];
            }
    
            // 交换数据
            for (int i = 0; i < tempLength; i++) {
                list[rear] = tempList[rear];
                rear--;
            }
        }
    
        /**
         * 遍历打印
         */
        public static void display(int[] list) {
            if (list != null && list.length > 0) {
                for (int num :list) {
                    System.out.print(num + " ");
                }
                System.out.println("");
            }
        }
    }

    五、LRU LFU

    1.lru采用双链表+hashmap实现

    class LRUCache {
        int size;
        Node head,tail;
        HashMap<Integer,Node> hm;
        public LRUCache(int capacity) { 
             size=capacity;
             head=tail=null;
             hm=new HashMap<>();
        }
        
        public int get(int key) {
            
             if(!hm.containsKey(key))
             {
                 return -1;
             }
             else
             {
                 Node ref=hm.get(key);
                 if(ref==head) return ref.val;
                 if(ref!=head && ref!=tail)
                 {
                     ref.Llink.Rlink=ref.Rlink;
                     ref.Rlink.Llink=ref.Llink;
                     
                 }
                 else if(ref!=head && ref==tail)
                 {
                     tail=tail.Llink;
                     tail.Rlink=null;
                     
                 }
                 ref.Llink=null;
                 ref.Rlink=head;
                 head.Llink=ref;
                 head=ref;
                 return ref.val;
             }
            
            
          
        }
        
        public void put(int key, int value) {
          
             if(hm.containsKey(key))
             {
                 hm.get(key).val=value;
                 this.get(key);
             }
             else
             {
                 Node temp=new Node(value,key);
                
                 if(hm.size()<size)
                 {   hm.put(key,temp);
                     if(head==null)
                     {
                         head=temp;
                         tail=temp;
                     }
                     else
                     {
                         temp.Rlink=head;
                         head.Llink=temp;
                         head=temp;
                     }
                 }
                 else
                 {
                     if(tail==head)
                     {
                         hm.remove(tail.key);
                         hm.put(key,temp);
                         head=tail=temp;
                     }
                     else
                     {
                         hm.remove(tail.key);
                         hm.put(key,temp);
                         Node help=tail;
                         tail=tail.Llink;
                         tail.Rlink=null;
                         help.Llink=null;
                         temp.Rlink=head;
                         head.Llink=temp;
                         head=temp;
                     }
                      
                        
                 }
                 
             }
            
            
        }    
    }
    
    class Node{
        int val;
        int key;
        Node Rlink;
        Node Llink;
        Node(int val,int key)
        {   this.key=key;
            this.val=val;
            Rlink=null;
            Llink=null;
        }
       
    }

    2.lfu

  • 相关阅读:
    程序运行时被用户删除了工作目录后崩溃
    const引用与非const引用
    NDKr10的各种BUG
    《区块链100问》第64集:区块链分叉是什么?
    《区块链100问》第65集:比特币生孩子了
    《区块链100问》第66集:软分叉和硬分叉是什么?
    《区块链100问》第67集:重放攻击是什么?
    《区块链100问》第68集:硬分叉之以太经典
    《区块链100问》第69集:区块链项目的分类和应用
    《区块链100问》第70集:区块链项目之币类
  • 原文地址:https://www.cnblogs.com/yangcao/p/13650248.html
Copyright © 2011-2022 走看看