• 算法图解笔记


    一、二分查找

    1. 仅当列表是有序的时候,二分查找才有用。

    2. 对于包含n个元素的有序列表,用二分查找最多需要log2n步(运行时间为对数时间或log时间),而简单查找最多需要n步。

    代码:

     1 def binary_search(inlist,item):
     2     low = 0 # python:from 0
     3     high = len(inlist)-1  
     4     while low<=high:
     5         mid = (low + high)/2
     6         guess = inlist[mid]
     7         if guess == item:
     8             return mid
     9         if guess <item:
    10             low = mid + 1
    11         if guess >item:
    12             high = mid - 1
    13     return None
    14 
    15 my_list = [1,3,5,7,9]
    16 print(binary_search(my_list,3)) #output:1
    17 print(binary_search(my_list,-1)) #output:None

    二、大O表示法

    1. 比较的是算法的操作数

    2. 并为单位

    3. 算法的速度指的并非是时间,而是操作数的的增速

    4. 指出最糟糕情况下的运行时间

    从快到慢的顺序

    O(logn)<O(n)<O(n*logn)<O(n2)<O(n!)  对应于: 对数时间(二分查找)< 线性时间(简单查找)<(快速排序)<(选择排序)<(商旅问题:遍历n个城市,要求总旅程最小)

    三、数组与链表

    数组:随机访问、读取数据时有优势、

    链表:顺序访问、插入与删除数据时有优势、读取数据时从头开始遍历(读取所有元素时)

      数组 链表
    读取 O(1) O(n)
    插入 O(n) O(1)
    删除 O(n) O(1)

    四、递归

    1. 递归由两部分组成:基线条件(函数不再调用自己)和递归条件(函数调用自己)

    循环的性能更好,递归让解决方案更清晰,并没有性能上的优势。

    2. 栈:“后进先出”,始终在栈顶工作。

    3. 调用栈(call stack)即:存储多个函数的变量,描述的是函数之间的调用关系,它由多个栈帧(stack frame)组成,每个栈帧对应着一个未运行完毕的函数,栈帧中保存了该函数的返回地址和局部变量,因而不仅能在执行完毕后找到正确的返回地址,还很自然地保存了不同函数间的局部变量互不相干——因为不同函数对应着不同的栈帧。

       调用另一个函数时,当前函数暂停并处于未完成的状态。

    4. 使用栈很方便,但是由于其存储详尽的信息(大量的函数调用的信息)会占用大量的内存。解决:改用循环或者尾递归(并非所有语言都支持)。

    5. 所有函数的调用都进入调用栈

    五、几种排序算法

    性能:直接插入排序 < 选择排序 < 冒泡排序 < 合并排序 < 快速排序

    1. 直接插入排序

    直接插入排序(Straight Insertion Sort)的基本思想是(两种不同的表述):

    1)把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。

    2)每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。

     1 #按照定义
     2 #错误代码
     3 def Insertsort1(inlist):
     4     length = len(inlist)
     5     #遍历(1...n-1)
     6     for i in range(1,length):
     7         j=i-1
     8         while j>=0 :
     9             if inlist[j]<inlist[i]:
    10                 break
    11             j = j-1
    12         #print(j)
    13         if j!=i-1:
    14             temp = inlist[i]
    15             for k in range(i-1,j+1,-1): #error
    16                 inlist[k+1] = inlist[k]
    17             inlist[j+1] = temp
    18     return inlist
    19 #正确代码
    20 def Insertsort2(inlist):
    21     length = len(inlist)
    22     #遍历(1...n-1)排序(0,1)(0..2)(0..n-1)
    23     for i in range(1,length):
    24         j=i-1
    25         while j>=0 :
    26             if inlist[j]<inlist[i]:
    27                 break
    28             j = j-1
    29         #print(j)
    30         if j!=i-1:
    31             temp = inlist[i]
    32             for k in range(i-1,j,-1): # right
    33                 inlist[k+1] = inlist[k]
    34             inlist[j+1] = temp
    35     return inlist
    36     
    37 my_list = [1,3,2,1,0,7,4]
    38     
    39 res = Insertsort2(my_list)
    40 print(res) # [0, 1, 1, 2, 3, 4, 7]
    41 #!!!
    42 for i in range(2,0,-1):
    43     print(i)  # 2,1 
    44 #0不会被输出

    2. 冒泡排序

     冒泡排序的基本思想是,对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序

     1 def swap(arr,i,j):
     2     arr[i] = arr[i] + arr[j]
     3     arr[j] = arr[i] - arr[j]
     4     arr[i] = arr[i] - arr[j] 
     5     return arr
     6 #错误代码
     7 def selectionSort_3(inarr):
     8     for i in range(len(inarr)):
     9         for j in range(i,len(inarr)-1): # error
    10             if inarr[j]>inarr[j+1]:
    11                 inarr = swap(inarr,j,j+1)
    12     return inarr
    13 #正确代码
    14 def selectionSort_4(inarr):
    15     for i in range(len(inarr)):
    16         for j in range(len(inarr)-1-i):
    17             if inarr[j]>inarr[j+1]:
    18                 inarr = swap(inarr,j,j+1)
    19     return inarr    
    20 arr_sort =  selectionSort_4([1,3,2,1,0,7,4])
    21 print(arr_sort)  # [0, 1, 1, 2, 3, 4, 7]
    22 #每一次比较,会将最大的值传到数组的后端!!!

    3. 选择排序

     选择排序法 是对 定位比较交换法(也就是冒泡排序法) 的一种改进。每趟从待排序的记录中选出关键字最小(或最大)的记录,顺序放在已排序的记录序列末尾,直到全部排序结束为止。依次挑选最小、第二小、第三小。。。 

     1 def findSmallest(inarr):
     2     smallest = inarr[0]
     3     smallest_index = 0
     4     for i in range(1,len(inarr)):
     5         if inarr[i] < smallest:
     6             smallest_index = i
     7             smallest = inarr[i]
     8     return smallest_index
     9 # 建立新数组    
    10 def selectionSort(arr):
    11     newArr = []
    12     for i in range(len(arr)):
    13         smallest_index = findSmallest(arr)
    14         newArr.append(arr.pop(smallest_index))
    15     return newArr
    16 
    17 arr_sort =  selectionSort([1,3,2,1,0,7,4])
    18 print(arr_sort)  # [0, 1, 1, 2, 3, 4, 7]
     1 #交换元素
     2 #错误代码:
     3 def selectionSort_1(arr):
     4     for i in range(len(arr)):
     5         smallest_index = findSmallest(arr[i:len(arr)])
     6         if i!= smallest_index:
     7             arr[i] = arr[i] + arr[smallest_index]
     8             arr[smallest_index] = arr[i] - arr[smallest_index]
     9             arr[i] = arr[i] - arr[smallest_index]
    10     return arr
    11 # smallest_index = findSmallest(arr[i:len(arr)]) 返回的索引是子序列的索引,与arr索引不一样
    12 #arr=[1,2,3,4,5],i=1时,findSmallest(arr[1:5])返回的索引为0(2对应的位置),但其在arr处的索引是1。
    13 #修改代码
    14 def selectionSort_1(arr):
    15     for i in range(len(arr)):
    16         smallest_index = findSmallest(arr[i:len(arr)])
    17         locate = i + smallest_index #!!!
    18         if i!= locate:
    19             arr[i] = arr[i] + arr[locate]
    20             arr[locate] = arr[i] - arr[locate]
    21             arr[i] = arr[i] - arr[locate]
    22     return arr
    23     
    24 arr_sort =  selectionSort_1([1,3,2,1,0,7,4])
    25 print(arr_sort)  # [0, 1, 1, 2, 3, 4, 7]

    4. 快速排序

     1)选择基准值

     2)将数组分成两个子数组:小于基准值的元素和大于基准值的元素

     3)对这两个数组进行快速排序(递归调用)

     1 def quickSort(inarr):
     2     if len(inarr)<2: # 空数组或只含有一个数组的元素
     3         return inarr
     4     pivot = inarr[0]
     5     less = [i for i in inarr[1:] if i <= pivot]
     6     greater = [i for i in inarr[1:] if i > pivot]
     7     return quickSort(less) + [pivot] + quickSort(greater)
     8 
     9 my_list = [1,3,2,1,0,7,4]
    10 print(quickSort(my_list)) 

    5.合并排序(merge sort)

    算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!

      直接插入排序 冒泡排序 选择排序 合并排序 快速排序
    算法复杂度  O(n2)  O(n2)   O(n2) O(nlogn)  O(nlogn)
    算法稳定性          

    注:1. 在大O表示法中n是操作数,用c表示算法所需要的固定时间量,c*n表示算法时间。当两种算法的大O运行时间不同时,这种常量无关紧要。如直接插入排序与快速排序。但对合并排序与快速排序来说,常量的影响很大,由于快速查找的常量比合并查找的常量小,因此运行时间都为O(nlogn),快速查找的时间更快。

           2. 快速排序的最糟情况下,运行时间为O(n2)。最佳情况即平均情况,运行时间为O(nlogn)

    六、分治算法

    1. 分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

    2. 分治与递归像一对孪生兄弟,经常同时应用在算法设计之。

    3. 分治法适用的情况

        分治法所能解决的问题一般具有以下几个特征:

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

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

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

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

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

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

    第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

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

    4. 分治法在每一层递归上都有三个步骤:

        step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

        step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题

        step3 合并:将各个子问题的解合并为原问题的解。

    七、 

  • 相关阅读:
    mac允许安装任何来源的软件
    Xcode Edit Schemes
    认识下算法工程师
    一定要熟练地使用常用的Foundation服务
    常用的UI控件
    认识iOS系统架构
    深拷贝/浅拷贝的理解
    Create Fiori List App Report with ABAP CDS view – PART 1
    [転載]SAP S/4HANAトライアル環境でCDS Viewを確認
    ABAP CDS
  • 原文地址:https://www.cnblogs.com/GuoXinxin/p/10269603.html
Copyright © 2020-2023  润新知