开始正题之前,先啰嗦两句,讲到算法,一定离不开数据结构,算法与数据结构是分不开的。我们知道程序执行,最终的执行者是计算机,一个程序执行需要有数据,有执行动作,以及执行动作的流程。一句话总结:告诉计算机你的数据是什么,计算机会给你放内存中,告诉计算机你要怎么来操作数据,以及步骤是什么,这一套动作的步骤就是算法的具体实现。算法只是一种思想。
数据在计算机中存储,数据对于计算机来说没有什么区别,表现均为二进制数0和1,计算机拿到数据就会放在内存中,内存是连续的,固定不变的,被使用之后就不能用了,除非这块内存被释放。对于用户来说,我们的数据有很多种,比如文字,图片,音视频等等,这些最终都会转换成二进制数给到计算机。但是这些数据要怎么存储呢?为什么要考虑怎么存储?直接交给计算机不就行了吗?原因就是我们存储的数据最后要对它操作,如果随便存储,那么操作起来可能会很麻烦,而操作是计算机的cpu来执行的,太复杂,会导致计算机的性能变差。就跟我们收纳一个道理,如果你的衣服乱七八糟不分类整理放置的话,那么找起来就很麻烦。计算机cpu和内存是固定不变的,为了提高执行的效率和资源利用率最大化,人们就提出了数据结构,也就是对于用户的数据,我们应该如何来存储,才能最方便操作,它是一种模型。目前已经被广泛使用的有:线性表,栈,队列,树等
其中线性表有:顺序表和链表,顺序表的代表(python语言):列表。链表:单链表、双链表,单向循环链表。顺序表和链表的区别就是在内存中的存储方式不一样,顺序顾名思义就是在内存中是顺序的存储,链表就不是,在内存中是分散的。
栈和队列其实就是链表的一种应用,栈这种数据结构的特点就是:先进后出,像一个瓶子,瓶底的先进去,最后才能拿出来。队列:先进先出,窗口买票排队就是这样一种类型,在一个窗口买票,先排队的在前面的,先出去,后进的最后出去。
那么有了数据,只完成了10分之一。后面重点是如何对数据操作,对数据的常用操作有:增加,删除,修改,遍历等,把数据的结构和对它的操作放在一起,就是我们所说的抽象数据类型,简称ADT。
使用python语言的话,可以用类来实现。
有了上面的数据,和对数据的操作,那么回到最初,我们的需求是让计算机帮我们做事,完成我们想要实现的功能需求。那么怎么来实现呢?这个就是算法了,也就是说算法其实就是我们为了实现某个需求,要怎么去做才可以完成,也就是实现的步骤是什么,算法就是这么个东西,它是一种思想,具体实现可以用不同的语言,比如c、java、python等。
以上比较啰嗦,可以忽略…
回到正题,本文主要是想总结的是排序算法,也就是,如果用户想对一组数据进行排序,升序或者降序,那么怎么来实现呢?就跟我们要到达山顶有很多种方式,那么算法也是一样,解决一个问题,可以有很多种解法。
我们要解决的问题:排序
解法:冒泡、选择、插入
一、冒泡排序
算法的思想:从左到右依次比较相邻的两个数,将不满足条件的数据进行调整,比如按照升序或者按照降序,则分别将大的数据放在右边,小的数据放在左边,降序则是将小的数据放在右边,大的数据放在左边。最后分别是最大或者最小的数据沉到底部。
算法的复杂度:
最优复杂度:O(n)
最差复杂度:O(n^2)
算法稳定。
下面给个例子:
比如待排序的序列,需求是需要将如下的序列按照升序进行排列:
34 12 23 56 78 21 9
首先第一轮:
从左边的第一个数开始:将34和12进行比较,34比12大,所以需要互换位置,互换之后变成12 34 23 56 78 21 9,接着从第二个位置开始,将第二个和第三个进行比较,34比23大,所以同样进行互换位置,互换之后变成:12 23 34 56 78 21 9,依次进行下去,最后经过一轮互换之后序列变成了如下:
12 23 34 56 21 9 78
经过一轮之后,可以看到最大的数78沉到了最底下。也可以抽象的理解为最大的数据冒到最上面了。
第二轮:同样的方法,只比较除了最后一个剩下的所有数据。
…
依次类推,最终就剩下一个数据,结束比较,得到的数据也就是满足条件的有序序列。
下面通过代码来实现。
class buble_seq(object):
def buble_func(self,li):
count = 0
for j in range(1,len(li)):
for i in range(0,len(li)-j):
if li[i] > li[i+1]:
li[i],li[i+1] = li[i+1],li[i]
count += 1
l = [34,12,23,56,78,21,91,34,1,14,100,9]
li_bu = buble_seq()
li_bu.buble_func(l)
print(l)
运行结果:
12
[1, 9, 12, 14, 21, 23, 34, 34, 56, 78, 91, 100]
二、选择排序
思想:将待排序的序列分为两部分,一部分是:有序的,放置在左边,一部分是待排序的为无序的,放置在右边,主要是操作右边无序的序列,找到满足条件的数据放入左边有序的序列中。首次从第一个开始,找到最值和第一个值交换,那么有序序列就生成了,是一个只有一个值的序列,第二次从第二个值开始,找到最值放入左边的有序序列。
选择排序的复杂度:
最优复杂度:O(n^2)
最坏复杂度:O(n^2)
算法不稳定。
举个例子:
比如待排序的序列,需求是需要将如下的序列按照升序进行排列:
34 12 23 56 78 21 9
第一轮:从索引值为0的34开始,标记当前最小的值默认为34,找到34到9中最小的数值,每次找到比34小的数值,就更新最小的值对应的索引,最后一轮走下来,可以看到是9,并记下索引值,也就是最小值的索引值,将该值和首次开始的位置进行交换,即9和34进行交换。9 12 23 56 78 21 34
第二轮:从第二个值,也就是索引为1的12开始,标记最小值索引为1,找到12到34之间最小的数据是它本身,所以不用进行交换。9 12 23 56 78 21 34
…
依次类推:
第n-1轮,进行倒数第二个数和最后一个数中找到最小的数值,满足条件后最后进行交换,这样左边的序列就是一个有序的序列了。
下面给出代码实现:
#选择排序算法
class select_list(object):
def sele_sort(self,li):
n = len(li)
for j in range(0,n-1):
in_l = j
for i in range(j+1,n):
if li[in_l] > li[i]:
# li[i], li[i + 1] = li[i + 1], li[i]
in_l = i
li[j], li[in_l] = li[in_l], li[j]
l = [34, 12, 23, 56, 78, 21, 91, 34, 1, 14, 100, 9]
li_bu = select_list()
li_bu.sele_sort(l)
print(l)
运行结果:
[1, 9, 12, 14, 21, 23, 34, 34, 56, 78, 91, 100]
三、插入排序
思想:将待排序的序列分为两组,有序序列和无序序列,左边为有序序列,右边为无序序列,与选择排序不同的是,插入排序是操作左边的序列,从右边的序列中拿出数据和左边的序列进行比较,满足条件后插入进左边的有序序列中。最后遍历到最后一个,左边的有序序列生成。
算法复杂度:
最优复杂度:O(n)
最坏复杂度:O(n^2)
稳定性:稳定
举个例子:
比如待排序的序列,需求是需要将如下的序列按照升序进行排列:
34 12 23 56 78 21 9
第一轮:从第二个数开始,索引为1,也就是12和前面的1个数进行比较,首先和34进行比较,12比34小,进行交换,交换后的序列为:12 34 23 56 78 21 9
第二轮:从第三个数开始,索引为2,也就是23和前面的两个数进行比较,首先和34进行比较,23比34小,进行交换,交换后为 12 23 34 56 78 21 9
然后将23和12进行比较,23比12大,不满足条件,不进行交换,这一轮结束后:12 23 34 56 78 21 9
…
依次类推。
最后一轮:将最后一个值和前面的n-1个数进行比较。放在合适的位置。
下面使用python语言实现:
class insert_seq(object):
def insert_func(self,li):
for j in range(1,len(li)):
count = 0
for i in range(j,0,-1):
if li[i] < li[i-1]:
li[i],li[i-1] = li[i-1],li[i]
count += 1
if count == 0:
break;
l = [34,12,23,1,4,2]
li_bu = insert_seq()
li_bu.insert_func(l)
print(l)
运行结果:
[1, 2, 4, 12, 23, 34]