Loading

几种简单的排序算法(二)基本排序算法

各种排序整理

速查:

image


以下的排序用python来实现,比较方便


2、快排

image

这个图是真的棒!一定要给抄过来哈哈哈哈哈,特地放一下原地址


a)、时间复杂度(重要)

众所周知 快排的 平均时间复杂度最优时间复杂度 都是:O(nlogn),

最坏情况下(元素基本有序):O( n^2 )

b)、概念和实现

快排(quick sort)采用了分治的思想,个人觉得用递归来概括也是可以的。

思想:

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

具体方法:

  1. 在数列之中,选择一个元素作为”基准”(pivot),或者叫比较值。(一般会选用 左1 元素)
  2. 数列中所有元素都和这个基准值进行比较,如果比基准值小就移到基准值的左边,如果比基准值大就移到基准值的右边
  3. 以基准值左右两边的子列作为新数列,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

代码实现:

def quickSort(targetlist, left ,right):
    if left>right:
        return
    pivot = targetlist[left]   # pivot是基准
    i = left
    j = right
    while(i < j):
        # 如果right一直大于pivot,则继续走 直到找到比pivot小的
        while(targetlist[j] >= pivot and i<j):
            j -=1
        while(targetlist[i] <= pivot and i<j):
            i+=1
        # 此时right < pivot,left > pivot, 将i和j做交换
        if(i < j): # 这里做判断是为了right到了left位置时,不用再将执行下面这三行代码了
            temp = targetlist[i]
            targetlist[i] = targetlist[j]
            targetlist[j] = temp
    # left和right相遇了
    # 将相遇点的元素和pivot做交换
    targetlist[left] = targetlist[j]
    targetlist[j] = pivot
    print(targetlist)
    # 对于基准点两边的元素分别在做排序
    quickSort(targetlist, left,j-1)    # 递归
    quickSort(targetlist, j+1,right)


if __name__ == "__main__":
    target = [180,183,187,182,55,123,490,999,2]
    quickSort(target,0,len(target)-1)
    print(target)

或者借用lambda表达式 一行代码...也行,下面代码结果是升序的

quick_sort = lambda array: array if len(array) <= 1 else quick_sort([
    item for item in array[1:] if item <= array[0]
]) + [array[0]] + quick_sort([item for item in array[1:] if item > array[0]])

target = [180,183,187,182,55,123,490,999,2]
a = quick_sort(target)
print(a)

c)、总结(重要)

  1. 稳定性:快排是一种不稳定排序,比如基准值的前后都存在与基准值相同的元素,那么相同值就会被放在一边,这样就打乱了之前的相对顺序
  2. 比较性:因为排序时元素之间需要比较,所以是比较排序
  3. 时间复杂度:快排的时间复杂度为O(nlogn)
  4. 空间复杂度:排序时需要另外申请空间(改进算法不需要额外空间),并且随着数列规模增大而增大,其复杂度为:O(nlogn)
  5. 归并排序与快排 :归并排序与快排两种排序思想都是分而治之,但是它们分解和合并的策略不一样:归并是从中间直接将数列分成两个,而快排是比较后将小的放左边大的放右边,所以在合并的时候归并排序还是需要将两个数列重新再次排序,而快排则是直接合并不再需要排序,所以快排比归并排序更高效一些
  6. 快速排序有一个缺点就是对于小规模的数据集性能不是很好

3、冒泡排序

img

冒泡排序是一种比较容易理解的排序算法,它重复的访问比较要排序的元素列,一次比较两个相邻的元素,一层一层的将较大的元素往后移动,这种类似气泡在上升过程中慢慢变大效果的排序就是冒泡排序

a)、时间复杂度

一般来说,因为它需要双层循环n*(n-1)),所以平均时间复杂度为O(n^2)

改进后的算法这里不考虑。

b)、概念和实现

思想:

  1. 从第一个和第二个开始比较,如果第一个比第二个大,则交换位置,然后比较第二个和第三个,逐渐往后
  2. 经过第一轮后最大的元素已经排在最后,所以重复上述操作的话第二大的则会排在倒数第二的位置。
  3. 那重复上述操作n-1次即可完成排序,因为最后一次只有一个元素所以不需要比较

代码实现:

"""冒泡排序"""
def bubbleSort(targetlist):
    # 第一层for循环表示循环的遍数
    for i in range(len(targetlist) -1):
        # 第二层for表示具体比较哪两个元素
        for j in range(len(targetlist) -1 -i):
            if targetlist[j] > targetlist[j+1]:
                # 如果前大后小,则交换
                targetlist[j],targetlist[j+1] = targetlist[j+1],targetlist[j]
                

if __name__ == "__main__":
    target = [180,183,187,182,55,123,490,999,2]
    bubbleSort(target)
    print(target)

c)、总结

  • 这是一种偏慢的排序算法,仅适用于对于含有较少元素的数列进行排序
  • 稳定性:只有当前一个元素大于后一个元素时候才会调换顺序,所以它是稳定的
  • 比较性:排序时候元素之间需要比较,所以是比较排序
  • 空间复杂度:只需要常数个辅助单元,所以空间复杂度为O(1),我们把空间复杂度为O(1)的排序成为原地排序(in-place)

