当前位置:   article > 正文

[数据结构]九大基础排序算法总结_entry pivot

entry pivot

九大基础排序算法小结


一直想做份总结,总是抽不出时间,趁着周末闲着直接用了一整天做一份,一些内容参考了网上的一些博主的总结,还有网络上的一些图片。

好了,接下来就看看内容吧!


排序算法:

排序方法

时间复杂度

空间复杂度

稳定性

选用情况

平均情况

最坏情况

最好情况

Insertion Sort

O(n^2)

O(n^2)

O(n)

O(1)

稳定

n较小时

Selection Sort

O(n^2)

O(n^2)

O(n^2)

O(1)

不稳定

n较小时

Bubble Sort

O(n^2)

O(n^2)

O(n)

O(1)

稳定

n较小时

Shell Sort

O(nlog2n)

O(n^s)

O(nlog2n)

O(1)

不稳定

S是所选组数

Quick Sort

O(nlog2n)

O(n^2)

O(nlog2n)

O(nlog2n)

不稳定

n较大时

Merge Sort

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

稳定

n较大时

Heap Sort

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

不稳定

n较大时

Radix Sort

O((n+r)d)

O((n+r)d)

O((n+r)d)

O(n+r)

稳定

位数少,数据量大

Tree Sort

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

(不允许出现重复数字)

基本无序时

注:Radix Sort中n=数字个数; r=分配后链表的个数;d=数字或字符串的位数.



1.插入排序(Insertion Sort)

1)算法描述:

·         Step1:将待排序数组分为有序和无序两组(初始情况下有序组为第一个元素)

·         Step2:取出无序组第一个元素(First_unsorted)放入临时空间,将first_unsorted与有序组从最后一个元素开始进行比较,如果比有序组小,则将有序组中的该元素向后移一位,直到找到第一个比first_unsorted小的时候,将first_unsorted放入。至此,有序组++,无序组--。

·         Step3:重复step2,直到无序组数量为0.

       

算法结束。

 

2)适用情况分析:

·         稳定 

·         数组,链表均可实现

·         空间复杂度O(1) 

·         时间复杂度O(n2) 

·         最差情况:反序,需要移动n*(n-1)/2个元素 

·         最好情况:正序,不需要移动元素

·         数据量较小,较有序的情况下性能较好

 

 

3)参考代码:

顺序版本↓:

  1. template<class List_entry>
  2. void Sortable_List<List_entry>::insertion_sort()
  3. {
  4. List_entry current;
  5. if (count == 0)return;
  6. for (int first_unsorted = 1; first_unsorted < count; first_unsorted++) {
  7. if (entry[first_unsorted] >= entry[first_unsorted - 1])continue;
  8. current = entry[first_unsorted];
  9. int position = first_unsorted;
  10. do {
  11. entry[position] = entry[position - 1];
  12. position--;
  13. } while (position > 0 && entry[position - 1] > current);
  14. entry[position] = current;
  15. }
  16. }


链式版本↓:


  1. template<class List_entry>
  2. void Sortable_list<List_entry>::insertion_sort()
  3. {
  4. Node<List_entry> *first_unsorted, *last_sorted, *current, *previous;
  5. if (head == NULL)return;
  6. last_sorted = head;
  7. while (last_sorted->next){
  8. first_unsorted = last_sorted->next;
  9. if (first_unsorted->entry < head->entry) {
  10. last_sorted->next = first_unsorted->next;
  11. first_unsorted->next = head;
  12. head = first_unsorted;
  13. }
  14. else {
  15. previous = head;
  16. current = previous->next;
  17. while (current->entry < first_unsorted->entry) {
  18. previous = current;
  19. current = current->next;
  20. }
  21. if (current == first_unsorted)last_sorted = first_unsorted;
  22. else {
  23. last_sorted->next = first_unsorted->next;
  24. first_unsorted->next = current;
  25. previous->next = first_unsorted;
  26. }
  27. }
  28. }
  29. }


2.选择排序(selection Sort)

1)算法描述:

·         Step1:将待排序数组分为有序和无序两组(初始情况下有序组为空)

·         Step2:从左向右扫描无序组,找出最小的元素,将其放置在无序组的第一个位置。至此有序组++,无序组--;

·         Step3:重复Step2,直至无序组只剩下一个元素。

 

算法结束。

 

2)适用情况分析:

·         不稳定 

·         数组,链表均可实现

·         空间复杂度:O(1) 

·         时间复杂度:O(n2)  

·         最坏情况:O(n2) 第一个元素为最大元素,其余元素正序,需要交换n-1个元素(如:4 3 2 1) 

·         最好情况:O(n2) 正序,不需要交换元素

·         选择排序的最坏情况和最好情况下的实际没有太大区别。即选择排序对于原始记录的顺序不敏感。

 

 

