您的位置:首页 > 编程语言 > Python开发

用python实现一些基本算法

2018-03-09 16:56 507 查看

一、冒泡算法

def bubble(LI):
    length = len(LI)
    for i in range(length-1):
        count = 0
        for j in range(length-1-i):
            if LI[j] > LI[j+1]:
                LI[j],LI[j+1] = LI[j+1],LI[j]
                count+=1
        if count==0:
            break
L = [1,7,8,4,3,6,5,9,0]
bubble(L)

print(L)
思想:每次大循环都相应移一个相应最大的数到后面-----推测:eg(一共有8个数,一共进行8-1=7次大循环),从而确定外层循环为for i in range(len(L) -1)
        第一次内层循环(目的:将最大的数移到最后,此时,大循环i =0, 内层一共要执行8-1=7次才能将完成任务),第二次内层循环(目的:将倒数第二大的数移到倒数第二位上,此时大循环i = 1, 内层一共要执行8-1-1=6 才能完成任务),  剩下2个数(大循环i = 6, 小循环一共要执行8-1-6=1次才能完成任务)------推测:(大循环i和小循环j保持 j = len(L)-1-i 的关系)
写出循环 for i in range(len(L)-1):
                    for j in range(len(L)-1-i):
                            ....(进行L[j]和L[j+1]的比较,和位置交换)
  存在问题:如果8个数大小的列表,在进行第三次大循环后,就已经完成了任务,再往后执行,就等于浪费时间了。
  解决方法:   所以这里引入count(一旦发现某次大循环就已经达到了排序的任务,就结束循环)    

二、二分查找

def binary(L, val):
    low = 0
    high = len(L)-1
    while low <= high:  #low,high表示一个可以进行比较的范围,所以必须Low<=high
        mid = (low+high)//2
        if L[mid] == val:
            return mid
        elif L[mid] > val:
            high = mid-1  #因为大了,所以进一步缩小范围
        else:
            low = mid+1 #因为小了,所以进一步扩大范围
    return '没找到'
print(binary([1,2,3], 3))

