算法40讲

选择最列表中最大的值

一、

新入元素比原来组中最小的小,那只需要1次操作,就可以找到第k大数字

如果进来的元组比最小的大,要进行台哦正,调整logk

于是时间复杂度是N*o(logk),

二、

单次擦欧总,每次进行排序 N*klogk

滑动窗口最大值

一、优先队列

大顶堆MaxHeap 最上面是最大的元素(删除离开的元素,加入新的元素logk,结果是堆顶元素o(1))

最终时间复杂度是NlogK

二、Queue双关队列o(N*1)

1、前k个元素一次加入

2、新的数字加入维护

时间复杂度 hashmap hashset

有序 treemap treeset

哈希略快二叉搜索树,顺序没有要求就用哈希,有要求二叉搜索树

两数之和

返回两数之和等于target的数的下标

一、x+y = 9 两层循环嵌套o(n^2)

二、y = 9-x

枚举x写一个for循环(o(n)),查询一下9-x是否在set里面存在(o(1)) 因此时间复杂度为o(1)

有效的字母异位词

一、sort 任何一个字符串进来,字符串里的所有字母按照字典进行排序

​ rat - art tar-art 快排的时间复杂度是nlogn

二、用map来进行计数 {letter:count} 任何里面的字符串将字母放在map里面进行计数

循环o(n) map插入删除o(1) 总共时间复杂度o(n)

def isanagram3(self,s,t):
    return sorted(s) == sorted(t) 


def isanagram1(self,s,t):
    dic1,dic2 = {
    
    },{
    
    }
    for item in s:
        dic1[item] = dic1.get(item,0) + 1
    for item in t:
        dic2[item] = dic2.get(item, 0) + 1
    return dic1 == dic2

def isanagram2(self,s,t):
    dic1,dic2 = [0]*26,[0]*26
    for item in s:
        dic1[ord(item)-ord('a')] += 1
    for item in t:
        dic2[ord(item)-ord('a')] += 1
        # ord() 函数是chr() 函数(对于8 位的ASCII 字符串)的配对函数
    return dic1 == dic2

三数之和

[-1,0,1,2,-1,-4] 0

一、暴力解法 a+b+c = 0 o(n^3)

二、c = -(a+b)将整个数组放在一个set里面,只需要o(1)的操作 o(n^2)

三、sort find

整个数组进行排序[-4,-1,-1,0,1,2]

1、loop 枚举a(o(n)),剩下的数组里面找b和c a+b+c>0 c左移 如果小于0,b右移 --o(n^2)

def threesum(self,nums):
    if len(nums) < 3:
        return []
    nums.sort()
    res = set()
    for i,v in enumerate(nums[:-2]):
        if i >= 1 and v == nums[i-1]:
            continue
        d = {
    
    }
        for x in nums[i+1:]:
            if x not in d:
                d[-v-x] = 1
            else:
                res.add((v,-v-x,x))
    return map(list,res)

def threesum1(self,nums):
    res = []
    nums.sort()
    for i in xrange(len(nums)-2):
        if i > 0 and nums[i] == nums[i-1]:
            continue
        l,r = i+1,len(nums)-1
        while l < r:
            s = nums[i] + nums[r] + nums[l]
            if s < 0 :l += 1
            elif s > 0: r -= 1
            else:
                res.append((nums[i],nums[l],nums[r]))
                while l <r and nums[l] == nums[l+1]:
                    l += 1
                while l < r and nums[r] == nums[r-1]:
                    r -= 1
                l += 1 ;r -= 1

树&二叉树&二叉搜索树

链表是特殊化的树,树是特殊化的图

class TreeNode:
    def __init__(self,val):
        self.val = val
        self.left,self,right = None ,None

二叉搜索树,也成为有序二叉树,排序二叉树,它可以是一颗空树,或者左子树的所有结点的值小于根节点,右子树的所有结点的值大于根结点

存在本质:让搜索更快,让排列更加有序

验证二叉搜索树

空的树就是一个二叉排序树

一、进行中序遍历

遍历出来的结点组成的数组,升序,结果就是二叉排序树

二、用递归来进行,判断

递归函数,要传min,max函数,再去递归左孩子,得到左孩子的最大值,然后右孩子,找到最小值,判断左边的最大值小于根节点的值,右子树的最小值大于根节点,然后继续递归

def isvalidBST(self,root):
    inorder = self.inorder(root)
    return inorder == list(sorted(set(inorder)))

def inorder(self,root):
    if root is None:
        return []
    return self.inorder(root.left) + [root.val] + self.inorder(root.right)
def isValidBST(self,root):
    self.prev = None
    return self.helper(root)

def helper(self,root):
    if root is None:
        return True
    if not self.helper(root.left):
        return False
    if self.prev and self.prev.val >= root.val
        return False
    self.prev = root
    return self.helper(root.right)
public boolean isValid(TreeNode root,Integer min,Integer max){
    
    
    if (root=null) return True;
    if (min != null && root.val <= min) return false;
    if (max != null && root.val >= max) return false;
    
    return isValid(root.left,min,root.val)&&
    isvalid(root.right,root.val,max);
}

二叉树/二叉搜索树的最近公共祖先

一、寻找路径path

需要要求有一个父亲指针的问题,但是可以这么想,做不能

