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

谁践踏了优雅 2022-09-17 11:23 260阅读 0赞

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

插入排序

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

伪代码:升序

  1. INSERTION-SORT(A)
  2. for j=2 to A.length
  3. key=A[j]
  4. // insert A[j] into the sorted sequence A[1..j-1].
  5. i=j-1
  6. while i>0 and A[i] > key
  7. A[i+1]=A[i]
  8. i=i-1
  9. A[i+1]=key

最坏情况的运行时间:a*n ^2 + b*n +c

具体代码实现部分:

C语言+in-place实现

  1. void insertion_sort(int array[], int first, int last)
  2. {
  3. int i,j;
  4. int key;
  5. for (j = first+1; j<=last;i++)
  6. {
  7. key = array[j];
  8. i=j-1;
  9. while((i>=first) && (array[i] > key))
  10. {
  11. array[i+1] = array[i];
  12. i--;
  13. }
  14. array[i+1] = key;
  15. }
  16. }

C++实现

  1. #include <iterator>
  2. template<typename biIter>
  3. void insertion_sort(biIter begin, biIter end)
  4. {
  5. typedef typename std::iterator_traits<biIter>::value_type value_type;
  6. biIter bond = begin;
  7. std::advance(bond, 1);
  8. for( ; bond!=end; std::advance(bond, 1)) {
  9. value_type key = *bond;
  10. biIter ins = bond;
  11. biIter pre = ins;
  12. std::advance(pre, -1);
  13. while(ins!=begin && *pre>key) {
  14. *ins = *pre;
  15. std::advance(ins, -1);
  16. std::advance(pre, -1);
  17. }
  18. *ins = key;
  19. }
  20. }

知识:

std::advance

  1. template <class InputIterator, class Distance>
  2. void advance (InputIterator& it, Distance n);

Advance iterator

Advances the iterator it by n element positions.

2.1-2 重写上文伪代码,使之按降序排序

  1. INSERTION-SORT(A)
  2. for j=2 to A.length
  3. key=A[j]
  4. // insert A[j] into the sorted sequence A[1..j-1].
  5. i=j-1
  6. while i>0 and A[i] < key
  7. A[i+1]=A[i]
  8. i=i-1
  9. A[i+1]=key

我们集中关注一个算法的最坏情况运行时间,即对规模为n的任何输入,算法的最长运行时间。

  • 一个算法的最坏运行时间给出了任何输入的运行时间上界
  • 对某些算法,最坏情况经常出现。例如,当在数据库中检索一条特定信息时,若该信息不在数据库中出现,则最坏情况出现

2.2-2 选择排序算法:在数组A中有n个数,首先找出A中的最小元素并将其与A[1]中的元素进行交换。接着,找出A中的次最小元素将其与A[2]中的元素交换。对A的前n-1个元素按该方式进行。

伪代码:

  1. SELECTION-SORT(A)
  2. n=A.length
  3. for j=1 to n-1
  4. smallest = j
  5. for i=j+1 to n
  6. if A[j] < A[smallest]
  7. smallest=i
  8. exchange A[j] with A[smallest]

具体实现代码:

  1. void selection_sort(int *a, int len)
  2. {
  3. register int i, j, min, t;
  4. for(i = 0; i < len - 1; i ++)
  5. {
  6. min = i;
  7. //查找最小值
  8. for(j = i + 1; j < len; j ++)
  9. if(a[min] > a[j])
  10. min = j;
  11. //交换
  12. if(min != i)
  13. {
  14. t = a[min];
  15. a[min] = a[i];
  16. a[i] = t;
  17. }
  18. }
  19. }

我们可以选择使用的算法设计技术有很多。上文插入排序使用了增量的方法:在排序子数组A[1..j-1]后,将单个元素A[j]插入子数组适当的位置,产生排好序的结果。

下面,我们了解一种称为“分治法”的设计方法。 许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

分治模式在每层递归时都有三个步骤:

  1. 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
  2. 解决这些子问题,递归地求解各个子问题。然而,若子问题的规模足够小,则直接求解。
  3. 合并这些子问题的解为原问题的解。

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

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

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

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

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

