算法导论:插入排序和归并排序

算法:非形式地说,就是任何良定义的计算过程,该过程取某个值或值的集合作为输入并产生某个值或值的集合作为输出。

插入排序

插入排序的工作方式像许多人排序一手扑克牌。每一步将一个待排序的元素,按其大小插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。

伪代码:升序
INSERTION-SORT(A)                                                       
 for j=2 to A.length                                                    
      key=A[j]                                                          
      // insert A[j] into the sorted sequence A[1..j-1].                  
      i=j-1                                                             
      while i>0 and A[i] > key                                         
                 A[i+1]=A[i]                                              
                 i=i-1                                                 
      A[i+1]=key                                                        

最坏情况的运行时间:a*n ^2 + b*n +c
具体代码实现部分:
C语言+in-place实现
 void insertion_sort(int array[], int first, int last)
 {
        int i,j;
        int key;
        for (j = first+1; j<=last;i++)
        {
                key = array[j];
                i=j-1;
                while((i>=first) && (array[i] > key))
                {
                        array[i+1] = array[i];
                        i--;
                }
                array[i+1] = key; 
 
        }
 }

C++实现
#include <iterator>
template<typename biIter>
void insertion_sort(biIter begin, biIter end)
  {
    typedef typename std::iterator_traits<biIter>::value_type value_type;
    biIter bond = begin;
    std::advance(bond, 1);
    for( ; bond!=end; std::advance(bond, 1)) {
      value_type key = *bond;
      biIter ins = bond;
      biIter pre = ins;
      std::advance(pre, -1);
      while(ins!=begin && *pre>key) {
        *ins = *pre;
        std::advance(ins, -1);
        std::advance(pre, -1);
      }
      *ins = key;
    }
  }

知识:

std::advance

template <class InputIterator, class Distance>
  void advance (InputIterator& it, Distance n);
Advance iterator
Advances the iterator it by n element positions.



2.1-2 重写上文伪代码,使之按降序排序
INSERTION-SORT(A)
 for j=2 to A.length
      key=A[j]
      // insert A[j] into the sorted sequence A[1..j-1].
      i=j-1
      while i>0 and A[i] < key
                 A[i+1]=A[i]
                 i=i-1
      A[i+1]=key


我们集中关注一个算法的最坏情况运行时间,即对规模为n的任何输入,算法的最长运行时间。
  • 一个算法的最坏运行时间给出了任何输入的运行时间上界
  • 对某些算法,最坏情况经常出现。例如,当在数据库中检索一条特定信息时,若该信息不在数据库中出现,则最坏情况出现
2.2-2 选择排序算法:在数组A中有n个数,首先找出A中的最小元素并将其与A[1]中的元素进行交换。接着,找出A中的次最小元素将其与A[2]中的元素交换。对A的前n-1个元素按该方式进行。
伪代码:
SELECTION-SORT(A)
n=A.length
for j=1 to n-1
    smallest = j
    for i=j+1 to n
       if A[j] < A[smallest]
          smallest=i
    exchange A[j] with A[smallest]
 
具体实现代码:
void selection_sort(int *a, int len)
{
    register int i, j, min, t;
    for(i = 0; i < len - 1; i ++)
    {
        min = i;
        //查找最小值
        for(j = i + 1; j < len; j ++)
            if(a[min] > a[j])
                min = j;
        //交换
        if(min != i)
        {
            t = a[min];
            a[min] = a[i];
            a[i] = t;
        }
    }
}


我们可以选择使用的算法设计技术有很多。上文插入排序使用了增量的方法:在排序子数组A[1..j-1]后,将单个元素A[j]插入子数组适当的位置,产生排好序的结果。
下面,我们了解一种称为“分治法”的设计方法。 许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
分治模式在每层递归时都有三个步骤:
  1. 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
  2. 解决这些子问题,递归地求解各个子问题。然而,若子问题的规模足够小,则直接求解。
  3. 合并这些子问题的解为原问题的解。

.归并排序算法完全遵循分治模式。直观上其操作如下:

分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。

解决:使用归并排序递归地排序两个子序列。

合并:合并两个已排序的子序列以产生已排序的答案。

归并排序算法的关键操作是“合并”步骤中两个已排序虚拟的合并。我们通过调用一个辅助过程MERGE(A,p,q,r)来完成合并。

伪代码:

MERGE(A,p,q,r)
n1=q-p+1
n2=r-q
let L[1..n1+1] and R[1..n2+1] be new arrays
for i=1 to n1
    L[i] = A[p+i-1]
for j=1 to n2
    R[j]=A[q+j]
L[n1+1]=INF
R[n2+1]=INF
i=1
j=1
for k=p to r
    if L[i]<=R[j]
       A[k]=L[i]
       i=i+1
    else A[k]=R[j]
       j=j+1


MERGE-SORT(A,p,r)
  if p<r
     q=[(p+r)/2]
     MERGE-SORT(A,p,q)
     MERGE-SORT(A,q+1,r)
     MERGE(A,p,q,r)

C++实现代码

void Merge(int a[], int b[], int low, int mid, int high)
{
    int k = low;
    int begin1 = low;
    int end1 = mid;
    int begin2 = mid + 1;
    int end2 = high;
    while(k <= high )
    {
        if(begin1 > end1)
            b[k++] = a[begin2++];
        else if(begin2 > end2)
            b[k++] = a[begin1++];
        else
        {
            if(a[begin1] <= a[begin2])
                b[k++] = a[begin1++];
            else
                b[k++] = a[begin2++];
        }
    }
 
}
 
void MergePass(int a[], int b[], int seg, int size)
{
    int seg_start_ind = 0;
    while(seg_start_ind <= size - 2 * seg) 
    {
        Merge(a, b, seg_start_ind, seg_start_ind + seg - 1, seg_start_ind + seg * 2 - 1);
        seg_start_ind += 2 * seg;
    }
    //#如果一段是正好可歸併的數量而另一段則少於正好可歸併的數量#%
    if(seg_start_ind + seg < size)
        Merge(a, b, seg_start_ind, seg_start_ind + seg - 1, size - 1);
    else
        for(int j = seg_start_ind; j < size; j++) //#如果只剩下一段或者更少的數量#%
            b[j] = a[j];
}
 
void MergeSort(int a[], int size)
{
    int* temp = new int[size];
    int seg = 1;
    while(seg < size)
    {
        MergePass(a, temp, seg, size);
        seg += seg;
        MergePass(temp, a, seg, size);
        seg += seg;
    }
    delete [] temp;
}
 
int main()
{
    int a[] = {3, 5, 3, 6, 4, 7, 5, 7, 4};
    MergeSort(a, sizeof(a) / sizeof(*a));
    //#輸出#%
    for(int i = 0; i < sizeof(a) / sizeof(*a); i++)
        cout << a[i] << ' ';
    cout << endl;
 
    return 0;
}



你可能感兴趣的:(插入排序,归并排序,算法导论,排序算法)