算法 基础

数据结构:数据(基本类型(int,float,char))的组织方式

算法复杂度

时间复杂度:用来估计算法运行时间的一个单位;O(n)、O(1) 常见于for循环, 或者log(n)—常见于while循环。循环减半时复杂度为log(n)

       常见时间复杂度排序  O(1) < O(logn)< O(n) < O(n^log n) < O(n^2) < O(n^2 log n ) < O(n^3)

timeit模块,测试python代码的执行速度;

空间复杂度:估算算法内存占用大小的单位;"空间换时间";  O(1),O(n)

算法:完成算法基础模块后,算法优化是关键点(方法:查找算法模块中重复或者无效计算,用内层加判断替换)

递归算法:调用自身,结束条件 --- 汉诺塔,斐波那契数列

列表查找:顺序查找(for循环),二分查找(数据候选区二分data[:0],查找范围缩减一半)

                  排序l初级:冒泡、选择、插入;-------  思想:数据分区(有序区/无序区,已查找/未查找),数据比较,数据换位

                  排序高级:快速排序、堆排序、归并排序

 python语言使用的内置排序方法是timsort,原理基于归并排序

#冒泡排序:列表中每相邻的两个数,如果前面的比后面的大,则互相替换位置
def bubble_sort(li):
for i in range(len(li)-1): # i 表示第N趟,一共n或者n-1趟
exchange = False
for j in range(len(li) - i - 1): #第i趟,无序区【0,n-i-1】 j 表示箭头 0- n-i-2
if li[j] > li[j+1]:
li[j],li[j+1] = li[j+1],li[j]
exchange = True
if not exchange:
break

#选择排序:选择最小的数排在第一的位置
def select_sort(li):
for i in range(len(li)-1): #n 或者n-1 趟
min_pos = i #第i趟无序区范围 i~最后 #min_pos 更新为无序区最小值位置
for j in range(i+1,len(li)):
if li[j] < li[min_pos]:
min_pos = j
li[i],li[min_pos] = li[min_pos],li[i]
#插入排序:挪位置,不断地将尚未排好序的数插入到已经排好序的部分。
def insert_sort(li):
for i in range(1,len(li)): #i表示摸到的牌的下标
tmp = li[i] #摸到的牌
j = i - 1 #排好序的最后一张牌的位置
while j >= 0 and li[j] > tmp: #只要往后挪就循环, 2个条件都得到满足
li[j+1] = li[j] #如果 j = -1 停止挪,如果li[j] 小了,停止挪
j -= 1 #j位置在循环结束时要么是 -1,要么是一个比tmp小的值
li[j+1] = tmp

 位运算: & | - ^ ~

快速排序:取一个元素P,列表被P分成两部分,左边比P小,右边比P大,递归完成排序
import sys 
sys.setrecursionlimit(5000) #设置递归深度
def quick_sort(li):
_quick_sort(li,0,len(li)-1)
def _quick_sort(li,left,right):
    if left < right:
        mid = partition(li,left,right)
        _quick_sort(li,left,mid-1)
        _quick_sort(li,mid+1,right)

def partition(li,left,right):
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li [left]
    li[left] = tmp
    return left
#归并排序 两个有序列表合并成一个有序列表  :先分解,后排序,分而治之
def merge(li,low,mid,high):
    i = low
    j = mid +1
    li_tmp = []
    while i <= mid and j <= high:
        if li[i] <= li[j]:
            li_tmp.append(li[i])
            i += 1
        else:
            li_tmp.append(li[j])
            j += 1
    while i <= mid:
        li_tmp.append(li[i])
        i += 1
    while j <= high:
        li_tmp.append(li[i])
        j += 1
    for i in range(low,high+1):
        li[i] = li_tmp[i-low]
        
def merge2list(li1,li2):
    li = []
    i = 0
    j = 0
    while i < len(li1) and j < len(li2):
        if li1[i] <= li2[j]:
            li.append(li1[i])
            i += 1
        else:
            li.append(li2[j])
            j += 1
    while i < len(li1):
        li.append(li1[i])
        i += 1
    while j < len(li2):
        li.append(li2[j])
        j += 1
    return li
#先递归,后归并
def merge_sort(li,low,high):
if low < high:
mid = (low + high)//2 #先分解
merge_sort(li,low,mid)
merge_sort(li,mid+1,high)
merge(li,low,mid,high) #归并合并

li = [10,4,6,3,8,2,5,7]
merge_sort(li,0,len(li)-1)
print(li)

堆排序前传———树与二叉树

树是一种数据结构,是一种可以递归定义的数据结构,是有n个节点组成的集合

树的概念:根节点、叶子节点、深度、孩子节点/父节点,子树

def sift(data,low,high):
    i = low  # i 最开始指向根节点
    j = 2*i + 1 # j 开始是左孩子
    tmp = data[i] # 把堆顶存起来
    while j <= high: #只要j位置有数
        if j <high and data[j] <data [j+1]:  #如果右孩子有且比较大
            j += 1 # j指向右孩子
        if tmp < data[j]:
            data[i] = data[j]
            i = j  # 往下看一层
            j = 2*i +1 
        else: #tmp更大,把tmp放在i的位置上
            data[i] = tmp #把tmp放在某一级领导位置上
            break
    data [i] = tmp  # 把tmp放在叶子节点上

