python--算法
一、算法入门
第一节:概念:一个计算过程,解决问题的方法。

第二节:时间复杂度:用来评估算法运行效率


常见时间复杂度(效率排序)
O(1)<O(logn)<O(n)<O(nlogn)<O(n**2)
总结:判断时间复杂度:
1、循环减半的过程-->O(logn)。
2、几次循环就是n的几次方的复杂度。
第三节:空间复杂度:评估算法内存占用大小的一个式子
第四节:二分查找和递归
第一点:二分查找 (时间复杂度是:O(logn))
思路:按比例逐步缩小需要考虑的数据范围,从而快速的逼近需要找到的数据。
从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以是候选区减少一半。
@cal_time def binary_search(li, val): left = 0 right = len(li) - 1 while left <= right: # 候选区非空 mid = (left + right) // 2 if li[mid] == val: return mid elif li[mid] < val: # 更新候选区 left = mid + 1 else: right = mid - 1 # 更新候选区 return "该值不存在"
第二点:递归 {1.调用自己;2.有结束条件}
@cal_time def bin_search_rec(data,val,low,high): if low <= high: mid = (low+high) //2 if data[mid] == val: return mid elif data[mid] > val: return bin_search_rec(data,val,low,mid-1) else: return bin_search_rec(data,val,mid+1,high) else: return None
二、排序
简单排序:冒泡、选择、插入;无须列表变成有序列表。
第一章:冒泡排序
思路:后者大于前者,则将后者与前者的位置替换
@cal_time def bubble_sort(li): ''' 冒泡排序,简单版 :param li: :return: ''' for i in range(len(li) - 1): # i表示第i次 why减1:最后一次不用再循环 for j in range(len(li) - i - 1): # j表示箭头的下标 if li[j] > li[j + 1]: # 如果后者大于前者 li[j], li[j + 1] = li[j + 1], li[j] # 后者与前者替换 return li def bubble_sort_nb(li): ''' 升级版,如果前后连个数不用替换,则结束算法 :param li: :return: ''' 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转台没有变,则前后不用替换,结束里层循环 break
return li
冒泡排序时间复杂度:O(n**2)
class Node(object): def __init__(self, item): self.item = item self.next = None def create_linklist_head(li): head = None for v in li: p = Node(v) p.next = head head = p return head ll = create_linklist_head([3, 5, 2, 6, 4]) # [4, 6, 2, 5, 3] # [4, 2, 5, 3, 6] def buddle_sort(head): p = head flag = True while p.next: val = p.item next = p.next if val < next.item: flag = False temp = val # 第一个值赋值给一个中间变量 p.item = next.item # 将后一个值赋值给前一个值 next.item = temp # 将前一个值后一个值 p = next # 改变比较的值 if flag: return head return buddle_sort(head) ret = buddle_sort(ll) def print_linklist(head): p = head while p: print(p.item) p = p.next print_linklist(ret)
第二章:选择排序
如何选出列表中的最小值;正向排序与反向排序
思路:最小值默认是无序区的第一个数;下一个数如果小于前一个数;则将最小值替换。
@cal_time def select_sort(li): for i in range(len(li) - 1): # i表示次数 min_pos = i # 最小值默认为无序区第一个数 i for j in range(i + 1, len(li)): if li[j] < li[min_pos]: min_pos = j if min_pos != i: li[i], li[min_pos] = li[min_pos], li[i] li = list(range(100)) random.shuffle(li) select_sort(li)
最大数反向判断: if li[j] > li[min_pos]:
时间复杂度:O(n**2)
第三章:插入排序
思路:把列表分成有序区和无序区,最初有序区只有一个元素。
每次从无序区选择一个元素,插入到有序去的位置,直到无序区变空。
@cal_time def insert_sort(li): for i in range(1, len(li)): # i表示摸到牌的位置 ;从1开始循环,列表里留一,就是手里的牌 j = i - 1 # 手里牌的位置 tmp = li[i] # 摸到的牌 while j >= 0 and li[j] > tmp: # 手里有牌并且手里的牌大于摸到的牌 li[j + 1] = li[j] j -= 1 li[j + 1] = tmp # 将摸到牌查到手里牌的后面 li = list(range(100)) random.shuffle(li) insert_sort(li) print(li)
时间复杂度:O(n**2)
==================================================
高级排序:快排、堆排、归并
第四章:快速排序 《******》
思路:
取出一个元素p,使元素归位;
列表被p分成两部分,左边都比p小,右边都比p大;
递归完成排序
快排的三步骤: (1)在数据集之中,选择一个元素作为"基准"(pivot)。 (2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。 (3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
import random from cal_time_my import * def partition(li, left, right): tmp = li[left] # 取列表的最左数为参考点,不能写li[0],因为要分段递归 while left < right: # 外层大循环的条件是,左<右,至少有两个数才能循环,等于时,到了中间,大于时,错位,都应跳出循环 while left < right and li[right] >= tmp: # 【右循环一次】如果右边的值大于等于基值,应该留在右边,继续向左,所以right -=1;只要left=right就退出循环,所有加left<right 的条件。 right -= 1 li[left] = li[right] # 右边找到小于基值tmp,就把这个数放到左边:也就是li[left] = li[right] while left < right and li[left] <= tmp: # 【左循环一次】 left += 1 li[right] = li[left] li[left] = tmp # 将最left的数写回中间位置。 return left def _quick_sort(li, left, right): ''' 递归查询 :param li: :param left: :param right: :return: ''' if left < right: # 至少两个元素 递归 mid = partition(li, left, right) _quick_sort(li, left, mid) # 左到中间递归循环 _quick_sort(li, mid + 1, right) # 中间到右边循环 @cal_time def quick_sort(li): _quick_sort(li,0,len(li)-1) li = list(range(10000, -1, -1)) random.shuffle(li) quick_sort(li)
javascript的快排
<script>
function quickSort(arr, start, end) {
if (start > end) {
return;
}
let i = start,
j = end,
pivot = arr[start]; //存放基准数
while (i !== j) {
// 从右边开始,找第一个小于基准的位置
while (arr[j] >= pivot && i < j) {
j--;
}
// 从左边开始,找第一个大于基准的位置
while (arr[i] <= pivot && i < j) {
i++
}
// 交换两个数
if (i < j) {
let tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
// 最后把基准数归位
arr[start] = arr[i];
arr[i] = pivot;
// 递归处理左边
quickSort(arr, start, i - 1);
// 递归处理右边
quickSort(arr, i + 1, end);
}
var arr = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8];
quickSort(arr, 0, arr.length - 1);
console.log(arr);
</script>
一行实现快速排序
li=[2,3,4,1] def qc(li): return qc([i for i in li[1:] if i <=li[0]]) + li[0:1] +qc([i for i in li[1:] if i>li[0]]) if len(li)>1 else li res = qc(li)
时间复杂度是O(nlogn)

三、数据结构

第五章 堆排序
第一节:树,一种数据结构。
树是一种可以递归定义的数据结构。

树的根节点:A
叶子节点:没有孩子的节点
树的深度(高度):就是有几层。
树的度:分了几叉;节点的度,树的度就是节点中最大的度
孩子节点:
父节点:
子树:子节点单独拿出来,就是一个子树
二叉树:度不超过2的树(节点做多有两个叉)

两种特殊的二叉树
满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。
完全二叉树:叶子节点只能出现在最下层和次下层,并且最下层的节点都集中在改层最左边的若干位置的二叉树。
二叉树的存储方式
方式一:链式存储
方式二:顺序存储(列表) 二叉树使用的顺序存储。
完全二叉树可以用列表来存储,通过规律可以从父亲找到孩子或从孩子找到父亲。

孩子找父亲:
(左或右)孩子:i
父亲:(i-1)//2
----------------------------------------------
父节点和左孩子节点的编号下标关系:
父:i
左孩子:2i+1
父节点和右孩子节点的编号下标关系:
父:i
右孩子:2i+2
第二节:堆,一种特殊的完全二叉树
大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大
小根堆:一颗完全二叉树,满足任一节点都比其孩子节点小

堆的一个特性:向下调整性
当根节点的左右子树都是堆时,可以通过一次向下的调整来将其变换成一个堆

堆排序过程:
1.建立堆
2.得到堆顶元素,为最大元素
3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序。
4.堆顶元素为第二大元素。
5.重复步骤3,直到堆变空。

浙公网安备 33010602011771号