算法浅尝
一、算法的定义
算法其实就是一个计算的过程,解决问题的方法,我们每个人针对某个问题写的一段代码都可以叫算法,只是大部分人写的算法并不高明而已。
二、时间复杂度和空间复杂度
时间复杂度:估计算法运行时间的单位。时间复杂度的简单算法:没有循环O(1),x层循环就是n的x次方即O(n**x),循环中有减半的操作就是O(logn)
一般情况,时间复杂度越低算法越快,按照执行效率排序依次为:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n**3)
空间复杂度:估计算法占用内存大小。空间复杂度简单算法:定义变量O(1),x维列表O(n**x).几维列表就是列表嵌套了几层
三、列表查找
列表查找就是从列表中找出指定元素,其实现方法有以下两种:
顺序查找:按顺序从头到尾遍历列表
二分查找:从有序列表的候选区中取出中间值与指定元素比较,从而使候选区减半
四、列表排序
前面说到二分查找的前提使列表必须是有序的,那么如何让无序列表变成有序列表,这里就需要用到排序算法了,主要的排序算法有以下九种:
Low B三人组:
冒泡排序:每两个相邻的数比较,大的那个放到后面,每一趟走完都会把剩余未排序的的数中最大的数放到列表后面相应的位置,完成排序需要n-1趟 每趟需要的比较次数n-i(已完成排序的数)-1
时间复杂度:O(n**2)
代码如下:
def bubble_sort(li): for i in range(len(li)-1): #循环多少趟 exchange=False #表示一趟是否发生交换 for j in range(len(li)-i-1): #每一趟需要比较多少次 if li[j]>li[j+1]: #每次比较都把更大的那个往后放 li[j],li[j+1]=li[j+1],li[j] exchange=True if not exchange: #如果一趟走完exchange为False,则表示之后不需要排序,停止排序 break
选择排序:每趟选出未排序的数中最小的数放在列表前面相应位置
时间复杂度:O(n**2)
代码如下:
def select_sort(li): for i in range(len(li)-1): #需要循环多少趟 min_loc=i #初始最小的数的下标 for j in range(i+1,len(li)): #初始最小数与它后面的数进行比较 if li[j]<li[min_loc]: #每次发现有更小的数就把最小数的下标改为该数的下标 min_loc=j li[i],li[min_loc]=li[min_loc],li[i] #一趟走完之后把该趟中找到最小的数换到该趟初始最小数的位置
插入排序:以第一个元素为基准,后面的元素跟它前面的所有元素依次比较,如果待排序元素前面的元素有比它大的就让那些元素往后移动然后让待排序元素插入到空缺的位置,整个过程就像打牌时理牌一样。
时间复杂度:O(n**2)
代码如下:
def insert_sort(li): for i in range(1,len(li)): #(从第二个数开始排序) tmp=li[i] #将待排序的数存起来,以免后面元素移动后被其他元素覆盖无法取出 j=i-1 #待排序元素前面第一个元素 while j>=0 and li[j]>tmp: 当比较元素索引小于等于第一个元素索引且比待排序元素大时,让比较元素往后移动一个位置,同时取出更前面的数的索引再次比较,如此循环直到条件不满足为止 li[j+1]=li[j] j-=1 li[j+1]=tmp #由于每次比较完j都会变为空缺位置前面的索引,所以当进行插入操作时需要将索引往后退一个
比较NB的排序算法:
快速排序:找到第一个元素用变量存起来,然后从右边找一个比取出的元素小的元素放到空出的位置,再从左边找一个比取出元素大的元素放到刚才移到左边的元素原来的位置,如此反复直到左右两边空缺的位置重合再将取出的元素放入该位置,最后用同样的方法处理已排序元素左右两边的元素。快速排序一般情况下的时间复杂度为O(nlogn),在最坏情况下即列表中元素为逆向排序(算法从小到大排,列表元素从大到小排列)其时间复杂度为O(n**2),此外在Python中还有递归深度的问题需要手动设置。
时间复杂度:O(nlogn)
代码如下:
def quick_sort(data,left,right): if left < right: mid=partition(data,left,right) quick_sort(data,left,mid-1) quick_sort(data,mid+1,right) def partition(data,left,right): tmp=data[left] #取出每次排序一开始最左边的数 while left < right: #当最小索引小于最大索引时,表示没有完成把比tmp小的数放左边,比tmp大的数放右边的任务, while left < right and data[right] >=tmp: #找到右边比tmp小的数然后放到左边,为了避免right不停减小于left或者left不停加大于right使排序出现错误需要再加一次判断left<right,保证当right=left时循环能够终止,此时列表的元素顺序不会发证变化 right-=1 data[left]=data[right] while left < right and data[left] <=tmp:找到左边比tmp大的数放到右边上一步移到左边的那个元素之前的索引位置 left+=1 data[right]=data[left] data[left]=tmp #当最小索引等于最大索引表示任务完成,当前索引值就是tmp应该插入的位置
return left #返回当前的left索引值
最NB的排序算法:
堆排序:
完全二叉树:二叉树从上到下从左到右结构完整,只在最底层的最右边开始一直往左依次缺少元素。
如果用顺序存储(列表)二叉树时,其子树的父节点与左孩子节点的下标的关系为:i~2i+1,与右孩子节点下标的关系:i~2i+2
       堆排序中有大根堆与小根堆之分,大根堆就是父节点都比子节点大,小根堆与之相反