思考:无脑方法(只想着实现[1, 2, 3, 4, 5]) ,以为奇数的第一次mid就是真正的mid(所以不用考虑//的问题),写完之后发现偶数的也行,到此完成。
       进一步:在列表长度为偶数的情况下,第一次mid均小于真正的mid(思考:因为程序中对 L[mid]和val进行了判断,所以mid在程序中的成长范围始终(low-----high),所以不用担心第一次mid均小于真正mid的问题,因为这个问题,通过范围很好的补偿了(所以这里是可以用//的原因)

三、插入排序(很慢)

//过程:将一个数插入到已经排好序的数据列中,使得新的数据列也是排好序的。
//
def insert_sort(L):
    for i in range(1, len(L)):

        key = L[i]

        j = i - 1
       while j >= 0:
            if  L[j] >key:

                    L[j+1] , L[j] = L[j], key   
            j -= 1

  return L
理解:因为i如果等于1,那么此时j = 0, 那么经过排序后,就是两个数进行比大小,所以得到的数据列是排好序的(此时长度为2)
以后的过程就是将一个数插入到一个已经排好序的数据列中(现实模型:一个人迟到了,别人都排好队了,这个人站在最后一个,他发现前面一个人比他高,然后他就说换个位置吧,换玩之后,他又发现前面人比他还高,再继续换..最终,他发现前面的人比他矮了,他就不换了,因为他确定他后面的那位比他高,前面那位比他矮,所以这个位置就是他应该站的位置)

四、直接选择排序

def  direct_sort(L):
    for  i in range(0, len(L)-1):

        min = i 

        for y in range(i+1, len(L) ): //思想: 遍历到某个位置的数,就找到该位置及后面所有数中最小的那个,

            if  L[y] < L[i]:                    //找到了,就用该位置的数和最小位置的数进行值交换

                min = y

       L[min], L[i] = L[i], L[min]

  return L

五、快速排序

思路:一般以开头为key。先从右向左边走,如果lists[right] < key, 就执行lists[left] =lists[right],然后就将key  赋值给 lists[right]。然后从左往右走,如果lists[left] > key, 就执行lists[right]] =lists[left],然后就将key 赋值给lists[left]。这样经过一次while大循环,即可实现以key为标准,key左边全是小于key的值,key右边全是大于key的值。然后以key所在位置作为分割线,进行分组递归。
key左边为一组,key右边为一组。

def quick_sort(lists, left ,right):
    if left >= right:
        return lists
    low = left
    high = right
    key = lists[left]   
    while left < right:
        while left < right and lists[right] >= key:
            right -= 1
        lists[left] = lists[right]   
        lists[right] = key                                                        ### A         
        print(left,right,lists)
        while left < right and lists[left] <= key:
            left += 1
        lists[right] = lists[left]
        lists[left] = key                                                         ###B

        print(left,right,lists)
    #-------------------------------------                               ###C

    quick_sort(lists, low, left-1)
    quick_sort(lists, left+1, high)
    return lists

print(quick_sort([6, 8, 9, 3, 5, 7],0,5))
#注意到A和B处都是为了确定key值所在位置,但是执行到quick_sort(lists, low, left-1),之前,key的位置都是一个,所以只需要在quick_sort(lists, low, left-1)值之前,确定key值就行了。又因为key永远都是周旋在lists[left]和lists[right]中(也就是要么lists[left] = key,要么就是lists[right]= key), 而且执行到quick_sort(lists, low, left-1)时,都是left =right ,所以这里直接可以lists[left] =key,或者lists[right] = key,这样就能确定key值了。
所以可以修改成
def quick_sort(lists, left, right):

    if left >= right:
        return lists
    key = lists[left]
    low = left
    high = right
    while left < right:
        while left < right and lists[right] >= key:
            right -= 1
        lists[left] = lists[right]
        while left < right and lists[left] <= key:
            left += 1
        lists[right] = lists[left]                         
    lists[right] = key             
    quick_sort(lists, low, left - 1)  
    quick_sort(lists, left + 1, high)
    return lists

#那么有个问题:这样决定key值的话,会不会对while中的循环过程造成影响。
#回答: 不会!因为在循环中,left,right所扩起来的范围逐渐缩小,而且重复值永远最多只有一个(只是key值会丢失,也就是说要将其中一个重复值改为key,这样就不会造成数据丢失),还是因为最后执行到分区递归时,left = right,所以这样做没问题。
#理解: 基本原理就是第一个函数(直白),第二个函数是改进了确定key的位置(不用交换一次数据,就确定一次,而是全部交换完之后,直接确定),精简了代码

六、希尔排序

def xier_sort(L):
      def xier_sort(L):
count = len(L)
group = int(count/2)
while group > 0:
for i in range(group, count):     #i从group开始(这样 j = i - group,前面才有值,然后j不断变大,数据列不断边长,这样就满足了插入排序的结构)
j = i - group
key = L[i]
while j >= 0:
if  L[j] > key:
L[j],L[j+group] = key, L[j]
j-=group
group = int(group/2)
return L
print(xier_sort([1,5,4,7,2,9,10,15,-2.5,0,6]))

这里用int(group/2),还是用round(group/2)都能得到结果(直接用round()就行了)

七、归并排序

def chaijie(L):
count = len(L)
middle = count//2
if len(L)<=1:
return L
left = chaijie(L[:middle])
right = chaijie(L[middle:])
return guibin_sort(left,right)

def guibin_sort(left, right):
l,r = 0,0
result = []
while l < len(left) and r < len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result+=left[l:]
result+=right[r:]
return result
print(chaijie([1,2,9,6,5,4,0,2.1,7.9]))
思想:先按照len(L)//2,划分left, right进行递归拆分,得到left,right后,就进行排序合并。left(left, right),right(left,right)通过排序合并最终能得到result。

八、堆排序

概念:根节点、非叶子节点、叶子节点
根节点: 根节点就是堆顶(同时也称非叶子节点),
非叶子节点:拥有自己的叶子节点(一个或两个)
叶子节点:没有自己的叶子节点
推导:    
        列  表  长  度(L):       3     4    5     6    7     8     9 
       非叶子节点个数(K):    1      2    2     3     3      4    4       满足K = len(L)// 2
       最后一个非叶子点的索引(S):   0     1     1    2      2      3    3     满足 S = K - 1 = len(L)// 2 -  1

      【for i  in range(len(L)// 2 -  1,-1,-1) 这就是所有叶节点的索引(根据规律得到)】
def heap_sort(L):
#TODO创建最大堆
count = len(L)
#遍历每个非叶子节点,进行最大堆创建
for root in range(count//2-1, -1, -1):
big_heap(L, root, count-1)
print(L)
#进行最大堆排序
for end in range(count-1,0,-1):
#一旦此序列成为大顶堆,就将L[0]和最后面的换
#这样,每次都能确定一个相对较大的在后面,从而完成由大到小的排序
L[end],L[0] = L[0],L[end]
big_heap(L, 0, end-1)
return L

def big_heap(L, root, end):
while True:
child = 2*root +1
if child > end:
break
if child+1 <= end and L[child+1] > L[child]:
child += 1
if L[child] > L[root]:
L[child],L[root] = L[root],L[child]
root = child
#因为是从最后一个非叶子节点进行的,所以如果L[root]比它的L[child]大的话
#就说明后面已经是完成了最大堆排序的。就不用再迭代进行最大堆建造了
else:

break
print(heap_sort([1,5,6,9,3,4,2,7,9.5,10.2]))

九、计数排序

def jishu_sort(L):
#记录每个数字有所少个,然后记录上<=该数字的个数
B = []
C = [0]*len(L)
for x in range(max(L)+1):
if x == 0:
B.append(L.count(x))
else:
B.append(L.count(x)+B[x-1])
#为了确保排序的稳定性,以倒序方式查找L中的每一个元素
for y in L[-1::-1]:
C[B[y]-1] = y
B[y] -= 1
return L
jishu_sort([1,6,9,4,3,6,2,8,0])
思路:根据L中的最大值构造出列表B(用来记录L中每个元素的个数),然后在B中,除了B[0]外,其他的B[i] = B[i]+B[i-1]
这样只需要在B中i位置的数字,就知道L[.] = i+1,应该在列表C中i-1的位置上。
【B用来记录L中每个元素的个数(经过B[i] = B[i]+B[i-1],除B[0] = B[0]外),就能通过B列表在C列表(C长度和B等长)找到L中对应元素应该在C中哪个位置存放, 每次存放完后,B中对应位置-1】
参考:https://www.cnblogs.com/developerY/p/3166462.html

十、基数排序

import math
def jishu_sort(L):
#得到L中最大的数字是几位数
k = int(math.ceil(math.log(max(L),10)))  

bucket = [[] for i in range(10)]
        for x in range(1,k+1):
for y in L:
bucket[int(y%(10**x)/(10**(x-1)))].append(y)  #可以实现对个位、十位、百位..的取模运算
L = []
for each in bucket:
L.extend(each)  #每次下一次循环的L, 就基于前一次排好的内容
bucket = [[] for i in range(10)]
jishu_sort([1,20,73,634,52,4,0])
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: