【算法】算法基础

基础

评估算法重要指标

  • 时间复杂度
  • 额外空间复杂度

算法技巧

  • 经验1: 把运算的指令控制在10^8 到10^9 之间。比如一个数组长度为10^2 ,找一个O(N ^2 )的算法即可通过。
  • 在这里插入图片描述

课程

class 1 简单排序

  • 选择排序:每一次拿当前数和所有数比较,最小的放在当前位置
  • 冒泡排序:拿当前数与下一个数比较,若当前数大则交换位置
  • 插入排序:拿当前数与前一个数比较,若小则交换位置,并进行下一个数判断
  • 二分查找:每次找到中间的那个数,进行比较

class 2 异或运算

  • 异或:无进位相加
  1. 如果不用额外变量,给两个变量交换位置
int a = x,  b = y;
a = a ^ b;
b = a ^ b;
a = a ^ b;
// 解释
第一步时:a = a ^b , b = b
第二步时:a = a^b, b = a^b^b = a
第三步时:a = a^b^a=b, b= a
  1. 有一组数,只有一个数出现奇数次,其他都是偶数次,如何找到?
int arr[] = ***;
int eor = 0;
for(int a : arr[]){
 eor = eor ^ a;
}
system.out.println(eor);
  1. 把一个整型的数,提取最右边的1
    int a = a & (-a)
    解释:
    如果a = 7. 二进制表示为0111;
    -a = ~a + 1.
    所以~a = 1000,
    ~a+1 = 1001,
    a& (-a) = 0001

  2. 有一组数,有2个数出现奇数次,其他都是偶数次,如何找到?
    先全部异或,找到eor = a^b;
    再找到最右边的1的数 rightOne;
    在根据 arr去与rightOne,根据是否等于0,可以把这组数分为rightone的那个1上的数,是否为1和不为1的两部分。
    再用eor与为1的去异或,得到其中一个数,再eor与这个数异或,得到另外一个数

  3. 有一组数,有1个数出现k次,其他都是m次,m> 1, k< m,如何找到出现k次的数,额外空间复杂度为O(1)?
    准备一个32个长度的数组。然后把数组的值按位,与到准备的数组里面去。
    再遍历这个数组,拿每一位的值

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import utils.MyArrayUtils;

import java.util.Map;
import java.util.Set;

/**
 * @program SpringCloud_HelloWorld
 * @description: 有一组数,有1个数出现k次,其他都是m次,m> 1, k< m,如何找到出现k次的数,额外空间复杂度为O(1)
 * @author: zhasiwei
 * @create: 2021/07/11 13:21
 */
public class findKTimes {

