1.快速排序
2.堆排序
3.归并排序
快速排序的思路:取一个元素p(第一个元素),使元素p归位,列表被p分为两部分,左边都比p小,右边都比p大,递归完成排序。
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
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(li, left, right):
tmp = li[left]
while left<right:
while li[right] >= tmp and left<right: # 从右边找比tmp小的数
right -= 1 # 往左走一步
li[left]=li[right] # 把右边的值写到左边空位上
while li[left] <= tmp and left < right:
left +=1
li[right]=li[left] # 把左边的值写到右边空位上
print(li)
li[left] = tmp # 把tmp归位
return left
def quick_sort(data,left,right):
if left < right: #至少2个元素
mid = partition(data,left,right)
quick_sort(data,left,mid-1)
quick_sort(data,mid+1,right)
li = [5,7,4,6,3,1,2,9,8]
quick_sort(li,0,len(li)-1)
print(li)
时间复杂度:O(nlogn) 每次partition的是n,总共logn层,所以时间复杂度为O(nlogn),你可以去用它和之前的lowb三人组做时间比较,效果就很明显了,同时也注意用cal_time时不要套在递归上,记得取个别名比较。不过快速排序中有递归的问题,也要修改递归深度,也会消耗一定的系统资源,而快速排序有最坏的情况,也是我之前说过排序都有一般时间复杂度,还有最坏情况。比如当li=[9,8,7,6,5,4,3,2,1],当li为倒序有序的列表时,它每次只少一个数,所以最坏情况为O(n2),不过是极为少数,因为你也可以解决最坏情况,比如打乱列表,shuffle,或者在随机化取一个数为mid。
树是一种数据结构 比如:目录结构(类如linux)
树是一种可以递归定义的数据结构
树由n个节点组成
如果n=0,那这是一棵空树;
如果n>0,那存在1个节点作为树的根节点,其
他节点可以分为m个集合,每个集合本身又是
棵树
树的根节点:A 最上面开始
叶子节点:B,C,H,J,P,Q,L,M,N 最后没有分叉的
树的深度(高度):4 就是说最深有几层,显然4层
树的度:6 就是看最多分了几叉
孩子节点/父节点 :j是父节点,p和q是它的孩子节点,很明显。
二叉树:度不超过2的树
每个节点最多有2个孩子节点
两个孩子节点被区分为左孩子节点和右孩子节点
满二叉树:一个二叉树,如果每一个层
的结点数都达到最大值,则这个二叉树
就是满二叉树。
完全二叉树:叶节点只能出现在最下层
和次下层,并且最下面一层的结点都集
中在该层最左边的若干位置的二叉树。
1.链式存储方式(后面在做讲解)
2.顺序存储方式 就是用列表来存
1.父节点和左孩子节点的编号下标有什么关系?
0-1 1-3 2-5 3-7 4-9
i→2i+1
2.父节点和右孩子节点的编号下标有什么关系?
0-2 1-4 2-6 3-8 4-10
i→2i+2
3.通过孩子节点怎么找到父节点?
i 一> (i-1)//2
堆:一种特殊的完全二叉树结构
大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
小根堆:一棵完全二叉树,满足任一节点都比其孩子节点少
假设根节点的左右子树都是堆,但根节点不满足堆的性质
可以通过一次向下的调整来将其变成一个堆。
1.建立堆。
2.得到堆顶元素,为最大元素
3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过-次调整重新使堆有序。
4.堆顶元素为第二大元素。
5.重复步骤3,直到堆变空。
def sift(li, low, high):
# li 列表 low 堆的根节点 high最后一个元素的位置
i = low # i最开始指向根节点
j = 2*i+1 # j开始为左孩子
tmp = li[low] # 把堆顶存起来
while j<=high: # 只要j位置有数
if j+1 <= high and li[j+1] > li[j]: # 如果右孩子有并且比较大
j = j+1 # j指向右孩子
if li[j] > tmp:
li[i] = li[j]
i = j
j = 2*i+1
else: # tmp更大,把tmp放到i的位置上
li[i]=tmp # 把tmp放到某一级领导位置上
break
else:
li[i]=tmp # 把tmp放在叶子节点
def heap_sort(li):
n = len(li)
for i in range((n-2)//2, -1,-1):
# i表示建堆的时候调整的下标
sift(li,i,n-1)
print(li)
# 堆排序完成
# 挨个出数
for i in range(n-1,-1,-1):
# i 指向当前堆的最后一个元素
li[0],li[i] = li[i], li[0]
sift(li,0,i-1) # i-1是新的high
li = [i for i in range(100)]
import random
random.shuffle(li)
print(li)
heap_sort(li)
sift函数时间复杂度为O(logn),出现折半过程
堆排序的时间复杂度:O(nlogn)
import heapq # q -->> queue表示优先队列 实现的是小根堆 先进先出是队列 出现小的先出或者大的先出为优先队列
import random
li = list(range(100))
random.shuffle(li)
print(li)
heapq.heapify(li) # 建堆
n=len(li)
for i in range(n):
print(heapq.heappop(li),end=',')
现在有n个数,设计算法得到前k大的数。(k 解决思路: 假设现在的列表分两段有序,如何将其合成为一个有序列表 分解:将列表越分越小,直至分成一个元素。 时间复杂度:O(nlogn) 三种排序算法的时间复杂度都是O(nlogn)
1.排序后切片 O(nlogn)
2.排序lowb三人组 O(kn)
3.堆排序思路 O(nlogk)
取列表前k个元素建立一个小根堆。堆顶就是目前第k大的数。依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元
素,如果大干堆顶,则将堆顶更换为该元素、并目对堆进行一- 次调整;遍历列表所有元素后,倒序弹出堆顶。topk实现
def sift(li, low, high):
i = low
j = i * 2 +1
tmp = li[low]
while j <= high:
if j + 1 <= high and li[j+1] < li[tmp]: # 小根堆
j = j+1
if li[j] < tmp:
li[i]=li[j]
i=j
j=2*i+1
else:
break
li[i]=tmp
def topk(li,k):
heap = li[0:k]
for i in range((k-2))//2,-1,-1):
sift(heap, i, k-1)
# 1.建堆
for i in range(k,len(li)-1):
if li[i] > heap[0]:
heap[0] = li[i]
sift(heap,0,k-1)
#2.遍历
for i in range(k-1,-1,-1):
heap[0],heap[i] = heap[i], heap[0]
sift(heap,0,i-1)
# 出数
return heap
import random
li = list(range(1000))
random.shuffle(li)
print(topk(li,10)
三:归并排序
归并排序–归并
这种操作就叫做归并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执行完,肯定有一部分没数了
while i<=mid:
ltmp.append(li[i])
i+=1
while j<=high:
ltmp.append(li[j])
j +=1
li[low:high+1]=ltmp
li = [2,4,5,7,1,3,6,8]
merge(li,0,3,7)
print(li)
归并排序–使用归并
终止条件:一个元素是有序的。
合并:将两个有序列表归并,列表越来越大。
def merge_sort(li,low,high):
if low<high: #至少有2个元素,递归
mid = (low+high)//2
merge_sort(li,low,mid)
merge_sort(li,mid+1,high)
merge(li,low,mid,high)
li=list(range(1000))
import random
random.shuffle(li)
print(li)
merge_sort(li,0,len(li)-1)
print(li)
空间复杂度:O(n) 不属于原地排序,归并必须需要一定的空间 python中sort方法内部实现基于归并排序和插入排序的结合NB三人组总结:
一般情况下,就运行时间而言:
快速排序<归并排序<堆排序