热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构利用Python实现常用排序算法的基本思路和代码

一、冒泡排序基本思路:冒泡排序(BubbleSort),是一种计算机科学领域简单的排序算法。它重复地走访过要排序的数列&#

一、冒泡排序

基本思路:冒泡排序(Bubble Sort),是一种计算机科学领域简单的排序算法它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。这个算法的名字由来是因为越小的元素会慢慢“浮”到数列的顶端,越重的元素会慢慢的沉入到底端,故名“冒泡排序”。时间复杂度最快: O(n),最坏: O(n^2)。注意,缺点是每一次只能确定一个元素。

python语言实现代码如下:

# 冒泡排序
def bubble_sort(my_list):length = len(my_list)# 序列长度为length,需要执行length-1轮交换
for i in range(1, length):# 对于每一轮交换,都将序列当中的左右元素进行比较
# 每轮交换当中,由于序列最后的元素一定是最大的,因此每轮循环到序列未排序的位置即可
for i in range(0, length-i):if my_list[i] > my_list[i+1]:temp = my_list[i]my_list[i] = my_list[i+1]my_list[i+1] = tempreturn my_list
if __name__ == '__main__':my_list = [1, 4, 5, 7, 3, 9, 10, 24, 0]new_mylist = bubble_sort(my_list)print(new_mylist)

二、直插法排序

基本思路:直插排序法就是将一个新的数据直接插入到已经排好的数据当中,将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过, 时间复杂度为O(n^2)

python语言实现代码如下:

# 直接插入排序
def insert_sort(my_list):# 遍历数组中的所有元素,其中0号索引元素默认已排序,因此从1开始
for i in range(1, len(my_list)):# 将该元素与已排序好的前序数组依次比较,如果该元素小,则交换
# range(x-1,-1,-1):i-1倒序循环到0
for j in range(i-1, -1, -1):# 判断:如果符合条件则交换
if my_list[j] > my_list[j+1]:temp = my_list[j+1]my_list[j+1] = my_list[j]my_list[j] = tempreturn my_list
if __name__ == '__main__':my_list = [1, 2, 10, 3, 9, 4, 5, 7]new_list = insert_sort(my_list)print(new_list)

三、选择排序

基本思路:比较+交换。从将要排序的序列中,找到最小的元素;如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。

时间复杂度:O(n^2)

python语言实现代码如下:

# 选择排序
def select_sort(my_list):# 依次遍历序列中的每一个元素
for x in range(0, len(my_list)):# 将当前位置的元素定义为当前的最小值
mixnum = my_list[x]# 将该元素与剩下的元素依次比较寻找最小元素
for i in range(x+1, len(my_list)):if my_list[i] # 将比较后得到的真正的最小值赋值给当前位置
my_list[x] = mixnumreturn my_list
if __name__ == '__main__':my_list = [2, 4, 1, 6, 7, 3, 9, 8]new_list = select_sort(my_list)print(new_list)

四、堆排序

基本思路:

取出大顶堆的根节点,和末尾的节点进行交换,在满足的大顶堆的前提下,进行重复交换,剩下的哪个值就是最小值。时间复杂度:O(n^2)

堆的概念:堆,分为大顶堆和小顶堆,

大顶堆要求节点的元素都要大于其孩子,

小顶堆要求节点元素都小于其左右

利用堆排序,就是基于大顶堆或者小顶堆的一种排序方法。一下是通过大顶堆来实现。

python语言实现代码如下:

