当前位置:   article > 正文

[leetcode]排序算法(冒泡排序,选择排序,插入排序,快速排序,计数排序)_leetcode冒泡排序

leetcode冒泡排序

目录 

 

1.冒泡排序

原理

 代码(python&cpp)

拓展:timeit()用法

2.选择排序

原理

3.插入排序

原理

代码(python&cpp)

4.归并排序

原理

代码

 5.快速排序

原理

代码(python&cpp)

6.计数排序

原理

 代码(python&cpp)

总结

参考


1.冒泡排序

冒泡排序(Bubble Sort)是一种很原始的排序方法,就是通过不断地交换“大数”的位置达到排序的目的。因为不断出现“大数”类似于水泡不断出现,因此被形象地称为冒泡算法。

原理

冒泡算法的基本原理就是比较相邻两个数字的大小。将两数中比较大的那个数交换到靠后的位置。不断地交换下去就可以将最大的那个数放到队列的尾部。然后重头再次交换,直到将数列排成有序数列。

一个 n 个数的数列需要排序 n -1轮。这样可以确保得到一个有序的数列。因此冒泡排序的时间复杂度是 o(n^{2})

 代码(python&cpp)

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:202112112
  4. # author:yang
  5. import random
  6. from randomList import randomList
  7. import sort
  8. def bubbleSort(iList):
  9. iLen = len(iList)
  10. for i in range(iLen):
  11. for j in range(0,iLen-i-1):
  12. if iList[j] > iList[j+1]:
  13. iList[j],iList[j+1] = iList[j+1],iList[j]
  14. return iList
  15. if __name__ == "__main__":
  16. iList = randomList(10)
  17. print("iList1:",iList)
  18. bubbleSort(iList)
  19. print("iList2:",iList)
  1. void bubbleSort() {
  2. int iList[] = { 5,4,7,8,2,9,10,4,0,3};
  3. int len = sizeof(iList) / sizeof(int);
  4. for (int i = 0; i < len; i++)
  5. {
  6. for (int j = 0; j < len - i - 1; j++) {
  7. if (iList[j] > iList[j+1]) {
  8. int temp = iList[j+1];
  9. iList[j+1] = iList[j];
  10. iList[j] = temp;
  11. }
  12. }
  13. }
  14. for (int i = 0; i < len; i++) {
  15. cout << iList[i] << " ";
  16. }
  17. cout << endl;
  18. }

拓展:timeit()用法

python中的timeit()方法, 它用于获取代码的执行时间。该库将代码语句默认运行一百万次,并提供从集合中花费的最短时间,可以用来检查代码的性能。

语法:

timeit.timeit(stmt, setup,timer, number)

参数:

stmt:这将采用您要测量其执行时间的代码。默认值为“pass”。

setup:这将包含需要在stmt之前执行的设置详细信息。这个参数可以将stmt的环境传进去。比如各种import和参数什么的。默认值为“ pass”。

timer:它将具有计时器值,timeit()已经设置了默认值,我们可以忽略它。

number:stmt将按照此处给出的编号执行。默认值为1000000。

注:

要使用timeit(),我们需要导入模块,如下:

import timeit

另外需要补充一点是,如果你想直接 stmt 那里执行函数。可以把函数申明在当前文件中,然后在 stmt = ‘func()’ 执行函数。然后使用 setup = ‘from __main__ import func’ 即可,如果要import 多个需要使用 setup = from __main__ import func; import simplejson'

2.选择排序

原理

简单地说,选择排序只做了一件事,就是从数列中选择最大(最小)的那个数,将这个数放到合适的位置。然后在抛开这个数的子数列中找最大(最小)的那个数放到合适的位置。然后……一直到子数列为空为止。与冒泡排序稍有不同的是,它不是相邻的两个数比较,而是某个数和数列中其他所有的数比较。

第一轮排序:以数列中第1个数为基数,遍历数列中其他的数字,找到最小的那个数,然后交换这两个数的位置。 本轮排序的结果将数列中最小的那个数放到了数列的第一位。

 第二轮排序:然后以数列中的第2个数为基数,遍历数列中剩余的其他数字,找到最小的那个数,交换这两个数的位置。第二个数字3已经是剩余数列中最小的数了。因此本轮无须交换数字,

 第N轮排序:按照这个规律,不断地找剩余数列中最小的数字,交换位置,直到将原始数列排成有序数列。