def heap_sort(data):
    n = len(data)
    for i in range(n//2 -1,-1,-1): # i 表示建堆时调整部分根的下标
        sift(data,i,n-1) #建堆完成
    for i in range(n -1,-1,-1): #i指向当前堆最后一个元素
        data[0],data[i] = data [i],data[0]
        sift(data,0,i-1) # i-1是新的high

# python内置堆模块
import heapq
import random
li = list(range(20))
random.shuffle(li)
heapq.heapify(li) #建堆
heapq.heappop(li)  #弹出最小值
n = len(li)
for i in range(n):
    print(heapq.heappop(li),end = ',')

 

 

希尔排序:
def
insert_sort(li,d): for i in range(d,len(li)): tmp = li[i] j = i - d while j >= 0 and li[j]> tmp: li[j+d] = li[j] j -= d li[j+d] = tmp def shell_sort(li): d = len(li)//2 while d > 0: insert_sort(li,d) d = d//2 li = list(range(1000)) random.randint(li) insert_sort(li)
计数排序
def
count_sort(li,max_num = 100): count = [0 for _ in range(max_num +1)] for val in li: count[val] += 1 li.clear() for i,v in enumerate(count): for _ in range(v): li.append(i)

栈:数据集合,只能在一端进行插入或删除操作的列表,特点是后进先出(last-in,first_out)。栈的基本操作:进栈push,出栈pop,取栈顶gettop。

队列:数据集合,仅允许在列表一端进行插入,另一端进行删除。插入的一端是队尾,删除的称为队头。先进先出。

迷宫找路
dirs = [ lambda x,y:(x-1,y) # lambda x,y:(x,y+1) # lambda x,y:(x+1,y) # lambda x,y:(x,y-1) # ] def solve_maze(x1,y1,x2,y2): stack = [] stack.append((x1,y1)) maze[x1][y1] = 2 while len(stack) > 0: # 当栈不空时循环 cur_node = stack[-1] if cur_node == (x2,y2): #如果到终点了 print(stack) return True for d in dirs: next_node = d(*cur_node) if maze[next_node[0]][next_node[1]] ==0: stack.append(next_node) maze[next_node[0]][next_node[1]] = 2 # 2表示已经走过的点 break else: stack.pop() else: print('无路') return False

 

  • 冒泡排序和插入排序是最基础的,面试官有时候喜欢拿它们来考察你的基础知识,并且看看你能不能快速地写出没有 bug 的代码。
  • 归并排序、快速排序和拓扑排序的思想是解决绝大部分涉及排序问题的关键,我们将在这节课里重点介绍它们。
  • 堆排序和桶排序,本节课不作深入研究,但有时间的话一定要看看,尤其是桶排序,在一定的场合中(例如知道所有元素出现的范围时),能在线性的时间复杂度里解决战斗

冒泡排序(Bubble Sort)

基本思想

给定一个数组,我们把数组里的元素通通倒入到水池中,这些元素将通过相互之间的比较,按照大小顺序一个一个地像气泡一样浮出水面。
实现

每一轮,从杂乱无章的数组头部开始,每两个元素比较大小并进行交换,直到这一轮当中最大或最小的元素被放置在数组的尾部,然后不断地重复这个过程,直到所有元素都排好位置。其中,核心操作就是元素相互比较。
例题分析

给定数组 [2, 1, 7, 9, 5, 8],要求按照从左到右、从小到大的顺序进行排序。 
解题思路

从左到右依次冒泡,把较大的数往右边挪动即可。空间复杂度是 O(1)

 

插入排序(Insertion Sort)

基本思想

不断地将尚未排好序的数插入到已经排好序的部分。空间复杂度是 O(1),时间复杂度是 O(n2)
特点

在冒泡排序中,经过每一轮的排序处理后,数组后端的数是排好序的;而对于插入排序来说,经过每一轮的排序处理后,数组前端的数都是排好序的。
例题分析

对数组 [2, 1, 7, 9, 5, 8] 进行插入排序。
解题思路

首先将数组分成左右两个部分,左边是已经排好序的部分,右边是还没有排好序的部分,刚开始,左边已排好序的部分只有第一个元素 2。接下来,我们对右边的元素一个一个进行处理,将它们放到左边。

 

归并排序(Merge Sort)

基本思想

核心是分治,就是把一个复杂的问题分成两个或多个相同或相似的子问题,然后把子问题分成更小的子问题,直到子问题可以简单的直接求解,最原问题的解就是子问题解的合并。归并排序将分治的思想体现得淋漓尽致。空间复杂度就是 O(n)。归并排序也是稳定的排序算法
实现

一开始先把数组从中间划分成两个子数组,一直递归地把子数组划分成更小的子数组,直到子数组里面只有一个元素,才开始排序。
排序的方法就是按照大小顺序合并两个元素,接着依次按照递归的返回顺序,不断地合并排好序的子数组,直到最后把整个数组的顺序排好。

 

快速排序(Quick Sort)

基本思想

快速排序也采用了分治的思想。
实现

把原始的数组筛选成较小和较大的两个子数组,然后递归地排序两个子数组。

 

拓扑排序(Topological Sort)

基本思想

和前面介绍的几种排序不同,拓扑排序应用的场合不再是一个简单的数组,而是研究图论里面顶点和顶点连线之间的性质。拓扑排序就是要将这些顶点按照相连的性质进行排序。
要能实现拓扑排序,得有几个前提:
图必须是有向图、图里面没有环,拓扑排序一般用来理清具有依赖关系的任务。

  • 将问题用一个有向无环图(DAG, Directed Acyclic Graph)进行抽象表达,定义出哪些是图的顶点,顶点之间如何互相关联。
  • 可以利用广度优先搜索或深度优先搜索来进行拓扑排序。

 

 

相关资源: 数据结构与算法

posted @ 2020-02-08 16:30  Christbao  阅读(215)  评论(0编辑  收藏  举报