4、选择排序

img

没有找到更好的动图了好可惜


选择排序(selection sort)是一种简单直观的排序算法。每次从待排序的数据元素中选出最小(最大)的一个元素,存放在序列的起始位置,故而称为 选择排序

a)、时间复杂度

选择排序同样是双层循环n*(n-1)),所以时间复杂度也为:O(n^2)

b)、概念和实现

思想:

  1. 设第一个元素为比较元素,依次和后面的元素比较,比较完所有元素找到最小的元素,将它和第一个元素互换
  2. 重复上述操作,我们找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序

代码实现:

该实现方法和冒泡方法有点像,都是双层循环

"""选择排序"""
def selectionSsort(targetlist):
    # 第一层for表示循环选择的遍数
    for i in range(len(targetlist) - 1):
        # 将起始元素设为最小元素
        min_index = i
        # 第二层for表示最小元素和后面的元素逐个比较
        for j in range(i + 1, len(targetlist)):
            if targetlist[j] < targetlist[min_index]:
                # 如果当前元素比最小元素小,则把当前元素角标记为最小元素角标
                min_index = j
        # 查找一遍后将最小元素与起始元素互换
        targetlist[min_index], targetlist[i] = targetlist[i], targetlist[min_index]


if __name__ == "__main__":
    target = [180,183,187,182,55,123,490,999,2]
    selectionSsort(target)
    print(target)

c)、总结

  1. 稳定性:因为存在任意位置的两个元素交换,比如[5, 8, 5, 2],第一个5会和2交换位置,所以改变了两个5原来的相对顺序,所以为不稳定排序
  2. 比较性:因为排序时元素之间需要比较,所以是比较排序
  3. 空间复杂度:只需要常数个辅助单元,所以空间复杂度也为O(1)

5、插入排序

img

插入排序(Insertion-Sort)的算法描述也是一种简单直观的排序算法。主要时通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应的位置再插入。就像是在书架中插书:先找到相应位置,将后面的书往后推,再将书插入,如上图所示。

插入排序的适用场景:一个新元素需要插入到一组已经是有序的数组中,或者是一组基本有序的数组排序

a)、时间复杂度

插入排序同样需要两次循坏一个一个比较,故时间复杂度也为O(n^2)

b)、概念和实现

思想:

  1. 从第二个元素开始和前面的元素进行比较,如果前面的元素比当前元素大,则将前面元素 后移,当前元素依次往前,直到找到比它小或等于它的元素插入在其后面
  2. 然后选择第三个元素,重复上述操作,进行插入
  3. 依次选择到最后一个元素,插入后即完成所有排序

举例:

一次遍历排好一个元素

比较次数 列表 操作
原始序列 [180,183,187,182,55,123,490,999,2]
第一次比较后 [180,183,187,182,55,123,490,999,2]
第二次比较后 [180,183,187,182,55,123,490,999,2]
第三次比较后 [180,182,183,187,55,123,490,999,2] 182从后往前同黄色序列比较
第四次比较后 [55,180,182,183,187,123,490,999,2] 55
第五次比较后 [55,123,180,182,183,187,490,999,2] 123
第六次比较后 [55,123,180,182,183,187,490,999,2]
第七次比较后 [55,123,180,182,183,187,490,999,2]
第八次比较后 [2,55,123,180,182,183,187,490,999] 2

代码实现:

"""插入排序"""
def insertionSort(targetlist):
    # 第一层for表示循环插入的遍数
    for i in range(1, len(targetlist)):
        # 设置当前需要插入的元素
        current = targetlist[i]
        # 与当前元素比较的比较元素
        pre_index = i - 1
        # 将所有当前元素之前的且大于当前元素的元素向后移动
        while pre_index >= 0 and targetlist[pre_index] > current:
            # 比较元素大于当前元素则把比较元素后移
            targetlist[pre_index + 1] = targetlist[pre_index]
            # 向前选择下一个比较元素,继续比较
            pre_index -= 1
        # 经过上面的一系列挪动,当比较元素小于当前元素,则将当前元素插入在其后面
        targetlist[pre_index + 1] = current


if __name__ == "__main__":
    target = [180,183,187,182,55,123,490,999,2]
    insertionSort(target)
    print(target)

c)、总结

  • 需要区别于选择排序

  • 当一个新元素需要插入到一组已经是有序的数组中,或者是一组基本有序的数组排序(而快排则是基本无序的情况下使用的)

  • 其他特点

    • 比较性:排序时元素之间需要比较,所以为比较排序
    • 稳定性:从代码我们可以看出只有比较元素大于当前元素,比较元素才会往后移动,所以相同元素是不会改变相对顺序是稳定的
    • 空间复杂度:只需要常数个辅助单元,所以空间复杂度也为O(1)





还有一些算法后面在更新,今天就先过掉这些基本算法



posted @ 2020-06-12 21:04  你见过魔法嘛  阅读(269)  评论(0编辑  收藏  举报