文章目录

  • 排序
    • 冒泡排序
    • 鸡尾酒排序
    • 选择排序:
    • 简单插入排序:
    • 二分插入排序
    • 快速排序:
    • 希尔排序:
    • 归并排序:
    • 堆排序:

排序

点击以下图片查看大图:

冒泡排序

1、比较相邻的元素,如果前一个比后一个大或者小,就把它们两个交换位置。

2、对每一对相邻的元素做同样的工作,从开始第一对到最后一对,这步做完之后,最后的元素会是最大或者最小的数。

3、针对所有的元素重复以上的步骤,除了最后一个

4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

空间复杂度:O(1)

时间复杂度:O(n^2)

void swap1(int *a,int i,int j)
{int tmp = a[i];a[i]=a[j];a[j]=tmp;
}
void swap2(int *a,int *b)
{int tmp=*a;*a=*b;*b=tmp;
/**a=*a^*b;*b=*a^*b;*a=*a^*b;*a=*a+*b;   //*a=*a**b;*b=*a-*b;   //*b=*a/*b;*a=*a-*b;   //*a=*a/*b;
*/
}void BubbleSort(int *a,int len)
{int i,j;int flag;for(i=0;i<len-1;i++){flag=1;for(j=0;j<len-1-i;j++){if(a[j] > a[j+1]){// swap1(a,j,j+1);swap2(&a[j],&a[j+1]);flag=0;}}if(flag) //一趟下来没有交换,说明已经有序return;}
}

鸡尾酒排序

冒泡排序的改进:鸡尾酒排序

鸡尾酒排序,也叫定向冒泡排序,是冒泡排序的一种改进,此算法与冒泡排序的不同处在与从低到高然后从高到低,而冒泡排序仅从低到高去比较序列里的每个元素。
鸡尾酒排序的优点是能够在特定条件下(如集合中大部分元素有序),减少排序的回合数。

空间复杂度:O(1)

时间复杂度:O(n^2)

void CocktailSort(int *a,int len)
{int l=0;      //边界    int r=len-1;int i;while(l<r)    //(n-1)/2{//前半轮 将最小的元素放到后面for(i=l;i<r;i++){if(a[i] < a[i+1])swap2(&a[i],&a[i+1]);}r--;//后半轮 将最大的元素方在前面for(i=r;i>l;i--){if(a[i] > a[i-1])swap2(&a[i],&a[i-1]);}l++;}
}

选择排序:

一种简单直观的排序算法,初始时在序列中找到最小(大)元素,放在序列起始位置作为已排序序列,然后,在从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。

空间复杂度:O(1)

时间复杂度:O(n^2)

void SelectSort(int *a,int len)
{int i,j;for(i=0;i<len-1;i++){int max = i;for(j=i+1;j<len;j++){if(a[j]>a[max])//获取最大元素下标max=j;}if(max != i)swap2(&a[max],&a[i]);}
}

简单插入排序:

插入排序是一种简单直观的排序算法,它的工作原理非常类似我们抓扑克牌

对于未排序的数据(右手拍牌),对于已排序的序列(左手拿的排行顺序的牌),在拿数据的时候,直接根据左手排序的数从右往左扫描,找到对应位置插入

1、第一个元素开始,该元素可以认为已经被排序

2、取下一个元素,在已经排序的元素序列中从右往左扫描

3、如果取出来的元素,比已排序元素要小,将该元素往左移到它指定的位置

4、重复3步骤,直到找到已排序的元素小于或等于新元素的位置

5、将新元素插入到该位置

6、重复执行2~5步骤

空间复杂度:O(1)

时间复杂度:O(n^2)

void InsertSort(int *a,int len)
{int i,j,g;for(i = 1;i < len;i++){g = a[i]; //获取元素j = i-1;while(j >= 0 && a[j] < g) //判断插入位置{a[j+1] = a[j];    //元素后移j--;}a[j+1] = g;    //插入元素}
}

二分插入排序

适用于数据量比较大的情况

//分成两部分前半部分有序通过二分查找,后半部分无序
//开始排序时前半部分仅有一个元素
//1.先保存需要插入元素
//2.搜索前半部分需要插入的位置
//3.在插入位置后面的元素整体后移一位
//4.在指定位置插入元素
void BinarySearchInsertSort(int *a,int len)
{int i,j,g,left,right,mid;for(i=1;i<len;i++){ g=a[i];    left=0;right=i-1;while(left <= right){mid=left + (right -left)/2;if(a[mid] < g)left = mid+1;elseright = mid-1;}for(j=i-1;j>=left;j--)a[j+1]=a[j];a[left]=g;}
}

快速排序:

快速排序是一种“分治法”。它将原本的问题分成两个子问题(比基准值小的数和比基准值大的数),然后再分别解决这两个问题。子问题,也就是子序列完成排序后,再像一开始说明的那样,把他们合并成一个序列,那么对原始序列的排序也就完成了。 不过,解决子问题的时候会再次使用快速排序,甚至在这个快速排序里仍然要使用 快速排序。只有在子问题里只剩一个数字的时候,排序才算完成

分割子序列时需要选择基准值,如果每次选择的基准值都能使得两个子序列的长度为原本的一半,那么快速排序的运行时间和归并排序的一样,都为 O(nlogn)。和归并排序类似,将序列对半分割 log2n 次之后,子序列里便只剩下一个数据,这时子序列的排序也就完成了。因此,如果像下图这样一行行地展现根据基准值分割序列的过程,那么总共会有 log2n 行。

每行中每个数字都需要和基准值比较大小,因此每行所需的运行时间为 O(n)。由此可知,整体的时间复杂度为 O(nlogn)。如果运气不好,每次都选择最小值作为基准值,那么每次都需要把其他数据移到基
准值的右边,递归执行 n 行,运行时间也就成了 O(n^2)。这就相当于每次都选出最小值并把它移到了最左边,这个操作也就和选择排序一样了。此外,如果数据中的每个数字被选为基准值的概率都相等,那么需要的平均运行时间为 O(nlogn)

实现思路:

1、从序列中挑出一个元素,作为“基准值”

2、把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在后面,相同放到任一边,就好比分区操作

3、对1~2步骤进行递归

注意:实现快速排序的关键在于先在数组中选择一个数字,然后把数组中的数字分为两部分,比选择的数字小的移到数组的左边,比选择的数字大的一道数组的右边

void swap(int *a,int *b)
{int tmp=*a;*a=*b;*b=tmp;
}void quick_sort(int *a,int low,int high)
{if(low>=high)return;
/*
* 为什么要定义first和last来代替low和high呢,
* 因为后续左右区间元素还需要用到low和high两个变量
* 至于key值得引入,其实用a[low]代替完全没有影响
*/int first=low;int last=high;int key=a[low];//基准值while(first<last){while(a[last] >= key && first < last)last--;while(a[first] <= key && first < last)first++;if(first!=last)swap(&a[first],&a[last]);elseswap(&a[low],&a[last]);    //基准值交换}quick_sort(a,low,first);quick_sort(a,first+1,high);
}

希尔排序:

希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率

但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

希尔排序的代码如下:

#include <stdio.h>  // 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)
// 最优时间复杂度 ---- O(n)
// 平均时间复杂度 ---- 根据步长序列的不同而不同。
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定void ShellSort1(int *a,int len)
{int gap,i,j,target;for(gap=len/2;gap>0;gap/=2){for(i=gap;i<len;i++){target=a[i];j=i-gap;while(j>-1 && a[j]>target){a[j+gap]=a[j];j-=gap;}a[j+gap]=target;}}
}void ShellSort(int A[], int n)
{int h = 0;while (h <= n)                          // 生成初始增量{h = 3 * h + 1;}while (h >= 1){for (int i = h; i < n; i++){int j = i - h;int get = A[i];while (j >= 0 && A[j] > get){A[j + h] = A[j];j = j - h;}A[j + h] = get;}h = (h - 1) / 3;                    // 递减增量}
}int main()
{int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大希尔排序int n = sizeof(A) / sizeof(int);ShellSort(A, n);printf("希尔排序结果:");for (int i = 0; i < n; i++){printf("%d ", A[i]);}printf("\n");return 0;
}

归并排序:

归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。

归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

设定两个指针,最初位置分别为两个已经排序序列的起始位置

比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针到达序列尾

将另一序列剩下的所有元素直接复制到合并序列尾

归并排序的代码如下:

#include <stdio.h>
#include <limits.h>// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(n)
// 稳定性 ------------ 稳定void Merge(int A[], int left, int mid, int right)// 合并两个已排好序的数组A[left...mid]和A[mid+1...right]
{int len = right - left + 1;int *temp = new int[len];       // 辅助空间O(n)int index = 0;int i = left;                   // 前一数组的起始元素int j = mid + 1;                // 后一数组的起始元素while (i <= mid && j <= right){temp[index++] = A[i] <= A[j] ? A[i++] : A[j++];  // 带等号保证归并排序的稳定性}while (i <= mid){temp[index++] = A[i++];}while (j <= right){temp[index++] = A[j++];}for (int k = 0; k < len; k++){A[left++] = temp[k];}
}void MergeSortRecursion(int A[], int left, int right)    // 递归实现的归并排序(自顶向下)
{if (left == right)    // 当待排序的序列长度为1时,递归开始回溯,进行merge操作return;int mid = (left + right) / 2;MergeSortRecursion(A, left, mid);MergeSortRecursion(A, mid + 1, right);Merge(A, left, mid, right);
}void MergeSortIteration(int A[], int len)    // 非递归(迭代)实现的归并排序(自底向上)
{int left, mid, right;// 子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]for (int i = 1; i < len; i *= 2)        // 子数组的大小i初始为1,每轮翻倍{left = 0;while (left + i < len)              // 后一个子数组存在(需要归并){mid = left + i - 1;right = mid + i < len ? mid + i : len - 1;// 后一个子数组大小可能不够Merge(A, left, mid, right);left = right + 1;               // 前一个子数组索引向后移动}}
}int main()
{int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 };      // 从小到大归并排序int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };int n1 = sizeof(A1) / sizeof(int);int n2 = sizeof(A2) / sizeof(int);MergeSortRecursion(A1, 0, n1 - 1);          // 递归实现MergeSortIteration(A2, n2);                 // 非递归实现printf("递归实现的归并排序结果:");for (int i = 0; i < n1; i++){printf("%d ", A1[i]);}printf("\n");printf("非递归实现的归并排序结果:");for (int i = 0; i < n2; i++){printf("%d ", A2[i]);}printf("\n");return 0;
}

堆排序:

堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

我们可以很容易的定义堆排序的过程:

由输入的无序数组构造一个最大堆,作为初始的无序区

把堆顶元素(最大值)和堆尾元素互换

把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整

重复步骤2,直到堆的尺寸为1

堆排序的代码如下:

#include <stdio.h>// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定void Swap(int A[], int i, int j)
{int temp = A[i];A[i] = A[j];A[j] = temp;
}void Heapify(int A[], int i, int size)  // 从A[i]向下进行堆调整
{int left_child = 2 * i + 1;         // 左孩子索引int right_child = 2 * i + 2;        // 右孩子索引int max = i;                        // 选出当前结点与其左右孩子三者之中的最大值if (left_child < size && A[left_child] > A[max])max = left_child;if (right_child < size && A[right_child] > A[max])max = right_child;if (max != i){Swap(A, i, max);                // 把当前结点和它的最大(直接)子节点进行交换Heapify(A, max, size);          // 递归调用,继续从当前结点向下进行堆调整}
}int BuildHeap(int A[], int n)           // 建堆,时间复杂度O(n)
{int heap_size = n;for (int i = heap_size / 2 - 1; i >= 0; i--) // 从每一个非叶结点开始向下进行堆调整Heapify(A, i, heap_size);return heap_size;
}void HeapSort(int A[], int n)
{int heap_size = BuildHeap(A, n);    // 建立一个最大堆while (heap_size > 1)           // 堆(无序区)元素个数大于1,未完成排序{// 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素// 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法Swap(A, 0, --heap_size);Heapify(A, 0, heap_size);     // 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn)}
}int main()
{int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大堆排序int n = sizeof(A) / sizeof(int);HeapSort(A, n);printf("堆排序结果:");for (int i = 0; i < n; i++){printf("%d ", A[i]);}printf("\n");return 0;
}

参考:https://www.runoob.com/w3cnote/ten-sorting-algorithm.html

数据结构排序算法 内部排序(冒泡、鸡尾酒、选择、简单插入、二分插入、快排、希尔、归并、堆排)C语言实现相关推荐

  1. JavaScript 几种排序算法实现(冒泡、选择、插入、归并、快速排序)

    1. 冒泡 // 冒泡 - 每次循环, 将两两中偏大的元素移动到右边 for (let i = 0; i < arr.length - 1; i++) {for (let j = i; j &l ...

  2. python 按条件选择行和列数据_小白学数据结构-排序算法Python(冒泡、选择、快速、希尔等等)...

    排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们通常所说的排序算法往往指的是内部排序算法,即数据 ...

  3. 选择排序 冒泡 java_排序扯淡:冒泡、选择、插入(Java)

    算法的本质 最近一直在想,算法的本质到底是什么,为什么有时云里雾里,有时却了然可见,深深思考了下,算法本质就是一个解决问题的思想,计算机的世界里需要的就是抽象的思想,有了抽象思想,你就有了高度,原谅我 ...

  4. 冒泡链表排序java_链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)...

    以下排序算法的正确性都可以在LeetCode的链表排序这一题检测.本文用到的链表结构如下(排序算法都是传入链表头指针作为参数,返回排序后的头指针) struct ListNode { int val; ...

  5. 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)

    这篇文章分析一下链表的各种排序方法. 以下排序算法的正确性都可以在LeetCode的链表排序这一题检测.本文用到的链表结构如下(排序算法都是传入链表头指针作为参数,返回排序后的头指针) struct ...

  6. 【Java】七大排序(冒泡、选择、插入、快排、归并、堆排、希尔排序)

    一.相关归纳总结 1.时间复杂度 O(n^2):冒泡排序,选择排序,插入排序 O(log2n):快速排序,希尔排序,堆排序,归并排序 2.空间复杂度 O(1):插入排序.冒泡排序,选择排序,堆排序,希 ...

  7. 据结构学习(冒泡、选择、插入、快速排....

    [AppleScript] 纯文本查看 复制代码 ? 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 0 ...

  8. C语言——十四种内部排序算法【直接插入排序-冒泡排序-选择排序-插入排序-希尔排序-归并排序-快速排序-堆排序-折半插入排序-二分查找-路插入排序-表插入排序-简单选择排序-直接选择排序-树形选择】

    目录: 一:插入排序 A:直接插入排序 1.定义: 2.算法演示 实例1: 3.基本思想 4.排序流程图 实例1: B:希尔排序 1.定义: 2.算法演示 实例2: C:其他插入排序 a:折半插入排序 ...

  9. 数据结构实验:内部排序算法的性能分析

    文章目录 前言 一.问题描述 二.问题分析 三.实验结果及分析 (1)实验数据描述 (2)实验结果 (3)性能分析 四.源代码 前言 记录下本学期的数据结构实验 本实验主要集中于比较几种内部排序算法 ...

最新文章

  1. 刚刚!2021世界一流学科排名,正式发布!
  2. 人工智能和物联网:智慧城市的交通管理
  3. RingtoneManager-获得系统当前的铃声
  4. 一分钟搞定Java环境变量配置
  5. mysql 查询 系统字段 自然日_Mysql查询用户留存/留存率问题用户n日(内)留存、某日新增用户n日(内)留存...
  6. 算法 - 普里姆算法(修路问题求解)
  7. mysql内置含糊_mySQL入门04 内置函数
  8. mysql+'@'%_mysql忘记登录的人:命令拒绝用户”@’%’
  9. 电导池行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  10. bat执行php语句,php与bat之间调用的方法
  11. ubuntu 删除opencv4_ubuntu16.04 卸载重装Opencv
  12. R语言ETL工程:插入与合并(add/bind)
  13. 反射相关知识及jOOR反射库介绍
  14. 国标28181:实时视频播放
  15. 网络对战五子棋(来一起PK鸭)
  16. 使用 Laragon 在 Windows 中搭建 PHP开发环境及常见问题解决
  17. 解决“the database principal owns a schema in the database and cannot be dropped“问题
  18. Laravel框架中上传图片
  19. 逆向某视频直播软件,破解收费观看
  20. 智能硬件APP开发之路:基于机智云APP开源框架

热门文章

  1. 20200512_惠普打印机功能地址保护错误
  2. 【maven】MavenReportException: Error while generating Javadoc:
  3. LoadRunner各版本对IE版本支持
  4. 转:为什么内向的人,更适合当领导?
  5. phpword html转word word转html
  6. windows2008 server 安装Oracle 11g 补丁 经历
  7. EC6108V9/EC6108V9U/EC6108V92/EC6108V97_Hi3798MV100_当贝桌面_通刷_卡刷固件包
  8. CSS 弹性布局/flex布局最后一行左对齐
  9. DIY桌面机械臂__机械结构设计_p2
  10. we用的java插件_WebStorm weex插件报错