选择排序数列完毕。共6个数,排序了5轮。理论上来说,选择排序的时间复杂度是 O ( n^2 )。但在Python中稍微有点特殊,因为Python 列 表 中 寻 找 最 小 的 那 个 数 不 需 要 逐 个 比 较 , 使 用min(iList[i:])就可以得到最小的那个数,所以使用Pythonic风格版本的选择排序,时间复杂度是 O ( n )。因此,理论上在Python版本的排序算法中选择排序算法是最快的。

 代码(python&cpp)

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:20211210
  4. # author:yang
  5. from randomList import randomList
  6. import timeit
  7. def selectionSort(iList):
  8. if len(iList) <= 1:
  9. return iList
  10. for i in range(0,len(iList)-1):
  11. if iList[i] != min(iList[i:]):
  12. minIndex = iList.index(min(iList[i:])) # 最小数的下标
  13. iList[i],iList[minIndex] = iList[minIndex],iList[i]
  14. print("第%d轮排序结果:"%(i+1),end=" ")
  15. print("ilist:",iList)
  16. return iList
  17. if __name__ == "__main__":
  18. iList = randomList(10)
  19. print(iList)
  20. print(selectionSort(iList))
  21. # print(timeit.timeit("selectionSort(iList)","from __main__ import selectionSort,iList",number = 1))

cpp待补充 

3.插入排序

插入排序(Insertion Sort)方法与打扑克取牌的排序很相似。在打扑克时,每取一张新牌,都会与手上已有的牌进行比较,将新牌插入到比自己小的牌后面。在取完所有的牌后,手上已有的牌就是个有序的序列。

原理

插入排序首先将数列分成两部分。数列的第一个数为left部分,其他的数为right部分。然后将right部分中的数逐一取出,插入left部分中合适的位置。当right部分为空时,left部分就成为了一个有序
数列。

举例:

 第一轮:

首先要做的是将数列分成左、右两部分,left部分是第一个数字,right部分是数列剩余的部分。首先在牌堆中取出第一张牌,牌面是7,

 第二轮排序

然后从牌堆中取出第二张牌,牌面是3。将牌面是3的牌放入手中合适的位置。将right部分的第一个数字3插入left部分合适的位置。3比7要小,插入到7的前面。

第N轮排序 

循环将right部分的数字插入left部分,插入排序的时间复杂度是 O ( n ^2 )。

代码(python&cpp)

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:20211210
  4. # author:yang
  5. from randomList import randomList
  6. import timeit
  7. def insertSort(iList):
  8. if len(iList) <= 1:
  9. return iList
  10. for right in range(1,len(iList)):
  11. target = iList[right]
  12. for left in range(0,right):
  13. if target <= iList[left]:
  14. # 使用python切片赋值
  15. iList[left+1:right+1] = iList[left:right]
  16. iList[left] = target
  17. break
  18. return iList
  19. if __name__ == "__main__":
  20. iList = randomList(10)
  21. print(iList)
  22. print(insertSort(iList))
  23. # print(timeit.timeit("selectionSort(iList)","from __main__ import selectionSort,iList",number = 1))

cpp待补充 

4.归并排序

归并排序(Merge Sort)是一种典型的递归法排序。它把复杂的排序过程分解成一个简单的合并子序列的过程。至于怎么得到这个子序列,就得自己调用自己了。

原理

归并排序首先要做的就是将数列分成左右两部分(最好是等分),然后将左、右两个子数列排序完毕后再合并到一起就成了一个有序数列。左、右两个子数列怎么变成有序数列呢?那就回头调用自
己,再把子数列分成左、右两部分,直到将所有的子数列的长度分为1为止。然后把子子数列排序完毕后合并成子数列……有点像那个著名的故事,山上有座山,山里有座庙,庙里有两个和尚……。和尚讲故事是无穷无尽的,幸运的是数列的长度即使再大也不会是无尽的。所以当子子子……序列分到不可再分割的时候(最小的序列长度为1时),就可以返回开始合并数列了。逐步合并子子子子……数列,到最后就得到了一个新的有序数列。

第一次分组

第二次分组

经过三次分组,已经将所有的子子子数列都变成了长度为1的数列。现在可以确定这些长度为1的数列必定是有序数列了,然后开始反向合并这些数列。

第一次合并

这里还需要考虑left和right长度不一致的情况。先合并数列[3]、[5]以及[9]、[4]。

第二次合并 

 第三次合并

归并排序完毕。经过3次合并,最终得到了一个有序数列。归并排序的时间复杂度是 O ( n Log n) )。

代码

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:20211210
  4. # author:yang
  5. from randomList import randomList
  6. import timeit
  7. def mergeSort(iList):
  8. if len(iList) <= 1:
  9. return iList
  10. middle = len(iList)//2
  11. left,right = iList[0:middle],iList[middle:]
  12. return mergeList(mergeSort(left),mergeSort(right))
  13. def mergeList(left,right):
  14. """合并序列"""
  15. mList = []
  16. while left and right:
  17. if left[0] >= right[0]:
  18. mList.append(right.pop(0))
  19. else:
  20. mList.append(left.pop(0))
  21. while left:
  22. mList.append(left.pop(0))
  23. while right:
  24. mList.append(right.pop(0))
  25. return mList
  26. if __name__ == "__main__":
  27. iList = randomList(10)
  28. print(iList)
  29. print(mergeSort(iList))
  30. # print(timeit.timeit("selectionSort(iList)","from __main__ import selectionSort,iList",number = 1))

 5.快速排序