调整:当子树根节点不满足堆排序的要求(默认是大根堆),而其子元素满足要求时,就从子元素中取出最大的那个作为新的父节点,原来的父节点元素如果比新父节点之前的子元素都大就把它放到新父节点之前的位置,否则就从新父节点之前的子元素中取出最大的那个作为该级子树的新父节点,如此循环。
建堆:从最后一个子树开始往上不断调整。
堆排序:将堆顶元素取出,将最底层的最右边的元素放到堆顶,然后再做一次调整,将新的堆顶元素取出放在之前拿出来的元素的前面,如此循环。
时间复杂度:O(nlogn)
代码如下:
#调整 def sift(data,low,high): #low为子树的根节点下标,high为子树的最底层最右边的元素的下标 i=low #子树的根节点 j=2*i+1 #左孩子节点 tmp=data[i] while j <=high: #左孩子下标小于等于列表的最大下标 if j+1<= high and data[j]<data[j+1]: #子树有右孩子就比较然后取出最大的孩子节点 j+=1 if data[j] > tmp: #子树根节点小于最大的孩子节点就让该孩子节点成为新的根节点 data[i]=data[j] i=j j=2*i+1 else: break data[i]=tmp #让最开始的根节点元素进入空位
def sift_sort(li):
n=len(li)
for i in range(n// 2 - 1, -1, -1): #构建堆
sift(li, i, n - 1)
for i in range(n - 1, -1, -1): # i指向堆的最后
li[0], li[i] = li[i], li[0] # 领导退休,刁民上位
sift(li, 0, i - 1) # 调整出新领导
return li
#下面的代码做TOP排序使用
def topn(li, n):
heap = li[0:n]
#构建堆,n//2-1表示从上往下最后一个子树的父节点索引
for i in range(n // 2 - 1, -1, -1):
sift(heap, i, n - 1)
#遍历,使最后的集合元素为原来集合中相对最小的那些数
for i in range(n, len(li)):
if li[i] < heap[0]:
heap[0] = li[i]
sift(heap, 0, n - 1)
#排序
for i in range(n - 1, -1, -1): # i指向堆的最后
heap[0], heap[i] = heap[i], heap[0] # 领导退休,刁民上位
sift(heap, 0, i - 1) # 调整出新领导
return heap
 
归并排序:
归并:将一个左边和右边各自有序的列表从左边有序部分的尾部分成两部分,然后不断从左边和右边各自取出最小的数进行比较,将相对更小的数放到一个新的列表中,直到有一边的数取完后就将另外一边的剩余部分放入列表,这个过程成为一次归并。
归并排序利用归并的特性,先将一个列表不停的对半分直到分到只剩两个元素就进行一次归并,然后再将排序后的两个小部分合起来再进行一次归并,最后将已经排序的最初的左右两个部分合起来再进行一次归并
时间复杂度:O(nlogn)
空间复杂度:O(n)
代码如下:
def merge(li,low,mid,high): #一次归并的过程 i=low j=mid+1 ltmp=[] while i <=mid and j<=high: #当两边都有数的情况下进行排序 if li[i] < li[j]: ltmp.append(li[i]) i+=1 else: ltmp.append(li[j]) j+=1 while i<=mid: #当只剩左边有数时将剩余的数放入列表 ltmp.append(li[i]) i+=1 while j<=high: #当只剩右边有数时将剩余的数放入列表 ltmp.append(li[j]) j+=1 li[low:high+1]=ltmp #将原来的部分用排序后的列表进行替换 def mergesort(li,low,high): #归并排序,将最初的列表分为两部分,然后先排序左边,左边先分成最小单元为两个元素的列表,然后分别排序,再将排序后的最小单元两两合并再进行排序,以此类推,直到最初的左边部分有序,然后同样的排序最初的右边部分,最后将左右两部分排序 if low < high: mid=(low+high)//2 mergesort(li,low,mid) mergesort(li,mid+1,high) merge(li,low,mid,high)
其他排序算法:
计数排序:当元素的大小在一定范围内,开辟一个元素个数对应最大范围的计数列表,其中的元素都是0,然后循环数据列表,每次循环将计数列表中索引值与数据相等的元素加一,最后循环取出计数列表的索引和元素,元素的大小即代表数据列表中与索引值相等的数出现的次数,从小到大往原数据列表写值,每个数据出现几次就在原数据列表中写几次。
时间复杂度:O(n)
代码如下:
def count_sort(li,max_num): count=[0 for i in range(max_num+1)] #计数 for num in li: count[num]+=1 i=0 for num,m in enumerate(count): #排序 for j in range(m): li[i]=num i+=1
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号