八大排序算法的 Python 实现
http://python.jobbole.com/82270/
和
算法导论
以下摘自:伯乐在线:http://blog.jobbole.com/70639/
归并排序算法是目前为止我们拥有的最重要的算法之一。它是一种基于比较的排序算法,使用分治法解决那些原本复杂度为O(N^2)的问题。归并排序是由数学家John von Neumann于1945年发明的。
快速排序是解决排序问题的另一种途径,它使用就地分解算法,同时它也是一种分治算法。这个算法的问题在于它是不稳定的排序算法,但它在基于内存的数组排序上确实非常高效。
最后,堆排序算法使用一个优先队列降低数据的查找时间,它也是一种就地排序算法,同样也是不稳定的排序算法。
相较于曾经使用的其他排序算法(如冒泡排序),上述算法带来了显著的改进。事实上,多亏了它们,今天我们才有了数据挖掘、人工智能、链接分析,以及世界上大部分的计算机工具,也包括网络在内。
先来感受一下排序的可视化吧:
用舞蹈来诠释排序算法,欢乐多多!
冒泡排序: http://t.cn/hrf58M
希尔排序:http://t.cn/hrosvb
选择排序:http://t.cn/hros6e
插入排序:http://t.cn/hros0W
快速排序:http://t.cn/ScTA1d
归并排序:http://t.cn/Sc1cGZ
视觉直观感受 7 种常用的排序算法
http://blog.jobbole.com/11745/
可视化对比十多种排序算法(C#版)
http://blog.jobbole.com/72850/
接下来自己实践操作一番
每个算法 先来段舞蹈观赏一下,然后找来各路牛人最直白的解释,最后是Python代码
http://www.cnblogs.com/kkun/archive/2011/11/23/2260265.html
http://blog.jobbole.com/72850/
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
如上图例子,
(a)有序序列:4,未排序:3,1,2,从3开始,从后向前,先跟4比较大小,如果3<4,则交换二者位置
Insertion sort works the way many people sort a hand of playing cards.
We start with an empty left hand and the cards face down on the table.
We then remove one card at a time from the table and insert it into the correct position in the left hand.
To find the correct position for a card, we compare it with each of the cards already in the hand, from right to left,
def insert_sort(lists): # 插入排序 count = len(lists) for i in range(<span style="color:#ff0000;">1</span>, count): key = lists[i] j = i - 1 while j >= 0: if lists[j] > key: <span style="color:#ff0000;">lists[j + 1] = lists[j] lists[j] = key</span> j -= 1 return lists
class solution(): def insersort(self,array): for i in range(1,len(array)): while array[i-1]>array[i] and i>=1: t=array[i-1] array[i-1]=array[i] array[i]=t i-=1 return array #array=[4,2,1,3] array=[4,3,2,1,3] r=solution().insersort(array) print(r)<span style="white-space:pre"> </span>#[1, 2, 3, 3, 4]
2. 冒泡排序:
http://t.cn/hrf58M
http://www.cnblogs.com/kkun/archive/2011/11/23/2260280.html
http://blog.csdn.net/morewindows/article/details/6657829
上图:
每次比较相邻的两个元素,如果第一个比第二个大,就交换他们两个。
第一轮,从0到N-1,会把最大的元素沉到最后一位
第二轮,从0到N-2
从0到N-3
一直到排完所有
复杂度分析:
http://www.cnblogs.com/jiqingwu/p/bubble_sort_analysis.html
<pre name="code" class="python">def bubblesort(A): for i in range(<span style="color:#ff0000;">0,len(A))</span>: for j in range(<span style="color:#ff0000;">1,len(A)-i)</span>: if A[<span style="color:#ff0000;">j-1]>A[j</span>]: A[j-1],A[j]=A[j],A[j-1] return A
3. 快速排序:
http://t.cn/ScTA1d
http://blog.csdn.net/morewindows/article/details/6684558
Quicksort, applies the divide-and-conquer paradigm
Here is the three-step divide-and-conquer process for sorting a typical subarray A[p,r]
Divide: Partition (rearrange) the array A[p,r] into two (possibly empty) subarrays A[p,q-1] and A[p+1,r] such that each element of A[p,q-1] is less than or equal to A[q] ,
which is, in turn, less than or equal to each element of A[p+1,r] .
Compute the index q as part of this partitioning procedure.
Conquer: Sort the two subarrays A[p,q-1] and A[p+1,r] by recursive calls to quicksort.
Combine: Because the subarrays are already sorted,no work is needed to combine them: the entire array A[p,r] is now sorted.
</pre><pre name="code" class="python">def quicksort(A<span style="color:#ff0000;">,p,r</span>): <span style="color:#ff0000;">if p<r:</span> q=Partition(A,p,r) quicksort(A,p,q-1) quicksort(A,q+1,r) def Partition(A,p,r): pivot=A[r] i=p-1 for j in range(p,r): if A[j]<<span style="color:#ff0000;">=</span>pivot: i=i+1 A[i],A[j]=A[j],A[i] A[i+1],A[r]=A[r],A[i+1] return i+1
4. 选择排序:
http://t.cn/hros6e
The algorithm divides the input list into two parts: the sublist of items already sorted, which is built up from left to right at the front (left) of the list,
and the sublist of items remaining to be sorted that occupy the rest of the list.
Initially, the sorted sublist is empty and the unsorted sublist is the entire input list.
The algorithm proceeds by finding the smallest (or largest, depending on sorting order) element in the unsorted sublist,
exchanging (swapping) it with the leftmost unsorted element (putting it in sorted order), and moving the sublist boundaries one element to the right.
It has O(n2) time complexity, making it inefficient on large lists, and generally performs worse than the similar insertion sort. Selection sort is noted for its simplicity, and it has performance advantages over more complicated algorithms in certain situations, particularly where auxiliary memory is limited.
def select_sort(lists): # 选择排序 count = len(lists) for i in range(<span style="color:#ff0000;">0, </span>count): min = i for j in range(i + 1, count): if lists[<span style="color:#ff0000;">min</span>] > lists[j]: min = j lists[min], lists[i] = lists[i], lists[min] return lists
5. 基数排序:
http://blog.csdn.net/cjf_iceking/article/details/7943609
radix sort is a non-comparative integer sorting algorithm that sorts data with integer keys
by grouping keys by the individual digits
which share the same significant position and value.
基数排序(以整形为例),将整形10进制按每位拆分,然后从低位到高位依次比较各个位。主要分为两个过程:
(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)
(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中
平均时间复杂度:O(dn)(d即表示整形的最高位数)
空间复杂度:O(10n) (10表示0~9,用于存储临时的序列)
稳定性:稳定
<span style="color:#ff0000;">import math</span> def radix_sort(lists, radix=10): k = int(math.ceil(math.log(max(lists), <span style="color:#ff0000;">radix</span>))) bucket = [[] for i in range(<span style="color:#ff0000;">radix</span>)] for i in range(1, k+1): for j in lists: bucket[j/(radix**(i-1)) % radix].append(j) del lists[:] for z in bucket: lists += z del z[:] return lists
6. 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。
Heapsort also introduces another algorithm design technique: using a data structure, in this case one we call a “heap,” to manage information.
可以利用数组的特点快速定位指定索引的元素。
its height is Theta(lgn)
堆
The (binary) heap data structure is an array object that we can view as a nearly complete binary tree
(The tree is completely filled on all levels except possibly the lowest, which is filled from the left up to a point)
The root of the tree is A[1] , and given the index i of a node, we can easily compute the indices of its parent, left child, and right child:
堆分为大根堆和小根堆,
大根堆的要求是每个节点的值都不大于其父节点的值,即
In a max-heap, the max-heap property is that for every node i other than the root,
A[PARENT[i]] >= A[i]。
(Thus, the largest element in a max-heap is stored at the root)
在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
For the heapsort algorithm, we use max-heaps.
MAX-HEAPIFY(A,i)
call MAX-HEAPIFY recursively on that subtree.
the running time of MAX- HEAPIFY on a node of height h as O(h)
BUILD-MAX-HEAP(A)
Each call to MAX-HEAPIFY costs O(lgn) time, and BUILD- MAX-HEAP makes O(n) such calls. Thus, the running time is O(n lg n)
we can build a max-heap from an unordered array in linear time.
HEAPSORT(A)
The heapsort algorithm starts by using BUILD-MAX-HEAP to build a max-heap on the input array A[1...n] ,
Since the maximum element of the array is stored at the root A[1], put it into its correct final position
decrementing A:heap-size—we observe that the children of the root remain max-heaps
The HEAPSORT procedure takes time O(n lg n)
since the call to BUILD-MAX- HEAP takes time O(n) and each of the n-1 calls to MAX-HEAPIFY takes time O(lgn)
先build一个heap,有个特性是root处是最大值,
所以把最大值交换到末尾,然后再main地build一个heap,得到第二大的值
再次交换,再次maintain
def adjust_heap(lists, i, <span style="color:#ff0000;">size)</span>: <span style="color:#ff0000;">lchild = 2 * i + 1 rchild = 2 * i + 2 max = i</span> if <span style="color:#ff0000;">lchild < size </span>and lists[lchild] > lists[max]: max = lchild if <span style="color:#ff0000;">rchild < size </span>and lists[rchild] > lists[max]: max = rchild if <span style="color:#ff0000;">max != i:</span> <span style="color:#ff0000;">lists[max], lists[i] = lists[i], lists[max]</span> adjust_heap(lists, max, size) def build_heap(lists, <span style="color:#ff0000;">size</span>): for i in range(<span style="color:#ff0000;">int</span>(size/2),-1,-1): adjust_heap(lists, <span style="color:#ff0000;">i, size</span>) def heap_sort(lists): <span style="color:#ff0000;">size = len(lists)</span> build_heap(lists, <span style="color:#ff0000;">size</span>) for i in range(size-1,0,-1): lists[<span style="color:#ff0000;">0</span>], lists[i] = lists[i], lists[0]
<span style="white-space:pre"> </span><span style="color:#ff0000;">size-=1</span>
adjust_heap(lists, <span style="color:#ff0000;">0,</span> size)
7.归并排序:
http://t.cn/Sc1cGZ
https://en.wikipedia.org/wiki/Merge_sort
Mergesort is a divide and conquer algorithm that was invented by John von Neumann in 1945.
Conceptually, a merge sort works as follows:
Divide the unsorted list into n sublists, each containing 1 element (a list of 1 element is considered sorted).
Repeatedly merge sublists to produce new sorted sublists until there is only 1 sublist remaining. This will be the sorted list.
归并排序最令人兴奋的特点是:不论输入是什么样的,它对N个元素的序列排序所用时间与NlogN成正比。http://blog.csdn.net/littlethunder/article/details/9472301
http://www.pythontab.com/html/2014/pythonhexinbiancheng_1114/910.html
归并排序也称合并排序,是分治法的典型应用。
分治思想是将每个问题分解成个个小问题,将每个小问题解决,然后合并。
具体的归并排序就是,将一组无序数按n/2递归分解成只有一个元素的子项,一个元素就是已经排好序的了。
然后将这些有序的子元素进行合并。
合并的过程就是 对 两个已经排好序的子序列,先选取两个子序列中最小的元素进行比较,
选取两个元素中最小的那个子序列并将其从子序列中去掉添加到最终的结果集中,直到两个子序列归并完成。
复杂度分析:
http://xwrwc.blog.163.com/blog/static/46320003201141582544245/
这是一个递推公式(Recurrence),我们需要消去等号右侧的T(n)
把T(n/2)展开成2T(n/4)+cn/2(下图中的(c)),然后再把T(n/4)进一步展开,直到最后全部变成T(1)=c
这是一个树状结构,每一层的和都是cn,共有lgn+1层,因此总的执行时间是cnlgn+cn,相比nlgn来说,cn项可以忽略,因此T(n)的上界是Θ(nlgn)。
T(n)的上下界都是Θ(nlgn),显然T(n)就是Θ(nlgn)。
http://blog.csdn.net/littlethunder/article/details/9472301
将一组无序数按n/2递归分解成只有一个元素的子项,一个元素就是已经排好序的了。
def mergesort(seq): if len(seq)<=1: return seq mid=int(len(seq)/2) left=<span style="color:#ff0000;">mergesort</span>(seq[:mid]) right=<span style="color:#ff0000;">mergesort</span>(seq[mid:]) return merge(left,right)
然后将这些有序的子元素进行合并。
合并的过程就是 对 两个已经排好序的子序列,先选取两个子序列中最小的元素进行比较,
选取两个元素中最小的那个子序列并将其从子序列中去掉添加到最终的结果集中,直到两个子序列归并完成。
def merge(left,right): <span style="color:#ff0000;">result=[] </span> i,j=0,0 <span style="color:#ff0000;"> while i<len(left) and j<len(right):</span> if left[i]<<span style="color:#ff0000;">=</span>right[j]: result.append(left[i]) i+=1 else: result.append(right[j]) j+=1 <span style="color:#ff0000;"> result+=left[i:] result+=right[j:] </span> return result
对链表进行归并排序
需要额外写一个获得mid的点
http://bookshadow.com/weblog/2014/11/21/leetcode-sort-list/
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: # @param head, a ListNode # @return a ListNode def sortList(self, head): if head is None or head.next is None: return head mid = self.getMiddle(head) rHead = mid.next mid.next = None return self.merge(self.sortList(head), self.sortList(rHead)) def merge(self, lHead, rHead): dummyNode = ListNode(0) dummyHead = dummyNode while lHead and rHead: if lHead.val < rHead.val: dummyHead.next = lHead lHead = lHead.next else: dummyHead.next = rHead rHead = rHead.next dummyHead = dummyHead.next if lHead: dummyHead.next = lHead elif rHead: dummyHead.next = rHead return dummyNode.next def getMiddle(self, head): if head is None: return head slow = head fast = head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next return slow
其他:
http://www.cnblogs.com/jingmoxukong/p/4308823.html
http://blog.csdn.net/morewindows/article/details/6678165/
http://blog.csdn.net/morewindows/article/details/6668714
希尔排序(Shell Sort)也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,
随着增量逐渐减少,每组包含的关键词越来越多,
当增量减至1时,整个文件恰被分成一组,算法便终止。
因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
舞蹈例子,
第一次,把10分成 2 份,此时增量 10/2 = 5 ,第二组从 0+5=5 开始,然后第2组的各个元素与前面距离自己5步的元素进行比较
第二次,把5分成 2 份,此时增量 5/2 = 2 ,第二组从 0+2=2开始,然后第2组的各个元素与距离自己2步的第1组各个元素进行比较
当增量减至1时,整个文件恰被分成一组,一次插入排序后,完成,算法便终止。
class solution(): def shellsort(self,array): l=int(len(array)/2) while l>0: #l=int(l/2) for i in range(l,len(array)): while i>=l: if array[i]<array[i-l]: t=array[i-l] array[i-l]=array[i] array[i]=t i-=l l=int(l/2) return array array=[49,38,65,97,26,13,27,49,55,4] r=solution().shellsort(array) print(r)
这个应该是标准的希尔排序吧
class solution(): def shellsort(self,array): step=int(len(array)/2) while step>0: for i in range(step): for j in range(i+step,len(array),step):
<span style="white-space:pre"> </span> <span style="color:#ff0000;">k=j-step</span>
<span style="color:#ff0000;"><span style="white-space:pre"> </span> key=array[j] while k>0: if array[k]>key: array[k+group]=array[k] </span><pre name="code" class="python"><span style="color:#ff0000;"><span style="white-space:pre"> </span> array[k]=key</span>k-=step
step=int(step/2) return array array=[49,38,65,97,26,13,27,49,55,4] r=solution().shellsort(array) print(r)