个人算法总结

算法入门(python总结)

前言

这次的总结,主要是为了工作中的程序优化进行总结。所有的程序都是按照c语言,改成了python的语言结构(虽然有些麻烦,但是c真的忘完了)。之后的面试算法部分,就复习自己总结的即可。以下开始进行总结~

目录

ACM基础算法

1.枚举

思想

  • 枚举也称作穷举,指的是从问题所有可能的解的集合中一一枚举各元素。

  • 用题目中给定的检验条件判定哪些是无用的,哪些是有用的。能使命题成立。即为其解。

特点

  • 优点:算法简单,在局部地方使用枚举法,效果会十分的好

  • 缺点:运算量过大,当问题的规模变大的时候,循环的阶数越大,执行速度越慢。计算量容易过大

代码

#比如现在给你一个鸡兔同笼问题,
#你其实可以将所有的情况展示出来
#这里代码有点简单,就不展示了。

2.二分

思想

算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。

基本思想:假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,

当前位置的值如果等于k,就找到了,如果没找到k,

直到找到为止,时间复杂度:O(log(n))

代码

#list是传入的列表(必须是排完序的,排序顺序无所谓),item是你要的结果
def binary_search(list, item):	
     low = 0
     high = len(list)-1                         
     n = 0
     while low <= high:
        mid = int((low + high)/2)              
        if list[mid]==item:	#相等就找到了
            return mid
        if list[mid]<item:  #否则根据排序结果,拆分找前后的关系      
            low = mid + 1
        else:
            high = (mid-1)

3.查并集

思想

查并集是一种树型的数据结构,用于处理不相交集合的合并及查询问题。用于高效的查找某两个元素是否属于同一个集合。
查并集主要分为两种操作:查找和合并。

代码

class DSU:
    def __init__(self, nums):
        self.root_relation_list = [-1] * (nums+1)  # 如果从一开始 初始化父节点列表 需要(nums+1),从0开始则不用
        
    def find_root_node(self, node): # 找父节点
        while self.root_relation_list[node] != -1: # 即如果node的父节点不是-1,则继续网上找
            node = self.root_relation_list[node]  # 更新node
        return node
        
    def node_connect(self, node1, node2):  # 将两个节点相连
        node1_root = self.find_root_node(node1)
        node2_root = self.find_root_node(node2)
        if node1_root != node2_root:
            self.root_relation_list[node1_root] = node2_root
        else:
            print("connected")
        return

4.DFS深度优先

思想

网络上找的图

在这里插入图片描述

从A点出发,假设第一个点是C点,那么我们就将A和C点标记

在这里插入图片描述

然后开始往B点走,走到B点之后,再标记B点,往E点走

在这里插入图片描述

走到E点发现,没有路可以走了,回退到B点,也称之为回溯,看看B点有没有其他未标记的路可以走,如果没有,再回退到C点,此时D点没有被标记,往D点走。

在这里插入图片描述

D点走完,发现可以往A点走,但是A点又被标记过了,所以又回退到D点,而D点又没有其他路可以走了,回退到C点,C点又没有路可以走了,回退到A点,往F点走。

在这里插入图片描述

F点走完往G点走,将G点标记后,G点没有其他的路可以走了,回退到F点,F点也没有其他的路可以走了,回退到A点,A点没有其他的路可以走了,整个深度优先搜索也就完成了。

5.BFS

思想

img

图a就是我们最常见的bfs最短路讲解图(每个小方格里面记录的是左上角起点距当前格子的最短距离);图b则是我们按照bfs的遍历顺序,将每个点标号。此时我们就会发现,这路径是一棵树。倘若还是看不出来,请看下图:

img

理解了这个之后,就会很好的帮助我们输出bfs的遍历顺序。

6.DP动态规划

思想

朴素递归算法之所以效率很低,是因为它反复求解相同的子问题。因此,动态规划方法仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。如果随后再次需要此子问题的解,只需查找保存的结果,而不必重新计算。因此,动态规划方法是付出额外的内存空间来节省计算时间,是典型的时空权衡的例子。而时间上的节省可能是非常巨大的:可能将一个指数时间的解转化为一个多项式时间的解。如果子问题的数量是输入规模的多项式函数,而我们可以在多项式时间内求解出每个子问题,那么动态规划方法的总运行时间就是多项式阶的。