    private static int find(int[] arr, int k, int m){

        if(arr == null || k > m || m <2){
            return -1;
        }

        int[] tmp = new int[32];
        for (int a : arr) {
            for (int i = 0; i < 32; i++) {
                if(((a >> i) & 1) == 1 ){
                    tmp[i] ++;
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < tmp.length; i++) {
            if(tmp[i] % m != 0){
               ans |= 1 << i;
            }
        }

        return ans;
    }

    private static int test(int[] arr, int k, int m){
        Map<Integer, Integer> map = Maps.newHashMap();
        for (int a : arr) {
            if(map.containsKey(a)){
                map.put(a, map.get(a) + 1);
            }else{
                map.put(a,1);
            }
        }

        for (Integer key : map.keySet()) {
            if(map.get(key) == k){
                return key;
            }
        }

        return -1;
    }


    private static int[] genRandomArr(int kinds, int maxValue, int k, int m){
        int[] arr = new int[k + (kinds - 1) * m];
        int index = 0;

        int key = genRandomNum(maxValue);
        Set<Integer> exists = Sets.newHashSet();
        exists.add(key);
        for (; index < k; index++) {
            arr[index] = key;
        }


        for (int i = 0; i < kinds - 1; i++) {
            int other = genRandomNum(maxValue);
            while (exists.contains(other)){
                other = genRandomNum(maxValue);
            }
            for (int j = 0; j < m; j++) {
                arr[index++] = other;
            }
        }

        for (int i = 0; i < arr.length; i++) {
            int random = (int)(Math.random() * arr.length);
            MyArrayUtils.swapArr(arr, i, random);
        }

        return arr;
    }

    private static int genRandomNum(int maxValue){
        return (int)(Math.random() * maxValue + 1) - (int)(Math.random() * maxValue);
    }

    public static void main(String[] args) {
        int testSize = 500000;
        int maxValue = 100;
        int kinds = 10;
        int k = 5;
        int m = 20;

        for (int i = 0; i < testSize; i++) {
            int[] arr = genRandomArr(kinds, maxValue, k, m);
            if( find(arr, k, m) != test(arr,k,m)){
                System.out.println("failed");
                break;
            }
        }


        System.out.println("end....");
    }


}

class 3 基础数据结构

  • 链表
  1. 单链表 反转
  2. 双向链表 反转
  3. 把指定的值删除
  • 栈、队列
  1. 双向链表实现栈、队列
  2. 数组表实现栈、队列
  3. 实现一个数组,并且增加一个获取最小值的方法,要求时间复杂度为O(1)
  4. 用栈实现队列
  5. 用队列实现栈
  • 递归
  1. master函数:分析递归的时间复杂度。
    T(N) = a* T(N/b) + O(N^d) 。
    子函数平均分为b份,其他的时间复杂度为O(N^d),可以用该公式
    例如: T(n) = 2* T(n/2) + O(1) 。
    T(n) 为总的时间复杂度;T(n/2)为子递归函数的时间复杂度,T(n/2)具体看子递归的复杂度,n/2是平均分为两份
    此时可以确定复杂度为:
    a. log_b^a >d , O(N^d)
    b. log_b^a <d , O(Nlog_ba)
    c. log_b^a = d, O(N^d *logN)
  • hash表
  • 有序表,treeMap,底层用红黑树,avl,sb数等实现,key要有比较器,可以获得Fist和last

class4 归并排序

思路:把一个数组分成两份,采用递归,让左右两边都有序。
此时,借助辅助数组,依次将有序的两个数组,往辅助数组内放数据。
复杂度为:
T(N) = 2* T(N/2) + O(N),根据master公式,O(N*logN)

面试题:

  1. 小和问题。把一个数组的每个位置的左边的所有小于该值的数,加起来的和
  2. 求逆序对数量。把一个数组的每个位置,和右边的所有数据对比,如果当前数大于右边的数,则逆序对+1
  3. 左组比右边的数的2倍还大的次数。
  4. 给定一个数组,给两个数lower和upper,返回有多少子数组的累加和在[lower, upper]之间

class 5 快速排序

  1. 荷兰国旗问题:给定一组数组,指定一个数。将大于这个数的放右边,小于这个数的放左边,等于这个数的放中间。不能使用其他数组,要求时间复杂度为O(1)
  2. 同上,但是指定的数是最后一个数。
  3. 快排(递归调用2的方式就是快排)leeCode 327题

class 6 堆和堆排序

  • 大根堆:完全二叉树,且结点必定比两个叶结点大或相等
  • 小根堆:与大根堆相反

堆排序:

  • heapInset():用当前位置与结点位置比较,若大,则交换。循环下去
  • heapify():用较大的叶结点与当前比较,若当前小,则交换。并循环下去
  • 把数组的数按堆的顺序排序好,再依次poll出根节点,剩下的继续按照堆的规则排序。

class 7 加强堆

手写加强堆

class 8 前缀树、计数排序、基数排序

class 9 链表

  • 练习:单链表,快慢索引。然后返回
    在这里插入图片描述
  • 链表 判断是否时回文。不用额外空间
  • 链表 回文:给一个数组l1 l2 l3 l4 r1 r2 r3 r4。要求返回 l1 r4 l2 r3 l3 r2 l4 r1.。不用额外空间
  • 链表 给一个乱序的列表,然后给一个数。要求小于的放左边,等于的放中间,大于的放右边
  • 链表 给一个链表,每个节点有一个random指针。要求:完全复制这个链表。不用额外空间。

一定要写的面试题:
在这里插入图片描述

class 10 二叉树

  • 递归实现前序、中序、后续
  • 不用递归实现前序、中序、后续

class 11 二叉树

  • 按层打印
  • 找 二叉树最宽层的节点数有多少
  • 一张纸,对折,一根凹痕,再对折,多一根凹痕一根凸痕迹。N次折后依次打印的凹凸顺序。

class 12 二叉树的递归套路

  • 判断是否是完全二叉树(所有节点要么满,要么都是在满的路上)
  • 给定一颗二叉树的头,判断这颗二叉树是不是平衡二叉树
  • 判断是不是搜索二叉树(每一颗树,左侧都比头小,右侧都比头大)
  • 求一颗二叉树中,两个节点之间的,最大距离
  • 判断一颗树是否是满二叉树
  • 判断所有满足搜索树的子树的最大节点
  • 一颗树,给两个节点,找到最低的公共结点
  • 有一颗多叉树,有n个请帖。但是当前节点和直接节点不能同时邀请。求邀请的最大值
    在这里插入图片描述

class 14,5 贪心算法 ,并查集

  • 给一组字符串,随机排序组成字符串,取出字典序最小的组合

class 16 并查集练习

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 16 图

在这里插入图片描述

  • 最小生成树K算法:先建立并查集,再把所有的边放到边中。每两个节点就判断是否在同一个set中,如果不在就union。(最小生成树,用最小的边,完成连通性)
  • 最小生成树Prim算法

class 17 暴力递归

  • 汉诺塔:写6个方法:从左->中、左->右、中->左、中->右、右->中、右->左。递归嵌套即可!!注意这里要从结果往开始分割思考。即第一步就是把n-1个圆盘看作一个整体。以此类推
  • 汉诺塔优化:把左、中、右抽象为from、to、other
  • 打印一个字符串的全部子序列,即 abc 要 a,ab,ac,abc,bc,
  • 打印一个字符串的全部子序列,不能有重复子序列,即 abc 要 a,ab,ac,abc,bc,
  • 打印一个字符串的全排列
  • 打印一个字符串的全排列,不能有重复子序列
  • 在这里插入图片描述

class 18 动态规划1

个人经验:
  1. 先按递归解出题目
  2. 增加缓存,避免重复计算
  3. 画一个二维矩阵,根据base case条件,去找规律。(直接根据第一种的,去写即可)

在这里插入图片描述
在这里插入图片描述

class 19 动态规划2

1.背包问题。int[] w是货物重量,int[] v是货物价值,背包最大重量为bag,求,最多能装的货物价值是多少
2.
在这里插入图片描述
在这里插入图片描述
最长公共子序列

class 20 动态规划3

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 21 动态规划4

采用压缩数组方式来做,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 22 动态规划5

在这里插入图片描述
在这里插入图片描述

class 23 动态规划6

在这里插入图片描述
在这里插入图片描述
总结
在这里插入图片描述
在这里插入图片描述

class 24 窗口内最大值与最小值的更新结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 25 单调栈

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 26 由斐波那契数列讲述矩阵快速幂技巧

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class 27 KMP算法

  1. 给2个字符串,找最长公共子串
  2. (next数组记住第二个字符串的最长前缀、后缀和。当匹配不上时,回到next数组记录的下标继续和第一个字符串进行匹配)

class 28 Manacher算法

  1. 给个字符串,求最长回文字符串
  2. (先给字符串加工,abc->#a#b#c#,然后par数组记录每个位置的最长回文字符串的长度,R代表以当前字符为中心的最右下标,C为当前下标。)

class 29 bfprt算法、蓄水池算法

pfrpt算法:一个讲究选比较数P的快排

  • 先5个数分组(为啥5个?因为作者规定,无理由)
  • 在每个组进行排序,此时O(N)
  • 取每个组的中位数,得到数组m
  • 取m的中位数,就是比较数P了
  • 后续取比较数P都这样来
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

class 30 Morris 遍历

优化二叉树遍历的空间复杂度,从O(树高度)->O(1)
前序:第一次来到重复节点打印。
中序:第一次来到重复节点不打印,第二次打印
后续:第二次来到重复节点,逆序打印左树的右边界。最后打印整棵树的右边界
在这里插入图片描述
在这里插入图片描述

class 31 线段树

区间更新和区间查询用该数据结构。问题在于怎么实现!很难写。支持的是一维的
数组0位置不用,从1开始

class 32 indexTree、AC自动机

indexTree:线段树的弟弟版本,只能解决单点的更新。但是可以支持二维的、三维的
数组0位置不用,从1开始
建立一个heplp数组。每个位置,管理当前位置,并且与前一个管理的进行合并,如果长度相等,就管理2个位置,再向前判断,若也等于2,则管理4.
给定一个index,转换为2进制,抛弃最右的1,并且+1,此时这个index管理的就是从该值到自身的值
求前缀和就是,把index的二进制,每次拿掉1的index相加。例如 index=1110.就等于arr[111]+arr[0110] + arr[0100]
当index的数改变了,那么arr数组中受牵连的位置有是所有index的二进制的最右边的1加本身。例如 0110 ,1000,10000,100000……(index & -index 可以得到最右边的1)

AC自动机:
一个前缀树,但是每个节点都有一个fail指针,若父节点的fail有同样的路径,则当前fail指向那个路径。否则指向root节点

class 33 与哈希函数有关的结构

在这里插入图片描述

class 34 资源限制类问题

在这里插入图片描述

class 35 有序表 上

class 36 有序表 中

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值