3)参考代码:


  1. template<class List_entry>
  2. void Sortable_List<List_entry>::selection_sort()
  3. {
  4. for (int i = 0; i < count - 1; i++) {
  5. int min = min_key(i, count - 1);
  6. swap(min, i);
  7. }
  8. }
  9. template<class List_entry>
  10. int Sortable_List<List_entry>::min_key(int low, int high)
  11. {
  12. int min = low;
  13. for (int i = low + 1; i <= high; i++) {
  14. if (entry[min] > entry[i])min = i;
  15. }
  16. return min;
  17. }
  18. template<class List_entry>
  19. void Sortable_List<List_entry>::swap(int low, int high)
  20. {
  21. List_entry tmp = entry[low];
  22. entry[low] = entry[high];
  23. entry[high] = tmp;
  24. }

 

3. 冒泡排序(Bubble Sort)

1)算法描述:

·         Step1:比较相邻的元素。如果第一个比第二个大,就交换他们两个

·         Step2:对每一对元素均进行此操作,经过一轮后最大的元素应位于最后一位

·         Step3:从第一个元素重复进行前两步,每一轮最后一个元素都不参与比较,进行n轮

       算法结束。

 

2)适用情况分析:

稳定

·         大部分采取顺序,链式也可实现

·         空间复杂度O(1) 

·         时间复杂度O(n2) 

·         数据顺序对算法影响不大

 

3)参考代码:

 

  1. template<class List_entry>
  2. void Sortable_List<List_entry>::bubble_sort()
  3. {
  4. for(int i=0;i<count;i++)
  5. for (int j = 0 ; j < count-i-1; j++)
  6. if (entry[j] > entry[j+1]) {
  7. List_entry tmp = entry[j];
  8. entry[j] = entry[j + 1];
  9. entry[j + 1] = tmp;
  10. }
  11. }

 

4.希尔排序(Shell Sort)---插入排序的优化

1)算法描述:

·         Step1:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序

·         Step2:依次缩减增量再进行排序

·         Step3:待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序

 

算法结束

 

因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序相较于前几种方法有较大的提升

 

2)适用情况分析:

·         不稳定

·         采取顺序实现,对下标敏感

·         空间复杂度O(1) 

·         时间复杂度O(nlogn)

·         步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。

·         Donald Shell 最初建议步长选择为N/2并且对步长取半直到步长达到1

·         已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)

 

3)参考代码:


  1. template<class List_entry>
  2. void Sortable_List<List_entry>::shell_sort()
  3. {
  4. int increment = count;
  5. do {
  6. increment = increment / 3 + 1;
  7. for (int start = 0; start < increment; start++)
  8. sort_interval(start, increment);
  9. } while (increment > 1);
  10. }
  11. template<class List_entry>
  12. void Sortable_List<List_entry>::sort_interval(int start, int increment)
  13. {
  14. List_entry current;
  15. int position;
  16. for (int first_unsorted = start + increment; first_unsorted < count;
  17. first_unsorted += increment) {
  18. if (entry[first_unsorted] < entry[first_unsorted - increment]) {
  19. position = first_unsorted;
  20. current = entry[position];
  21. do {
  22. entry[position] = entry[position - increment];
  23. position -= increment;
  24. } while (position > start&&entry[position - increment] > current);
  25. entry[position] = current;
  26. }
  27. }
  28. }


在网络上看到了简化版本的shell sort,也可作参考:

  1. void shellsort3(int a[], int n)
  2. {
  3. int i, j, gap;
  4. for (gap = n / 2; gap > 0; gap /= 2)
  5. for (i = gap; i < n; i++)
  6. for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)
  7. swap(a[j], a[j + gap]);
  8. }


5.快速排序(Quick Sort)

1)算法描述:

·         Step1:在待排序列中选取一个轴点(pivot),通常选取中间点

·         Step2:将轴点与其他元素进行比较,将比轴点小的元素放在轴点之前,比轴点大的元素放在轴点之后。至此,pivot已被排好序

·         Step3:对0-(pivot-1)和(pivot+1)-n分别递归进行上述两步。

       算法结束。

 

2)适用情况分析:

·         不稳定。

·         顺序实现

·         空间复杂度:O(1) 

·         时间复杂度:O(nlog2n) 

·         最坏情况:O(n2)要排序数组基本有序,基准值每次取最大(最小)元素,退化为冒泡。 

·         最好情况:O(nlog2n) 基准值两边元素个数基本相同.

 