过程

img

动态规划的核心是定义状态和状态转移方程(注意,关键在于定义方程而非关注递推式的求解方法)

动态规划就是 使用递归的思路 找到正确的算法步骤使得得到正确的解,然后使用迭代的方式写出来。(如此便验证了那句自顶向下的考虑问题,自底向上的解决问题)

例子

爬楼梯问题
一个人爬楼梯,每次只能爬1个或两个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法
动态规划的状态转移:第 i 个状态的方案数和第 i-1, i-2时候的状态有关,即:dp[i]=dp[i-1]+dp[i-2],dp表示状态矩阵。

def climb_stairs(n):
    dp=[0]*n
    dp[0]=1
    dp[1]=2
    for i in range(2,n):
        dp[i]=dp[i-1]+dp[i-2]
    return dp[n-1]

7.贪心

思想

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

过程

建立数学模型来描述问题; 把求解的问题分成若干个子问题; 对每一子问题求解,得到子问题的局部最优解; 把子问题的解局部最优解合成原来解问题的一个解。

例子

分糖果(leetcode455)

题目:已知一些孩子和一些糖果,每个孩子有需求因子g,每个糖果有大小s,当某个糖果的大小s>=某个孩子的需求因子g时,代表该糖果可以满足该孩子,求使用这些糖果,最多能满足多少孩子(注意,某个孩子最多只能用1个糖果满足)

思考:
当某个孩子可以被多个糖果满足时,是否需要优先用某个糖果满足这个孩子?
当某个糖果可以满足多个孩子时,是否需要优先满足某个孩子?
贪心规律是什么?

贪心规律:
某个糖果如果不能满足某个孩子,则该糖果也一定不能满足需求因子更大的孩子

某个孩子可以用更小的糖果满足,则没必要用更大糖果满足,因为可以保留更大的糖果满足需求因子更大的孩子
孩子的需求因子更小则其更容易被满足,故优先从需求因子小的孩子尝试,可以得到正确的结果(因为我们追求更多的孩子被满足,所以用一个糖果满足需求因子较小或较大的孩子都是一样的)。
算法设计:

(1)对需求因子数组g和糖果大小数组s进行从小到大的排序
(2)按照从小到大的顺序使用各糖果尝试是否可满足某个孩子,每个糖果只尝试1次,只有尝试成功时,换下一个孩子尝试,直到发现没更多的孩子或者没有更多的糖果,循环结束。

class Solution:
    def findContentChild(self,g,s):
        g = sorted(g)
        s = sorted(s)
        child = 0
        cookie = 0
        while child < len(g) and cookie < len(s):
            if g[child] <= s[cookie]:
                child +=1
            cookie+=1
        return child
if __name__ =="__main__":
    g = [5,10,2,9,15,9]
    s = [6,1,20,3,8]
    S = Solution()
    result = S.findContentChild(g,s)

8.分治

思想

将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题----“分”
将最后子问题可以简单的直接求解----“治”
将所有子问题的解合并起来就是原问题打得解----“合”

  • 该问题的规模缩小到一定的程度就可以容易地解决

绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加

  • 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用

  • 利用该问题分解出的子问题的解可以合并为该问题的解;

如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。

面试用的

10大排序算法

1.冒泡排序

img

d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]
d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序

while 1:
    state = 0  # 假设本次循环没有改变
    for i in range(len(d0) - 1):
        if d0[i] > d0[i + 1]:
            d0[i], d0[i + 1] = d0[i + 1], d0[i]
            state = 1  # 有数值交换,那么状态值置1
    if not state:  # 如果没有数值交换,那么就跳出
        break

print(d0)
print(d0_out)

2.选择排序

img

def select_sort(data):
    d1 = []
    while len(data):
        min = [0, data[0]]
        for i in range(len(data)):
            if min[1] > data[i]:
                min = [i, data[i]]
        del data[min[0]]  # 找到剩余部分的最小值,并且从原数组中删除
        d1.append(min[1])  # 在新数组中添加
    return d1