# -------------------------堆排序--------------------------------
# **********获取左右叶子节点**********
def Left(i):return 2*i + 1
def Right(i):return 2*i + 2
# ********** 调整大顶堆 **********
# L:待调整序列 length: 序列长度 i:需要调整的下标
def adjust_max_header(L, length, i):# 定义一个int值保存当前序列最大值的下标
largest = i# 执行循环操作:两个任务:1 寻找最大值的下标;2.最大值与父节点交换
while True:# 获得序列左右叶子节点的下标
left, right = Left(i), Right(i)# 当左叶子节点的下标小于序列长度 并且 左叶子节点的值大于父节点时,将左叶子节点的下标赋值给largest
if (left and (L[left] > L[i]):largest = leftprint('左叶子节点', largest)else:largest = i# 当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给largest
if (right and (L[right] > L[largest]):largest = rightprint('右叶子节点', largest)# 如果largest不等于i 说明当前的父节点不是最大值,需要交换值
if largest != i:temp = L[i]L[i] = L[largest]L[largest] = tempi = largestcontinue
else:break
return L# ********** 建立大顶堆 **********
def build_max_header(L):length = len(L)for x in range(int((length-1)/2), -1, -1):adjust_max_header(L, length, x)# ********** 堆排序 **********
def do_header_sort(L):# 先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点
build_max_header(L)# i:当前堆中序列的长度.初始化为序列的长度
i = len(L)# 执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3...)
# 2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度
while i > 0:temp = L[i-1]L[i-1] = L[0]L[0] = temp
# 堆中序列长度减1
i -= 1
# 调整大顶堆
adjust_max_header(L, i, 0)return L# 程序执行入口
if __name__ == '__main__':L = [2, 4, 3, 1, 7, 8, 9, 5, 10, 12, 11]print(do_header_sort(L))

五、快速排序

基本思路:从序列当中选择一个基准数(pivot)(我们选择第一个数为基准数)
将序列当中的所有数依次遍历,比基准数大的位于其右侧,比基准数小的位于其左侧
重复步骤,直到所有子集当中只有一个元素为止。

python语言实现代码如下:

# 快速排序
# L:待排序的序列 start排序的开始 index,end序列末尾的index
# 对于长度为length的序列:start = 0;end = length - 1
def quick_sort(L, start, end):if start , j, pivot = start, end, L[start]while i # 从右开始向左寻找第一个小于pivot的值
while (i and (L[j] >= pivot):j = j - 1
# 将小于pivot的值移到左边
if i 1
# 从左开始向右寻找第一个大于pivot的值
while (i and (L[i] 1
# 将大于pivot的值移到右边
if i 1
# 循环结束后,说明 i=j,此时左边的值全都小于pivot,右边的值全都大于pivot
# pivot的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序即可
# 递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
L[i] = pivot# 左侧序列继续排序
quick_sort(L, start, i-1)# 右侧序列继续排序
quick_sort(L, i+1, end)return Lif __name__ == '__main__':L = [9, 7, 2, 1, 7, 5, 3, 4]print(quick_sort(L, 0, len(L) - 1))


六、希尔排序

基本思路:将待排序数组按照步长进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。

python语言实现代码如下:

# 希尔排序
def insert_shell(L):# 初始化gap值,此处利用序列长度的一般为其赋值
gap = int(len(L)/2)# 第一层循环:依次改变gap值对列表进行分组
while gap >= 1:# 下面:利用直接插入排序的思想对分组数据进行排序
# range(gap,len(L)):gap开始
for x in range(gap, len(L)):# range(x-gap,-1,-gap):x-gap开始与选定元素开始倒序比较,每个比较元素之间间隔gap
for i in range(x-gap, -1, -gap):# 如果该组当中两个元素满足交换条件,则进行交换
if L[i] > L[i+gap]:temp = L[i+gap]L[i+gap] = L[i]L[i] = temp# while循环条件折半
gap = int(gap/2)return Lif __name__ == '__main__':L = [3, 2, 4, 6, 3, 9, 4, 2]print(insert_shell(L))

七、归并排序

基本思路:将已有的子序列合并,达到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

首先将序列每次折半拆分,然后将划分后的序列段两两排序合并

python语言实现代码如下:

# 归并排序
# 这是合并的函数
# 将序列L[first...mid]与序列L[mid+1...last]进行合并
def mergearray(L,first,mid,last,temp):# i,j,k分别进行赋值
i, j, k = first, mid+1, 0
# 当左右两边都有数时进行比较,取较小的数
while (i <&#61; mid) and (j <&#61; last):if L[i] <&#61; L[j]:temp[k] &#61; L[i]i &#61; i&#43;1
k &#61; k&#43;1
else:temp[k] &#61; L[j]j &#61; j&#43;1
k &#61; k&#43;1
# 如果左边序列还有数
while i <&#61; mid:temp[k] &#61; L[i]i &#61; i&#43;1
k &#61; k&#43;1
# 如果右边序列还有数
while j <&#61; last:temp[k] &#61; L[j]j &#61; j&#43;1
k &#61; k&#43;1
# temp当中该段有序元素赋值给L待排序列使之部分有序
for x in range(0, k):L[first&#43;x] &#61; temp[x]
# 这是分组的函数
def merge_sort(L,first,last,temp):if first int((first &#43; last) / 2)# 使左边序列有序
merge_sort(L, first, mid, temp)# 使右边序列有序
merge_sort(L, mid&#43;1, last, temp)# 将两个有序序列合并
mergearray(L, first, mid, last, temp)
# 归并排序的函数
def merge_sort_array(L):# 声明一个长度为len(L)的空列表
temp &#61; len(L)*[None]# 调用归并排序
merge_sort(L, 0, len(L)-1, temp)

八、基数排序

 基本思路&#xff1a;通过序列中各个元素的值&#xff0c;对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。

分配&#xff1a;我们将L[i]中的元素取出&#xff0c;首先确定其个位上的数字&#xff0c;根据该数字分配到与之序号相同的桶中

收集&#xff1a;当序列中所有的元素都分配到对应的桶中&#xff0c;再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ]

对新形成的序列L[]重复执行分配和收集元素中的十位、百位...直到分配完该序列中的最高位&#xff0c;则排序结束

python语言实现代码如下&#xff1a;


#************************基数排序****************************
# 确定排序的次数
# 排序的顺序跟序列中最大数的位数相关
def radix_sort_nums(L):maxNum &#61; L[0]# 寻找序列中的最大数
for x in L:if maxNum # 确定序列中的最大元素的位数
times &#61; 0
while maxNum > 0:maxNum &#61; (int)(maxNum/10)times &#61; times&#43;1
return times
# 找到num从低到高第pos位的数据
def get_num_pos(num,pos):return ((int)(num/(10**(pos-1))))%10
# 基数排序
def radix_sort(L):count &#61; 10*[None] # 存放各个桶的数据统计个数
bucket &#61; len(L)*[None] # 暂时存放排序结果
# 从低位到高位依次执行循环
for pos in range(1, radix_sort_nums(L)&#43;1):# 置空各个桶的数据统计
for x in range(0, 10):count[x] &#61; 0
# 统计当前该位(个位&#xff0c;十位&#xff0c;百位....)的元素数目
for x in range(0,len(L)):#统计各个桶将要装进去的元素个数
j &#61; get_num_pos(int(L[x]), pos)count[j] &#61; count[j] &#43; 1
# count[i]表示第i个桶的右边界索引
for x in range(1, 10):count[x] &#61; count[x] &#43; count[x-1]# 将数据依次装入桶中
for x in range(len(L)-1, -1, -1):# 求出元素第K位的数字
j &#61; get_num_pos(L[x], pos)# 放入对应的桶中&#xff0c;count[j]-1是第j个桶的右边界索引
bucket[count[j]-1] &#61; L[x]# 对应桶的装入数据索引-1
count[j] &#61; count[j]-1
# 将已分配好的桶中数据再倒出来&#xff0c;此时已是对应当前位数有序的表
for x in range(0, len(L)):L[x] &#61; bucket[x]


八、桶排序&#xff08;简化版&#xff1a;最简单最快速的排序&#xff09;

基本思路&#xff1a;个人理解-逆向思维&#xff0c;首先将要排序的目标排序好&#xff0c;然后将出现的数值记录&#43;1&#xff0c;形象的说就是&#xff0c;每出现一次&#xff0c;就将它放入对应的桶中&#xff0c;然后将出现的次数输出。

demo:

第一步&#xff1a;将分数按照大小排序&#xff0c;分数由0-100分构成&#xff0c;我们就定义一个数组int a[100] ,然后循环遍历数组的下标&#xff0c;将所有的下标对应的值初始化为0

# python中没有数组&#xff0c;我们用列表代替
a &#61; []
for i in range(0, 101):# 初始化0
a.append(0)

第二步&#xff1a;第一步以后&#xff0c;我们现在已经有了代表一百个分数的列表元素了&#xff0c;初始化为0&#xff0c;那么&#xff0c;我们的思维是&#xff0c;每一个元素就代表一个‘’桶‘’&#xff0c;每出现一次就将元素初始化的值对应&#43;1&#xff0c;也就是说&#xff0c;列表中&#xff08;C语言中是数组&#xff0c;下标&#xff09;每一个元素的值&#xff0c;就代表下标&#xff08;分数&#xff09;对应出现的次数。

# 循环输入5个数
for i in range(5):n &#61; int(input("请输入第%d个分数:" % i))a[n] &#43;&#61; 1

第三步&#xff0c;遍历这个列表&#xff0c;将大于0的元素&#xff0c;输出对应的下标&#xff08;分数&#xff09;&#xff0c;0不输出&#xff08;没有出现过&#xff09;&#xff0c;值是多少就输出几。

# 循环遍历&#xff0c;一次将出现大于0次数的元素输出&#xff0c;值是多少就输出多少&#xff0c;下标的对应的分数
for i in range(0, 101):for j in range(0, a[i]):print(i)

完整代码如下&#xff1a;

# python中没有数组&#xff0c;我们用列表代替
a &#61; []
for i in range(0, 101):# 初始化0
a.append(0)# 循环输入5个数
for i in range(5):n &#61; int(input("请输入第%d个分数:" % i))a[n] &#43;&#61; 1


# 循环遍历&#xff0c;一次将出现大于0次数的元素输出&#xff0c;值是多少就输出多少&#xff0c;下标的对应的分数
for i in range(0, 101):for j in range(0, a[i]):print(i)

这里的桶排序只是一个简化版本的桶排序&#xff0c;并不是真正意义的桶排序&#xff0c;不过思想是这样的&#xff0c;&#xff08;个人理解是逆向思维&#xff0c;从问题着手&#xff09;&#xff0c;由于python3中&#xff0c;已经没有数组的定义&#xff0c;只好用列表代替&#xff0c;希望大家不要纠结代码&#xff0c;要学会这种数据结构的抽象思维&#xff0c;只有这样&#xff0c;才能编程你们的东西。

桶排序最大的问题就是&#xff0c;当你的数据达到数以万计的时候&#xff0c;他就需要定义相应的空间&#xff0c;十分浪费&#xff0c;所以这样用空间换时间的做法不靠谱&#xff0c;再者&#xff0c;要是小数更没有办法进行排序了&#xff0c;所以引入了冒泡&#xff0c;快排&#xff0c;选择&#xff0c;直插&#xff0c;希尔等排序。


总结&#xff1a;

当数据量小的时候&#xff0c;这八种排序不会有什么区别。

但是当数据量达到几万&#xff0c;甚至几十万的时候&#xff0c;就会拉开差距。

从运行结果上来看&#xff0c;堆排序、归并排序、基数排序比其他算法要快的多。

提醒&#xff1a;这里的排序只是C语言中排序的最简单的思想&#xff0c;想要深入研究和挖掘&#xff0c;需要对算法进行不断的优化处理&#xff0c;以后有时间给大家更新。


















推荐阅读
  • 这篇文章主要介绍了Python拼接字符串的七种方式,包括使用%、format()、join()、f-string等方法。每种方法都有其特点和限制,通过本文的介绍可以帮助读者更好地理解和运用字符串拼接的技巧。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • Python语法上的区别及注意事项
    本文介绍了Python2x和Python3x在语法上的区别,包括print语句的变化、除法运算结果的不同、raw_input函数的替代、class写法的变化等。同时还介绍了Python脚本的解释程序的指定方法,以及在不同版本的Python中如何执行脚本。对于想要学习Python的人来说,本文提供了一些注意事项和技巧。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • OO第一单元自白:简单多项式导函数的设计与bug分析
    本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
  • 本文介绍了使用Python解析C语言结构体的方法,包括定义基本类型和结构体类型的字典,并提供了一个示例代码,展示了如何解析C语言结构体。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
author-avatar
mobiledu2502855653
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有