3)参考代码:


  1. template<class List_entry>
  2. void Sortable_List<List_entry>::quick_sort()
  3. {
  4. do_quick_sort(0, count - 1);
  5. }
  6. template<class List_entry>
  7. void Sortable_List<List_entry>::do_quick_sort(int low, int high)
  8. {
  9. int pivot_position;
  10. if (low < high) {
  11. pivot_position = partition(low, high);
  12. do_quick_sort(low, pivot_position - 1);
  13. do_quick_sort(pivot_position + 1, high);
  14. }
  15. }
  16. template<class List_entry>
  17. int Sortable_List<List_entry>::partition(int low, int high)
  18. {
  19. List_entry pivot;
  20. swap(low, (low + high) / 2); //将pivot放置到第一位
  21. pivot = entry[low];
  22. int last_small = low;
  23. for (int i = low + 1; i <= high; i++)
  24. if (entry[i] < pivot) swap(last_small++, i); //比pivot小的元素前移
  25. swap(low, last_small); //将pivot放回
  26. return last_small;
  27. }


6.归并排序(Merge Sort)

1)算法描述:

Step1:把待排序的列表划分为分成近似相等的两部分

Step2:分别将两个子列表排序(递归进行)

Step3:然后再合并成一个完整的列表

算法结束。

 

2)使用情况分析:

·         稳定 

·         链式实现

·         空间复杂度:O(n) 

·         时间复杂度:O(nlog2n) 

·         最坏情况:O(nlog2n) 

·         最好情况:O(nlog2n)

 

3)参考代码:


  1. template<class List_entry>
  2. void Sortable_list<List_entry>::mergesort(Sortable_list<List_entry> & list)
  3. {
  4. Sortable_list secondlist;
  5. if (list.size() > 1) {
  6. mergedivide(list, secondlist);
  7. mergesort(list);
  8. mergesort(secondlist);
  9. mergecombine(list, secondlist);
  10. }
  11. }
  12. template<class List_entry>
  13. void Sortable_list<List_entry>::mergecombine(Sortable_list<List_entry> & firstlist, const Sortable_list<List_entry> & secondlist)
  14. {
  15. Sortable_list tmp;
  16. List_entry x, y;
  17. int n = 0, m = 0, i = 0;
  18. while (n < firstlist.size() && m < secondlist.size()) {
  19. firstlist.retrieve(n, x);
  20. secondlist.retrieve(m, y);
  21. if (x <= y) {
  22. tmp.insert(i++, x);
  23. n++;
  24. }
  25. else {
  26. tmp.insert(i++, y);
  27. m++;
  28. }
  29. }
  30. while (n < firstlist.size()) {
  31. firstlist.retrieve(n++, x);
  32. tmp.insert(i++, x);
  33. }
  34. while (m < secondlist.size()) {
  35. secondlist.retrieve(m++, y);
  36. tmp.insert(i++, y);
  37. }
  38. firstlist = tmp;
  39. }
  40. template<class List_entry>
  41. void Sortable_list<List_entry>::mergedivide(Sortable_list<List_entry>& firstlist, Sortable_list<List_entry>& secondlist)
  42. {
  43. int mid = (firstlist.size() - 1) / 2 + 1;
  44. List_entry x;
  45. for (int i = 0; firstlist.retrieve(mid, x)==success; i++) {
  46. secondlist.insert(i, x);
  47. firstlist.remove(mid, x);
  48. }
  49. }


7.堆排序(Heap Sort)

1)算法描述:

         堆的定义:堆是一棵完全二叉树或者是近似完全二叉树。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。

 

·         Step1:建堆,序列分成无序和有序两组(初始有序为0)

·         Step2:取出堆顶最大的元素,与待排序列的最后一个元素作交换,至此,无序组--,有序组++,并对无序组进行堆调整

·         Step3:重复上述步骤,直至无序组仅剩一个元素

       算法结束。

 





2)使用情况分析:

不稳定

顺序结构实现

时间复杂度:o(nlogn)

空间复杂度:o(1)

由于建初始堆所需的比较次数较多,不建议在数据量小的情况下使用

 

3)参考代码:


  1. template<class List_entry>
  2. void Sortable_List<List_entry>::heap_sort()
  3. {
  4. build_heap(); //建堆
  5. for (int last_unsorted = count - 1; last_unsorted > 0; last_unsorted--) {
  6. List_entry current = entry[last_unsorted];
  7. entry[last_unsorted] = entry[0];
  8. insert_heap(current, 0, last_unsorted - 1); //将最后一个元素插入堆中,获取下一个最值
  9. }
  10. }
  11. template<class List_entry>
  12. void Sortable_List<List_entry>::insert_heap(const List_entry & current, int low, int high)
  13. {
  14. int large = 2 * low + 1;
  15. while (large <= high) {
  16. if (large < high&&entry[large] < entry[large + 1])
  17. large++; //将下标large调整为最大孩节点
  18. if (entry[large] < current)break;
  19. else { //promote entry[large]
  20. entry[low] = entry[large];
  21. low = large;
  22. large = 2 * low + 1;
  23. }
  24. }
  25. entry[low] = current;
  26. }
  27. template<class List_entry>
  28. void Sortable_List<List_entry>::build_heap()
  29. {
  30. for (int low = count / 2 - 1; low >= 0; low--) {
  31. List_entry current = entry[low];
  32. insert_heap(current, low , count - 1);
  33. }
  34. }