if __name__ == "__main__":
    d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]  # 原始乱序
    d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序
    d1 = select_sort(d0)
    print(d1)
    print(d0_out)

3.插入排序

img

def direct_insertion_sort(d):  # 直接插入排序,因为要用到后面的希尔排序,所以转成function
    d1 = [d[0]]
    for i in d[1:]:
        state = 1
        for j in range(len(d1) - 1, -1, -1):
            if i >= d1[j]:
                d1.insert(j + 1, i)  # 将元素插入数组
                state = 0
                break
        if state:
            d1.insert(0, i)
    return d1

if __name__ == "__main__":
    d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]  # 原始乱序
    d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序
    d1 = direct_insertion_sort(d0)
    print(d1)
    print(d0_out)

4.堆排序

img

import numpy as np
d0 = [99, 5, 36, 7, 22, 17, 46, 12, 2, 19, 25, 28, 1, 92]
def sort_max(data):  # 直接冒泡一下吧,小到大
    for i in range(len(data) - 1):
        for j in range(len(data) - 1):
            if data[j] > data[j + 1]:
                data[j], data[j + 1] = data[j + 1], data[j]
    return data
def heap_min(data, type):
    index = 0
    if not type:
        for i in range(len(data[1:])):
            if data[index] > data[i + 1]:
                index = i + 1
        data[0], data[index] = data[index], data[0]
        return data
    else:
        for i in range(len(data[1:])):
            if data[index] < data[i + 1]:
                index = i + 1
        data[0], data[index] = data[index], data[0]
        return data
def heap_adj(data, type):  # data 原始堆,type=1最大堆,type=0最小堆
    length = len(data)
    floor = int(np.log2(length))
    for i in range(floor, 0, -1):  # 3(7 6 5 4)-2(3 2)-1(1)
        for j in range(2 ** floor - 1, 2 ** (floor - i) - 1, -1):
            # print(i,j)    # j-1 为当前父节点
            d_mid = [data[j - 1]]  # j = 7,j-1 =6 index
            if j * 2 <= length:  # 14
                d_mid.append(data[j * 2 - 1])
            if j * 2 + 1 <= length:
                d_mid.append(data[j * 2])

            d_mid = heap_min(d_mid, type)

            if len(d_mid) == 2:
                data[j - 1], data[j * 2 - 1] = d_mid[0], d_mid[1]
            elif len(d_mid) == 3:
                data[j - 1], data[j * 2 - 1], data[j * 2] = d_mid[0], d_mid[1], d_mid[2]
    return data
if __name__ == '__main__':
    d1 = []
    for i in range(len(d0)):
        data = heap_adj(d0, 0)
        d1.append(d0[0])
        del d0[0]
    print(d1)

5.快速排序

img

def quick_sort(data):
    d = [[], [], []]
    d_pivot = data[-1]  # 因为是乱序数组,所以第几个都是可以的,理论上是一样的
    for i in data:
        if i < d_pivot:  # 小于基准值的放在前
            d[0].append(i)
        elif i > d_pivot:  # 大于基准值的放在后
            d[2].append(i)
        else:  # 等于基准值的放在中间
            d[1].append(i)
    # print(d[0], d[1], d[2])
    if len(d[0]) > 1:  # 大于基准值的子数组,递归
        d[0] = quick_sort(d[0])
    if len(d[2]) > 1:  # 小于基准值的子数组,递归
        d[2] = quick_sort(d[2])
    d[0].extend(d[1])
    d[0].extend(d[2])
    return d[0]
if __name__ == "__main__":
    d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]  # 原始乱序
    d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序
    d1 = quick_sort(d0)
    print(d1)
    print(d0_out)

6.希尔排序

img

def direct_insertion_sort(d):  # 直接插入排序,因为要用到后面的希尔排序,所以转成function
    d1 = [d[0]]
    for i in d[1:]:
        state = 1
        for j in range(len(d1) - 1, -1, -1):
            if i >= d1[j]:
                d1.insert(j + 1, i)  # 将元素插入数组
                state = 0
                break
        if state:
            d1.insert(0, i)
    return d1
