冒泡排序:
两层循环,第一层冒泡次数,第二层比较次数
def bubblesort(L):
n = len(L)
for i in range(n):
for j in range(n-i-1):
if L[j] > L[j+1]:
L[j+1], L[j] = L[j], L[j+1]
#return L
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
bubblesort(array)
print("-------final -------")
print(array)
选择排序
关键在于两层循环,第一层是选择次数,第二层是找出最小值的比较次数
def choosesort(L):
n = len(L)
for i in range(n):
min_index = i
for j in range(i,n):
if L[min_index] > L[j]:
min_index = j
L[i],L[min_index] = L[min_index],L[i]
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
choosesort(array)
print("-------final -------")
print(array)
插入排序
第一层循环,需要插入的次数;第二层循环,需要插入数据的比较次数,一直比较到它的前一个数比它小或者它是第一个数结束;第二层循环是重点,要求索引不能超出,同时当发现前一个数小于它时要停止当前插入数字的循环(指的第二层)
def insertsort(L):
n = len(L)
for i in range(1,n):
for j in range(i,0,-1):
if L[j] > L[j-1]:
break
L[j-1],L[j] = L[j],L[j-1]
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
insertsort(array)
print("-------final -------")
print(array)
快速排序
法1:常规的算法是使用两个指针的移动来实现
对一串数字进行排序,在python中就是对列表中的数据排序
基本思路是:首先在列表中选取一个数据作为元,将比元小的数放在左边,比元大的数放在元的右边,递归的进行排序
其实思想是蛮简单的,就是通过第一遍的遍历(让left和right指针重合)来找到数组的切割点。
其实思想是蛮简单的,就是通过第一遍的遍历(让left和right指针重合)来找到数组的切割点。
第一步:首先我们从数组的left位置取出该数(20)作为基准(base)参照物。
第二步:从数组的right位置向前找,一直找到比(base)小的数,
如果找到,将此数赋给left位置(也就是将10赋给20),
此时数组为:10,40,50,10,60,
left和right指针分别为前后的10。
第三步:从数组的left位置向后找,一直找到比(base)大的数,
如果找到,将此数赋给right的位置(也就是40赋给10),
此时数组为:10,40,50,40,60,
left和right指针分别为前后的40。
第四步:重复“第二,第三“步骤,直到left和right指针重合,
最后将(base)插入到40的位置,
此时数组值为: 10,20,50,40,60,至此完成一次排序。
第五步:此时20已经潜入到数组的内部,20的左侧一组数都比20小,20的右侧作为一组数都比20大,
以20为切入点对左右两边数按照"第一,第二,第三,第四"步骤进行,最终快排大功告成。
快速排序是原址排序,基本思想是:选择一个标准元,将比它小的放在它的左边,比它大的放在它的右边;递归的进行这个操作就排序好了。难点在于如何将将比它小的放在它的左边,比它大的放在它的右边
快速排序是空间复杂度为O(1)的原址排序,原址排序:要注意开始就要出地址序列,从而方便将元素填入对应的地址;
快速排序是通过选择一个标准元,将比它小的放在左边,比它大的放在右边,递归进行操作.算法有两个关键点:1、通过双指针来指向小于标准元和大于标准元的元素;2、终止条件,大小指针对应的索引:low >= high;
#解决难点有两个思路:
#1、算法导论上的方法:找出标准元在有序情况下的位置,然后将其左边分为一个序列,都比标准元小;右边分为一个子序列,都比标准元大;
#通过两个指针来实现对选定标准元的二分
#一般情况下是直接选择最右边的作为标准元
def partition(L,left,right):
#设定标准元
key = L[right]
i = left - 1
for j in range(left,right):
if L[j] < key:
i +=1
L[i],L[j] = L[j],L[i]
L[i+1],L[right] = L[right],L[i+1]
return i+1
def quicksort(L,left,right):
if left < right:
q = partition(L,left,right)
quicksort(L,left,q-1)
quicksort(L,q+1,right)
#2、常规的思路,通过循环来找出两个子序列,直接在递归中实现序列的二分
def quicksort(L,left,right):
if left >= right
return #递归终止条件子序列不存在或者大小为1
#应用两个指针来找出子序列
low = left
high = rigt
key = L[low]
while low < high:
while low < high and key < L[high]:
high -=1
L[low] = L[high]
while low < high and key >=L[low]
low +=1
L[high] = L[low]
L[low] = key
quicksort(L,left,low-1)
quicksort(L,low+1,right)
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
quicksort(array,0,len(array)-1)
print("-------final -------")
print(array)
归并排序
归并排序的关键思想是:分治。分指:将序列二分为连个序列;治指:将拍好序的序列合并层一个序列; 其空间复杂度为O(n);
其基本思想是:将序列递归的分为两个子序列,直到子序列的大小不大于1,然后合并两个排好序的子序列。
def merge(L1,L2):
m = len(L1)
n = len(L2)
i = 0
j= 0
temp = []
while i < m and j < n:
if L1[i] < L2[j]:
temp.append(L1[i])
i +=1
else:
temp.append(L2[j])
j +=1
while( i < m):
temp.append(L1[i])
i +=1
while(j < n):
temp.append(L2[j])
j +=1
return temp
def mergesort(L):
if len(L) <2:
return L
middle = len(L)//2
L1 = mergesort(L[:middle])
L2 = mergesort(L[middle:])
temp = merge(L1,L2)
return temp
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
array=mergesort(array)
print("-------final -------")
print(array)
#
以上的方法空间复杂度比较高,可以实现O(n)的空间复杂度;
通过传入一个序列大小的temp来实现:
def merge(L,left,middle,right,temp):
i = left
j= middle + 1
k = 0 # temp 的索引
while i <= middle and j <= right:
if L[i] < L[j]:
temp[k] = L[i]
k +=1
i +=1
else:
temp[k]=L[j]
k +=1
j +=1
while(i<=middle):
temp[k] = L[i]
k +=1
i +=1
while(j<=right):
temp[k]=L[j]
k +=1
j +=1
#将temp传回元数组array
t = 0
while(left<=right):
L[left] = temp[t]
left +=1
t +=1
def mergesort(L,left,right,temp):
if left < right:
middle = (left + right)//2
mergesort(L,left,middle,temp)
mergesort(L,middle+1,right,temp)
merge(L,left,middle,right,temp)
#测试
if __name__ == '__main__':
array = [96,14,10,9,6,99,16,5,1,3,2,4,1,13,26,18,2,45,34,23,1,7,3,22,19,2]
#array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12]
print("before sort:", array)
temp = [0]*len(array)
mergesort(array,0,len(array)-1,temp)
print("-------final -------")
print(array)
##备注:为了减少空间存储,需要传入一个temp空间,每次需要的额外空间就调用这个temp,
而不用再次分配空间,同时为了在递归时及时的释放空间,需要将处理好的数据,传回原数组。
同时要在对应的原数组的索引范围之内,因此在定义函数时需要传入原数组的索引范围。
必须要传入索引范围,才能做原址排序或者O(n)空间复杂度的排序
堆排序
完全二叉树
完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,
有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点
一一对应时称之为完全二叉树。
完全二叉树 是 一种除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐的树,
即优先填充左子节点。
最大堆最小堆
堆分为最大堆和最小堆,其实就是完全二叉树。最大堆要求节点的元素都要不小于其孩子,
最小堆要求节点元素都不大于其左右孩子,两者对左右孩子的大小关系不做任何要求,其实很好理解。
最大堆:每个结点的值都大于或等于左右孩子结点
最小堆:每个结点的值都小于或等于左右孩子结点
基本思想
首先构建完全二叉树
然后构建最大堆
交换
维护最大堆