排序(插入,希尔,选择,堆排序,归并,快排)

本文深入讲解了五种经典的排序算法:插入排序、希尔排序、选择排序、堆排序和快速排序,包括它们的定义、算法复杂度及伪代码实现。

     《数论相关》那篇里最后提到的那道题——回文字符串,应该很多同学都会做吧,不过在这还是班门弄斧说一下思路:

假设有n个字符的字符串中有m1个字符n1,m2个字符n2,...mk个字符nk,则,m1 + m2 + ... + mk = n 且 m1,m2,...,mk中只能有1个奇数。这n个字符的字符串可以组合成的回文字符串个数,即转换成对m1/2个n1,m2/2个n2,...,mk/2个nk的全排列个数(n/2)!/(m1/2)!(m2/2)!...(mk/2)! (上篇博文提示1),根据提示2,将(n/2)!/(m1/2)!(m2/2)!...(mk/2)!的每一个阶乘均表示成不大于n/2的所有素数的阶乘积的形式则阶乘的除法即转换成减法。

      本篇总结一下学过的基础排序算法,只给出伪代码(自己亲手写出的代码才是自己的):

1.插入排序

直接插入排序    

定义:在长度为n的数列中,将第i个元素与第0~(i-1)个元素(已排序)从右到左依次比较,插入到合适的位置,使第0~i个元素也有序(0<= i < n)。算法导论中的     一个非常形象的比喻,从牌堆中一张一张抓牌,将抓到的牌,与手中已排好的牌从右到左依次比较,插到正确的位置。
算法复杂度:O(n^2)
    伪代码:
    insert_sort(A)
     for j = 2 to A.length
        key = A[j]
        i = j - 1
       while i > 0 && A[i] > key
           A[i+1] = A[i];
           i = i - 1
       A[i + 1] = key

希尔排序

定义:先将整个数列分割成若干个子数列分别进行直接插入排序,待整个数列中的记录"基本有序"时,再对全体记录进行一次直接插入排序,适用于部分有序的数列。
子数列的分割原则:
1.将相隔某个增量的元素组成一个子序列
2.每次设定的增量递减直到最后为1,并且增量序列的值必须互质
3.增量序列的取得方法尚未求出
部分有序:
1.每个元素距离它的最终位置不远
2.一个有序大数组接一个小数组
3.数组中只有几个元素的位置不正确

2.选择排序

简单选择排序

定义:在长度为n的数列中,从(n - i + 1)个数中选出最大(最小)的数与第i个数交换(1<= i <= n)
算法复杂度:O(n^2)
伪代码:
select_sort(A)
  for i = 1 to A.length
    l = i;
    for j = i to (n - i + 1)
        if A[j] > A[l]
            l = j
    exchange i and l

堆排序
 改进的选择排序算法,利用一种叫“堆”的数据结构进行信息管理。堆不仅用在堆排序中,也可以构造一种有效的优先的队列(应用很多,如共享计算机系统的作业调度)。
堆(二叉堆)
定义:是一个数组,可以被看成是一个近似的完全二叉树,树上每一个节点依层次分别对应数组的一个元素。
最大(小)堆 / 大(小)顶堆:除根节点外所有节点的父节点都不比自身小(大)
堆的操作(最大堆):
1.堆维护:当数组中某个位置index的元素不满足其所在的堆的性质时(该元素的值小于其子元素),需要将该元素与子节点中最大的交换位置,再以交换后的位置                              index1递归次过程,直到不需要与子节点交换位置为止
  伪代码:
        max_heapify(A, i)
l_child = 2i
r_child = 2i + 1
largest = i;
if l_child <= A.length && A[l_child] > A[i]
    largest = l_child
if r_child <= A.length && A[r_child] > A[largest]
    largest = r_child
if (largest != i)
    exchange A[largest] with A[i]
    max_heapify(A, largest]
2.建堆:可以通过1.堆维护,将长度为n的数组A转换成最大堆,因为子数组(A[n/2 + 1]~A[n])都是树的叶子节点,可以看成只有1个元素的大顶堆,则可以依次对 元素                   A[n/2]~A[1]依次调用堆维护,则可以生成一个最大堆
  伪代码:
       build_max_heap(A)
           for i = A.length / 2  to 1
           max_heapify (A, i)
堆排序
定义:利用以上两个堆的操作即可完成排序算法。在长度为n的数列中,先建好堆,循环以下两步:
    1.取出根元素,将最后一个叶子结点放入根节点位置
    2.对根节点做堆维护
算法复杂度:O(nlgn)
伪代码:
         heap_sort(A)
build_max_heap(A)
for i = A.length to 2
    exchange A[i] with A[1]
    copy A[A.length] to A[A.length - 1]
    max_heapify(A[length - 1], 1)
优先队列
同样是利用堆的数据结构,根据关键字的优先级存储关键字,应支持以下操作:
1. insert(S, key):关键字插入
2. maximum(S):返回优先级最大关键字
3.extract_maximum(S):取得优先级最大关键字并将其从队列中去除

3.归并排序

定义:将两个或两个以上的有序表组合成一个新的有序表
算法复杂度:O(nlgn)
伪代码:递归实现的两路归并排序
        merge_sort(A, s, e)
if s < e
mid = s + (e - s)>> 1;
merge_sort(A, s, mid)
merge_sort(A,mid + 1, e)
merge(A, s , mid, e)
        merge(A, s, mid, e)
tmp = int[e-s+1]
for i = s, j = mid + 1 to mid, e
     if A[i] > A[j] tmp[k++] = A[j++]
    else tmp[k++] = A[i++]
while i <= mid tmp[k++] = A[i++]
while j <= e tmp[k++] = A[j++]
A[s,e] = tmp[0,e-s]

4.快速排序

定义:从数列A[p,q]中任意选取一个值作为pivot(支点),将数列划分为两个子数列A[p,r],A[r+1,q],使得A[p,r]的所有元素均小于pivot,而A[r+1,q]的所有元素均大于                       pivot,然后对分成的两个子数列递归调用此过程
算法复杂度:O(nlgn)
伪代码:
        quick_sort(A,p,q)
if p < q
r = partition(A,p, q)
quick_sort(A,p,r-1)
quick_sort(A,r+1,q)
1.常见的两头扫描实现partition
        partition(A,low,high)
pivot = A[low]
while low < high
    while low < high &&  A[high] >= pivot high--
    A[low] = A[high]
    while low < high && A[low] <= pivot low++
A[high ] = A[low ]
A[low] = pivot
return low
2.单头扫描实现partition(循环里的判断是:如果碰到一个小于pivot的元素,则把该元素与第一个大于pivot的元素元素进行交换)
partition(A,low,high)
pivot = A[high]
i = low -1
for j = low to high - 1
    if A[j] <= pivot
        i = i + 1
        exchange A[i] with A[j]
exchange A[i+1] with A[high]
return i+1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值