一、排序算法
1、 假设含有n个记录的序列为{r1,r2,……,rn},其相应的关键字分别为{k1,k2,……,
kn},需确定 1,2,……,n的一种排列p1,p2,……,pn,使其相应的关键字满足kp1≤
kp2≤……≤kpn(非递减或非递增)关系,即使得序列成为一个按关键字有序的序列
{rp1,rp2,……,rpn},这样的操作就称为排序。
2、排序的稳定性
- 稳定:如果a原本在b前面,而a=b,排序后a和b的顺序不改变。
- 不稳定:如果a原本在b前面,而a=b,排序后a可能出现在b的后面。
3、排序的内外
- 内排序:所有排序操作都在内存中完成
- 外排序:由于数据过大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输进行。
4、排序算法的性能三大因素
- 时间性能(时间复杂度):一个算法执行所耗费的时间。
- 辅助空间(空间复杂度):运行一个程序所需内存的大小。
- 算法的复杂性:算法本身的法复杂度,而不是指算法的时间复杂度。
5、下面是每个排序的复杂度
二、交换排序:冒泡排序
1、冒泡排序:一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
2、冒泡排序算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素同样的工作,从开始第一对到结尾的最后一对。
- 针对所有元素重复以上的步骤,除了最后一个
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一个数字需要比较。
代码示例如下:
def BubbleSort(nums):
len_nums = len(nums)
for i in range(len_nums):
for j in range(len_nums-i-1):
if nums[j] > nums[j+1]:
nums[j + 1], nums[j] = nums[j], nums[j + 1]
return nums
if __name__ == '__main__':
nums = [5,2,2,8,3]
b = BubbleSort(nums)
print(b)
三、交换排序:快速排序
1、快速排序的基本思想是:通过一趟排序将待排记录分割成独立的;两部分,其中一部分记录的关键字比另一部分记录的关键字小,则可分别对者两部分记录继续进行排序,以达到整个有序的目的。
代码实现如下:
def Quicksort(arr):
if len(arr)< 2:
return arr
else:
pivot = arr[0]
list1 = [i for i in arr[1:] if i <= pivot]
list2 = [i for i in arr[1:] if i > pivot]
return Quicksort(list1) + [pivot] + Quicksort(list2)
print(Quicksort([5,2,2,8,3]))
四、插入排序:直接插入排序
1、直接插入排序的基本操作是将一个记录插入到已经排好的有序的列表中,从而得到一个新的、记录数增1的有序表。
2、原理:每次将排序中的元素,插入到前面已经排好的有序序列中去,直到完成。
步骤:
- a[0]为有序区,待排序区为a[1…n-1]。令i=1.
- 将a[1]与a[0]中元素比较,将小的元素放在第一个位置。
- 以此类推,直到待排序中全部元素插入完成为止。
代码实现如下:
def insert_sort(nums):
count = len(nums)
for i in range(1,count):
key = nums[i]
j = i - 1
while j >= 0:
if nums[j] > key:
nums[j+1] = nums[j]
nums[j] = key
j -= 1
return nums
if __name__ == '__main__':
nums = [5,2,2,3,1]
nums_sort = insert_sort(nums)
print(nums_sort)
五、插入排序:希尔排序
1、基本思想:
算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的然后
再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行
直接插入排序后,排序完成。
代码实现如下:
def insert_sort(nums):
count = len(nums)
for i in range(1,count):
key = nums[i]
j = i - 1
while j >= 0:
if nums[j] > key:
nums[j+1] = nums[j]# 2,2,4,5,1
nums[j] = key
j -= 1
return nums
def shellSort(arr):
step = int(len(arr)/2)
while step>0:
print('step=',step)
arr_len = len(arr)
for index in range(arr_len):
if index + step < arr_len:
current_val = arr[index]
if current_val > arr[index + index]:
arr[index],arr[index + step] = arr[index + step],arr[index]
step = int(step / 2)
else:
return insert_sort(arr)
arr = [12,34,54,2,3,5,44]
sorted_arr = shellSort(arr)
print(sorted_arr)
六、选择排序
1、选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
代码实现如下:
def findSmallest(arr):
smallest = arr[0]
smallest_index = 0
for i in range(1,len(arr)):
if arr[i] < smallest:
smallest = arr[i]
smallest_index = i
return smallest_index
def selectionsort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append(arr.pop(smallest))
return newArr
print(selectionsort([5,3,6,2,10]))
七、选择排序:堆排序
1、 堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,利用数组的特点快速
定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的
值都不大于其父节点的值。最大的值一定在堆顶。
八、归并排序
1、归并排序是创建在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。
分治法:
- 分割:递归地把当前序列平均分割成两半
- 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起。
九、基数排序
1、基数排序(radix sort)它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
代码实现如下:
def RadixSort(Lst):
d = len(str(max(Lst)))
for i in range(d):
BucketLst = [[] for k in range(10)]
for j in range(len(Lst)):
BucketLst[Lst[j]//(10**i)%10].append(Lst[j])
Lst = [number for B in BucketLst for number in B]
return Lst
if __name__ == '__main__':
Lst1 = [810,700,821,892,846,199]
print(RadixSort(Lst1))
十、八皇后问题
1、回溯法,又被称为“试探法”。解决问题时,每进行一步,都是抱着试试或者这么走下去肯定达不到目标,立刻做回退操作重新选择。这种走不通
就回退再走的方法就是回溯法。
2、回溯法从问题本身出发,寻找可能实现的所有情况。和穷举法的思想相近,不同在于回溯法在列举过程中如果发现当前情况很本不可能存在,就停止后续的工作,返回上一步新的尝试。
回溯和递归唯一的联系就是,回溯法可以用递归思想实现。
3、回溯法的求解过程实质上是先序遍历“状态树”的过程。树中每一个叶子结点,都有可能是问题的答案。
4、八皇后问题代码实现
def conflict(state, nextColumn):
"""
state: (7, 4, 6, 0, 2) 标记已经排好的每个皇后的位置,(0,7), (1, 4), (2,6),(3,0),(4,2)
nextColumn: 2 下一行八皇后准备要放置的列索引位置
利用回溯法判断是否可以在第6行的第3列放置皇后, 如果可以,返回True, 如果不可以,返回False;
"""
nextRow = rows = len(state) # 5
for row in range(rows): # row = 0 1 2 3 4
column = state[row]
if abs(column - nextColumn) in (0, nextRow - row):
"""
i=0:
state[0]=7, nextColumn=2
如果差值等于0,两个皇后在同一列, 则代表冲突, 返回True;
如果列的差值等与行的差, 两个皇后在对角线上, 则代表冲突, 返回True;
"""
return True
return False
# nums = 8 pos=()
def queens(num, state=()):
"""
采用生成器的方式来产生每一个皇后的位置,并用递归来实现下一个皇后的位置。
num: 皇后的数量
state: 标记已经排好的每个皇后的位置
"""
for pos in range(num): # 八皇后的数量N=0, 1, 2, 3, 4, 5, 6 , 7 你要在哪一列放置皇后
# 如果不冲突,则递归构造棋盘。
if not conflict(state, pos): # 回溯法的体现
# 如果棋盘状态state已经等于num-1,即到达倒数第二行,而这时最后一行皇后又没冲突,直接yield,打出其位置(pos, )
if len(state) == num - 1: # state=()
yield (pos,)
else: # (0, )
for result in queens(num, state + (pos,)):
"""
pos = 0
(0, ) (0, 2), (0, 2, 4), ()
1). pos=0,第一行放在第一列,这时不会冲突,但是不会进入if,因为还没到达倒数第二行, (0)
2). 进入else后,再调用queens(num, state+(pos,),这时进入第二行,再次递归展开则是queens(num,state+(pos, )+(pos, ) )
2). 进入else后,再调用queens(num, state+(pos,),这时进入第三行,再次递归展开则是queens(num,state+(pos, )+(pos, ) +(pos, ) )
3). 到达最后一行时返回(pos, ),再返回倒数第二行,再返回倒数第三行,最后到达最开始那层(pos, )+result, pos为第一行皇后所在列,
"""
yield (pos,) + result
def prettyprint(solution): # (6, 1, 5, 2, 0, 3, 7, 4)
"""
友好展示: 为了直观表现棋盘,用X表示每个皇后的位置
:param solution:
:return:
"""
def line(pos, length=len(solution)): # pos为6,即绘制在第7列 . . . . . . X .
return '. ' * (pos) + 'X ' + '. ' * (length - pos - 1)
for pos in solution:
print(line(pos))
if __name__ == '__main__':
solutions = queens(8)
for index, solution in enumerate(solutions):
print("第%d种解决方案:" % (index + 1), solution)
prettyprint(solution)
print('*' * 100)