def shell_sort(d):  # d 为乱序数组,l为初始增量,其中l<len(d),取为len(d)/2比较好操作。最后还是直接省略length输入
    length = int(len(d) / 2)  # 10
    num = int(len(d) / length)  # 2
    while 1:

        for i in range(length):
            d_mid = []
            for j in range(num):
                d_mid.append(d[i + j * length])
            d_mid = direct_insertion_sort(d_mid)
            for j in range(num):
                d[i + j * length] = d_mid[j]
        # print(d)
        length = int(length / 2)
        if length == 0:
            return d
            break
        # print('length:',length)
        num = int(len(d) / length)
if __name__ == "__main__":
    d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]  # 原始乱序
    d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序
    d1 = shell_sort(d0)
    print(d1)
    print(d0_out)

7.归并排序

img

# 归并排序,还有些问题。其中有些细节需要重新理解
# 也是递归问题
def merge_sort(data):  # 分治发的典型应用,大问题拆分成小问题,逐个击破,之后将结果合并
    half_index = int(len(data) / 2)  # 将数组拆分
    d0 = data[:half_index]
    d1 = data[half_index:]
    if len(d0) > 1:
        d0 = merge_sort(d0)
    if len(d1) > 1:
        d1 = merge_sort(d1)
    index = 0
    for i in range(len(d1)):
        state = 1
        for j in range(index, len(d0)):
            if d1[i] < d0[j]:
                state = 0
                index = j + 1
                d0.insert(j, d1[i])
                break
        if state == 1:  # 如果大于d0这个队列的所有值,那么直接extend所有数据
            d0.extend(d1[i:])
            break
    return d0

if __name__ == "__main__":
    d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]  # 原始乱序
    d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]  # 正确排序
    d1 = merge_sort(d0)
    print(d1)
    print(d0_out)

8.计数排序

img

d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]
d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64]
d_max = 0
d_min = 0
for i in d0:
    if d_max < i:
        d_max = i
    if d_min > i:
        d_min = i
d1 = {}
for i in d0:
    if i in d1.keys():
        d1[i] += 1
    else:
        d1[i] = 1
d2 = []
for i in range(d_min, d_max + 1):
    if i in d1.keys():
        for j in range(d1[i]):
            d2.append(i)
print(d2)

9.桶排序

img

d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]
d1 = [[] for x in range(10)]
for i in d0:
    d1[int(i / 10)].append(i)
for i in range(len(d1)):
    if d1[i] != []:
        d2 = [[] for x in range(10)]
        for j in d1[i]:
            d2[j % 10].append(j)
        d1[i] = d2
d3 = []
for i in d1:
    if i:
        for j in i:
            if j:
                for k in j:
                    if k:
                        d3.append(k)
print(d3)

10.基数排序

img

1 d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44]
 d1 = [[] for x in range(10)]
 # 第一次 最小位次排序
 for i in d0:
     d1[i % 10].append(i)
 print(d1)
 d0_1 = []
 for i in d1:
     if i:
         for j in i:
             d0_1.append(j)
 print(d0_1)
 # 第二次 次低位排序
 d2 = [[] for x in range(10)]
 for i in d0_1:
     d2[int(i/10)].append(i)
 print(d2)
 d0_2 = []
 for i in d2:
     if i:
         for j in i:
             d0_2.append(j)
 print(d0_2)

常见算法

如何翻转一个单链表?

class Solution(object):
	def reverseList(self, head):
		"""
		:type head: ListNode
		:rtype: ListNode
		"""
# 申请两个节点,pre和 cur,pre指向None
		pre = None
		cur = head
# 遍历链表,while循环里面的内容其实可以写成一行
# 这里只做演示,就不搞那么骚气的写法了
		while cur:
# 记录当前节点的下一个节点
			tmp = cur.next
# 然后将当前节点指向pre
			cur.next = pre
# pre和cur节点都前进一位
			pre = cur
			cur = tmp
		return pre

斐波那契数列

def fib();
a, b = 0, 1
while true:
    yield a
    a, b = b, a+b

from itertools import islice
print list(islice(fib(), 5))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值