排序算法常用术语
稳定与不稳定
稳定: 如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定: 如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序与外排序
内排序: 所有排序操作都在内存中完成;
外排序: 由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
排序算法的性能三大影响因素
1 . 时间性能(时间复杂度): 一个算法执行所耗费的时间。
2. 辅助空间 (空间复杂度): 运行完一个程序所需内存的大小。
3. 算法的复杂性 : 算法本身的复杂度,而不是指算法的时间复杂度
常见排序算法性能比较
排序算法
交换排序:冒泡排序
冒泡排序 (Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
冒泡排序复杂度分析
最好的情况, 也就是要排序的表本身就是有序的,那么我们比较次数,可以推断出就是n‐1 次的比较,没有数据交换,时间复杂度为O(n)。
最坏的情况,即待排序表是逆序的情况,此时需要比较1+2+3+4+…+(n-1)=n(n-1)/2次,即时间复杂度为O(n**2)。
稳定性: 稳定
def BubbleSort(nums):
"""实现冒泡排序"""
length = len(nums)
for i in range(length - 1):
for index in range( length - i - 1):
if nums[index] > nums[index + 1]:
nums[index], nums[index + 1] = nums[index + 1], nums[index]
return nums
if __name__ == '__main__':
nums = [3,4,5,6,1,24,9]
print(BubbleSort(nums))
交换排序:快速排序
快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
层数为O(logn)(即调用栈的高度为O(logn)),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(logn) = O(nlogn)
"""
基本思想:通过一趟排序将待排序记录分隔成两部分,其中一部分记录的关键字大于另一部分记录的关键字,
则可对这两部分记录继续进行排序,以答到整个序列有序的目的
"""
def quickSort(array):
if len(array) < 2:
return array
else:
# key = array[0]
# bigarray = []
# smallarray = []
# for i in array[1:]:
# if i > key:
# bigarray.append(i)
# if i < key:
# smallarray.append(i)
# return quickSort(smallarray) + [key] + quickSort(bigarray)
pivot = array[0]
less = [i for i in array[1:] if i < pivot]
greater = [i for i in array[1:] if i > pivot]
return quickSort(less) + [pivot] + quickSort(greater)
if __name__ == '__main__':
print(quickSort([10, 5, 2, 3, 11, 55, 1]))
插入排序: 直接插入排序
直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。
原理: 每次将排序中的元素,插入到前面已经排好序的有序序列中去,直到排序完成。
def insert_num(nums):
"""直接插入排序"""
# 待排序的序列长度
count = len(nums)
# 将第一个索引认为是有序的数值序列,依次遍历除了第一个元素之外的其他元素,插入到前面的有序序列
for i in range(1, count):
# 要插入的数值元素
key = nums[i]
# 插入元素的前一个元素的索引
j = i - 1
# 进行大小比较的时候是从有序序列的最后一个元素(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, 3, 6, 9, 10, 8, 4]
sort_nums = insert_num(nums)
print(sort_nums)
最好的情况, 也就是要排序的表本身就是有序的, 因此没有移动的记录,时间复杂度为 O(n)。
最坏的情况, 即待排序表是逆序的情况,时间复杂度为 O(n**2)。
插入排序:希尔排序
基本思想:
算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的然后
再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行
直接插入排序后,排序完成。
def insert_num(nums):
for i in range(1, len(nums)):
# j为有序列表的最后一个元素下标
j = i - 1
key = nums[i]
while j >= 0:
if nums[j] > key:
nums[j + 1] = nums[j]
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:
if arr[index] > arr[index + step]:
arr[index], arr[index + step] = arr[index + step], arr[index]
step = int(step / 2)
return insert_num(arr)
if __name__ == '__main__':
arr = [49, 38, 65, 97, 76, 13, 27, 49, 55, 4]
arr = shellSort(arr)
print(arr)
选择排序
选择排序(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 Selection_Sort(arr):
"""选择排序"""
"""
首先在未排序的序列中找出最小(大)元素,存放在排序序列的起始位置,然后,
再从剩余未排序的元素中继续寻找最小(大)元素,然后放在已经排序的序列的末尾,
依次类推,直到所有的元素均排序完毕
"""
newArr = []
for i in range(len(arr)):
# 返回最小元素的索引
smallest = findSmallest(arr)
# 取出arr中的最小元素,添加到newArr中,并将其从arr中删除
newArr.append(arr.pop(smallest))
return newArr
if __name__ == '__main__':
arr = [8, 4, 5, 12, 88, 10]
print(Selection_Sort(arr))
时间复杂度:
最优时间复杂度:O(n2 )
最坏时间复杂度:O(n2 )
稳定性:不稳定(考虑升序每次选择最大的情况)