伪代码:

  1. MERGE(A,p,q,r)
  2. n1=q-p+1
  3. n2=r-q
  4. let L[1..n1+1] and R[1..n2+1] be new arrays
  5. for i=1 to n1
  6. L[i] = A[p+i-1]
  7. for j=1 to n2
  8. R[j]=A[q+j]
  9. L[n1+1]=INF
  10. R[n2+1]=INF
  11. i=1
  12. j=1
  13. for k=p to r
  14. if L[i]<=R[j]
  15. A[k]=L[i]
  16. i=i+1
  17. else A[k]=R[j]
  18. j=j+1
  19. MERGE-SORT(A,p,r)
  20. if p<r
  21. q=[(p+r)/2]
  22. MERGE-SORT(A,p,q)
  23. MERGE-SORT(A,q+1,r)
  24. MERGE(A,p,q,r)

C++实现代码

  1. void Merge(int a[], int b[], int low, int mid, int high)
  2. {
  3. int k = low;
  4. int begin1 = low;
  5. int end1 = mid;
  6. int begin2 = mid + 1;
  7. int end2 = high;
  8. while(k <= high )
  9. {
  10. if(begin1 > end1)
  11. b[k++] = a[begin2++];
  12. else if(begin2 > end2)
  13. b[k++] = a[begin1++];
  14. else
  15. {
  16. if(a[begin1] <= a[begin2])
  17. b[k++] = a[begin1++];
  18. else
  19. b[k++] = a[begin2++];
  20. }
  21. }
  22. }
  23. void MergePass(int a[], int b[], int seg, int size)
  24. {
  25. int seg_start_ind = 0;
  26. while(seg_start_ind <= size - 2 * seg)
  27. {
  28. Merge(a, b, seg_start_ind, seg_start_ind + seg - 1, seg_start_ind + seg * 2 - 1);
  29. seg_start_ind += 2 * seg;
  30. }
  31. //#如果一段是正好可歸併的數量而另一段則少於正好可歸併的數量#%
  32. if(seg_start_ind + seg < size)
  33. Merge(a, b, seg_start_ind, seg_start_ind + seg - 1, size - 1);
  34. else
  35. for(int j = seg_start_ind; j < size; j++) //#如果只剩下一段或者更少的數量#%
  36. b[j] = a[j];
  37. }
  38. void MergeSort(int a[], int size)
  39. {
  40. int* temp = new int[size];
  41. int seg = 1;
  42. while(seg < size)
  43. {
  44. MergePass(a, temp, seg, size);
  45. seg += seg;
  46. MergePass(temp, a, seg, size);
  47. seg += seg;
  48. }
  49. delete [] temp;
  50. }
  51. int main()
  52. {
  53. int a[] = {3, 5, 3, 6, 4, 7, 5, 7, 4};
  54. MergeSort(a, sizeof(a) / sizeof(*a));
  55. //#輸出#%
  56. for(int i = 0; i < sizeof(a) / sizeof(*a); i++)
  57. cout << a[i] << ' ';
  58. cout << endl;
  59. return 0;
  60. }

发表评论

表情:
评论列表 (有 0 条评论,260人围观)

还没有评论,来说两句吧...

相关阅读

    相关 算法导论学习之插入排序

    《算法导论》买了好久了,基本上没怎么看,最近思想上有了转变,觉得学习才是王道。准备重新拾起来学习,下面我就《算法导论》中的排序算法中的 插入排序做了个c++的简单实现,附加解

    相关 算法导论:c++归并排序

    基本思想就是把数组一直分成两半,然后对这两半进行排序归并。 先分成左右两半,然后合并时比较左右两半一直选最小的替代原数组。这种排序是非原址的,需要额外的空间。 伪代码非

    相关 算法导论归并排序

    归并排序的思想就是分治法; 分治法:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。 分治模式在每层递归时都有三个步骤: 一,分解原问题