8.基数排序(Radix Sort)

1)算法描述:

LSD:

Step1:将待排序的元素按照最后一位进行分桶操作(桶按照最后一位排序从大到小)

Step2:按顺序将各个桶中的数据连接起来,形成新的序列

Step3:依次对新序列的倒数第二位至第一位进行上述两步操作

算法结束

 

MSD:

Step1:将待排序的元素按照最高位进行分桶操作

Step2:对每个桶的按照第二位进行再次分桶(递归进行)

Step3:进行至最后一位分桶操作完成后,对每个桶进行合并操作,得到有序序列

算法结束

 

 

2)使用情况分析:

·         稳定

·         链式实现

·         时间复杂度:假设在基数排序中,r为基数,d为位数。则基数排序的时间复杂度为O(d(n+r))

·         空间复杂度:在基数排序过程中,对于任何位数上的基数进行“装桶”操作时,都需要n+r个临时空间

·         数据量大时有明显优势,通常使用LSD法

 

3)参考代码:

MSD:


  1. int alphabetic_order(char c)
  2. {
  3. if (c == ' ' || c == '\0') return 0;
  4. if (c >= 'a'&&c <= 'z')return (c - 'a' + 1);
  5. if (c >= 'A'&&c <= 'Z')return (c - 'A' + 1);
  6. return 27;
  7. }
  8. void Sortable_list::rethread(queue<Record> queues[])
  9. {
  10. Record x;
  11. for (int i = 0; i < max_char; i++)
  12. while (!queues[i].empty()) {
  13. x = queues[i].front();
  14. insert(size(), x);
  15. queues[i].pop();
  16. }
  17. }
  18. void Sortable_list::MSD_radix_sort()
  19. {
  20. Record data;
  21. queue<Record> queues;
  22. while(remove(0,data)==success){
  23. queues.push(data);
  24. }
  25. sort(queues, key_size);
  26. }


LSD:


  1. void Sortable_list::LSD_radix_sort()
  2. {
  3. Record data;
  4. queue<Record> queues[max_char];
  5. for (int position = key_size - 1; position >= 0; position--) {
  6. // Loop from the least to the most significant position.
  7. while (remove(0, data) == success) {
  8. int queue_number = alphabetic_order(data.key_letter(position));
  9. queues[queue_number].push(data); // Queue operation.
  10. }
  11. rethread(queues); // Reassemble the list.
  12. }
  13. }


9.树排序(Tree Sort)

1)算法描述:

对于一棵二叉查找树,中序遍历的序列即为有序序列。

因此,二叉查找树的插入过程也可以看成是排序的过程。即

·         Step1:将无序序列逐一插入二叉排序树。

·         Step2:对二叉排序树进行中序遍历

算法结束

 

2)适用情况分析:

·         不稳定

·         链式实现

·         时间复杂度:o(nlogn)

·         空间复杂度:o(1)

·         Tree sort比较适合于无序的序列,对于基本有序的序列效率较低

 

 

3)参考代码:


  1. template<class Record>
  2. Error_code Binary_search_tree<Record>::c_insert(const Record & item)
  3. {
  4. Binary_node<Record> *&tmp=root;
  5. if(!tmp)root = new Binary_node<Record>(item);
  6. else {
  7. while (tmp != NULL) {
  8. if (tmp->data > item)tmp = tmp->left_child;
  9. else if (tmp->data < item)tmp = tmp->right_child;
  10. else return duplicate_error;
  11. }
  12. tmp = new Binary_node<Record>(item);
  13. return success;
  14. }
  15. }
  16. template<class Entry>
  17. void Binary_tree<Entry>::recursive_inorder(Binary_node<Entry>* sub_root, void(*visit)(Entry &))
  18. {
  19. if (sub_root) {
  20. recursive_inorder(sub_root->left_child,visit);
  21. (*visit)(sub_root->data);
  22. recursive_inorder(sub_root->right_child, visit);
  23. }
  24. }
  25. template<class Entry>
  26. void Binary_tree<Entry>::recursive_inorder(Binary_node<Entry>* sub_root, void(*visit)(Entry &))
  27. {
  28. if (sub_root) {
  29. recursive_inorder(sub_root->left_child,visit);
  30. (*visit)(sub_root->data);
  31. recursive_inorder(sub_root->right_child, visit);
  32. }
  33. }

 


 做的有些仓促,还望大家指正错误。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/163897
推荐阅读
相关标签
  

闽ICP备14008679号