从祖先找路径1,路径2,看从根开始,最后一个结点是什么,则变成公共祖先

二、

recursion

find PorQ(root,p,q)以root为根的子树里面,找p和q,找到谁都可以

if root == p or root == q:

​ return root

findPorQ(root,p,q){
    
      
    if root == null ||root == p || root == q:
        return root
    TreeNode left = findPorQ(root.left,p,q)
    TreeNode right = findPorQ(root.right,p,q)
    return left == null ? right : right == null ? left : root;
}
def lowestcommonancestor(self,root,p,q):
    if p.val < root.val > q.val:
        return self.lowestcommonancestor(root.left,p,q)
    if p.val > root.val < q.val:
        return self.lowestcommonancestor(root.right, p, q)
    return root

二叉树的遍历(知识)

preorder

inorder

postorder

def preorder(self,root):
    if root:
        self.traverse_path.append(root.val):
        self.preorder(root.left)
        self.preorder(root.right)
        
def inorder(self,root):
    if root:
        self.preorder(root.left)
        self.traverse_path.append(root.val):
        self.preorder(root.right)
        
def postorder(self,root):
    if root:
        self.preorder(root.left)
        self.preorder(root.right)
        self.traverse_path.append(root.val):

递归&分治

递归(通过函数体类似进行循环)层层递进,层层回来

def recursion(level,param1,param2,...):
    # recursion terminator终止条件
    if level > MAX_LEVEL:
        print_result
        return
    
    # process logic in current level业务逻辑
    process_data(level,data...)
    
    # drill down调自己函数本身,解决下一层任务
    self.recursion(level +1,p1,...)
    
    #reverse the current level status if needed 进行收尾工作
    reverse_state(level)

分治:大问题分成小问题,一一解决

def divide_conquer(problem, param1, param2, ...):
    # recursion terminator终止条件
    if problem is None:
        print_result
        return

    # prepare data
    data = prepare_data(problem)
    subproblems = split_problem(problem,data)
    
    # conquer subproblems
    subresult1 = self.divide_conquer(subproblems[0],p1,...)
    subresult2 = self.divide_conquer(subproblems[1],p1,...)
    subresult3 = self.divide_conquer(subproblems[2], p1, ...)

    # reverse the current level status if needed 进行收尾工作
    result = process_result(subresult1,subresult2,subresult3,...)

计算x的n次方

pow(x,n)

一、直接调库函数 o(1)

二、暴力

写一个循环,每次乘以x,进行求解o(N)

三、让你算x的n次方,能不能让它一份为二,从中间劈开,想n是奇数还是偶数

如果是偶数,n/2刚好是中间,计算y = x ^n/2 result = y * y

如果是奇数,中间x先拿掉,当成偶数来计算,n/2刚好是中间,计算y = x ^n-1/2 result = y * y*x

x^n – x^n/2 – x^n/4 – x^1 or x^0 时间复杂度logN

def myPow(self,x,n):
    if not n :
        return 1
    if n < 0:
        return 1/self.myPow(x,-n)
    if n % 2:
        return x * self.myPow(x,n-1)
    return self.myPow(x*x,n/2)
def myPow(self,x,n): # 位运算
    if n < 0:
        x = 1/x
        n = -n
    pow = 1
    while n:
        if n & 1:# 二进制位
            pow *= x
        x *= x
        n >>= 1 # 右移一位
    return pow

求众数

并不是任何一个数组都有众数

一、暴力

写两个循环,枚举所有的x,针对每一个x进行计数,返回数值最大的x值(或者大于n/2)最后的时间复杂度是o(N^2)

二、map

{x:count(x)} loop ==> map count o(N)

三、sort[1,2,3,3,3] 重复的次数大于n/2 o(Nlogn)

四、分治

对于数组找左半边找到最大值放入left ,右边也找放入right

如果left == right 那么结果就是他们

如果不相同,比较count(left) count(right) 谁大返回谁 时间复杂度 o(nlogn)

注意:要解决比如出现[1,2,3]的情况

贪心算法

对问题求解,总是旨在当前做最好的选择

位运算

程序中在计算机内存中都是用二进制形式存储

位运算说穿,就是直接对整数在内存中的二进制位进行操作

由于为匀速那直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快

& 与 两个位都为1,结果才为1

| 或 两个位都为0,结果才为0

^ 异或 两个位相同为0,不同为1

_ 取反 0变1,1变0

<< 左移 各二进位全部左移若干位,高位丢弃,低位补0

》》 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算数右移)有的补0(逻辑右移)

实战常用的位运算操作
x&1 == 1 OR ==0 判断奇偶(x % 2 ==1)
x = x&(x - 1)=> 清零最低位的1 1010000 & 1001111 = 1000000
x & -x => 得到最低为的1 1010000 & 0100000 = 

动态规划

1、递归+记忆化 --> 递推

2、状态的定义 opt[n] dp[n] fib[n]

3、状态转移方程 opt[n] = best_of(opt[n-1], opt[n-2], …)

4、最优子结构

动态规划/回溯/贪心

回溯(递归):重复计算

贪心:永远局部最优

DP:记录局部最优子结构/多种记录值

Guess you like

Origin blog.csdn.net/weixin_44697051/article/details/115058850