快速排序(Quick Sort)算法也是一种递归排序。

原理

以列表中的任意一个数为基准(一般选择列表中的第一个数),将列表分为左、右(前、后)两个子列表:左边子列表的数要比基准数小,右边子列表的数要比基准数大。然后继续把左边子列表和右边子列表按同样的方法继续分解、比较,一直到分无可分为止。然后按照左边子列表(比基准数小)+基准数+右边子列表(比基准数大)的方式连接起来。最后得到一个有序的数列。

以数列[7,3,5,1,9,4]为例。

第一次分组

以“7”为基准,比7小的分在左边的子数列中,比7大的分在右边的子数列中。

 此时左边的子序列有4个数[3, 5, 1, 4],还需要继续分组。右边的子序列中只有一个数9,不需要再分了。第二次只需要将左边的部分分组排序就可以了。

第二次分组

左边的子序列还剩下[3, 5, 1, 4],现在以第一个数3为基准数,将后面部分的[5, 1, 4]分成两部分。同样还是比基准数(3)小的放到左边子序列,比基准数(3)大的放到右边子序列。

 数分组完毕后。只要按照一定的顺序重新组合起来就可以了。组合很简单,就是小的数(左边部分)+中间数(基准数)+大的数(右边部分)。

第一次合并

将子序列合并,具体规则是left + [base] + right。这里需要稍微注意一下,left和right都是序列(列表),而base部分是单独的一个数,所以需要将它序列化。

 第二次合并

 第三次合并

 经过3次分组、合并后,得到了有序数列。快速排序的时间复杂度最坏的情况下是 O ( n ^2 )。

代码(python&cpp)

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:20211210
  4. # author:yang
  5. from randomList import randomList
  6. import timeit
  7. def quickSort(iList):
  8. if len(iList) <= 1:
  9. return iList
  10. left = []
  11. right = []
  12. for i in iList[1:]:
  13. if i <= iList[0]:
  14. left.append(i)
  15. else:
  16. right.append(i)
  17. return quickSort(left) + [iList[0]] + quickSort(right)
  18. if __name__ == "__main__":
  19. iList = randomList(10)
  20. print(iList)
  21. print(quickSort(iList))
  22. # print(timeit.timeit("selectionSort(iList)","from __main__ import selectionSort,iList",number = 1))

注:此方法速度快。

6.计数排序

计数排序(Counting Sort)算法是一种比较特殊的算法,因为它不是一个基于比较的算法。将两个数进行比较,将大的数放后面、小的数放前面,这样的算法都是基于比较的算法。

原理

它采用了一个巧妙的方法,选择一个数为基数,然后统计整个数列中有多少个数比基数小。如果有 n 个数比基数小,那么基数就放到新数列的第n +1的位置上(rList[n])。以数列[7, 3, 5, 1, 9, 4]为例,首先要做的是先创建一个与[7, 3, 5, 1, 9, 4]相同长度的空数列。

 第一个数的位置

第二个数的位置

第三个数的位置

依次类推

将所有的数字都插入到新数列后,排序就完成了。计数排序的时间复杂度是 O ( n + k ),其中 k 是整数的范围。

 代码(python&cpp)

  1. #!/user/bin/env/python3
  2. #-*-conding:utf-8 -*-
  3. # data:20211210
  4. # author:yang
  5. from randomList import randomList
  6. import timeit
  7. def countingSort(iList):
  8. if len(iList) <= 1:
  9. return iList
  10. iLen = len(iList)
  11. rList = [None]*iLen
  12. for i in range(iLen):
  13. small = 0 # 比基数小的
  14. same = 0 # 与基数相等的
  15. for j in range(iLen):
  16. if iList[j] < iList[i]:
  17. small += 1
  18. elif iList[j] == iList[i]: # 相同的数
  19. same += 1
  20. for k in range(small,small+same):
  21. rList[k] = iList[i]
  22. return rList
  23. if __name__ == "__main__":
  24. iList = randomList(10)
  25. print(iList)
  26. print(countingSort(iList))
  27. # print(timeit.timeit("selectionSort(iList)","from __main__ import selectionSort,iList",number = 1))

总结

 从表格上来看,排序100次用时最少,速度最快的应该是快速排序。果然是名副其实。选择排序的速度与快速排序相差无几。用时最多、速度最慢的是插入排序,其次就是冒泡排序。当然,根据输入数列的不同,这个排名可能会有所变动。

参考

图解leetcode初级算法(python版)--胡松涛

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/693492
推荐阅读
相关标签
  

闽ICP备14008679号