折半插入排序、冒泡排序、快速排序、简单选择排序、堆排序整理(介绍、原理、代码、时间复杂度)

发布时间:2024年01月17日

很多人在学习数据结构时被排序所困扰,以下是我整理的五种排序总结(折半插入排序、冒泡排序、快速排序、简单选择排序、堆排序),包括代码、时间复杂度、应用场景等。

  1. 折半插入排序 (Binary Insertion Sort)
    • 原理:该算法是插入排序的一种优化版本。在普通的插入排序中,为了找到新元素的位置,我们可能需要线性搜索。但在折半插入排序中,我们使用二分查找来找到新元素的位置,从而减少比较次数。
    • 步骤:对于数组中的每一个元素,通过二分查找确定其应该插入的位置,然后将该位置及其右侧的元素后移,最后将新元素插入到正确的位置。
    • 时间复杂度:最坏情况下是O(n^2),但平均情况下,由于二分查找的优化,时间复杂度为O(n log n)
      // 折半插入排序函数 ?
      
      void insert_sort(int* array , int n){ ?
      
      ? ? int temp; // 用于暂存需要插入的元素 ?
      
      ? ? ?for(int i = 1; i < n; i++){ // 从第二个元素开始循环 ?
      
      ? ? ? ? ?int low = 0; // 左边界初始化为0 ?
      
      ? ? ? ? ?int hight = i-1; // 右边界初始化为当前元素的前一个位置 ?
      
      ? ? ? ? ?temp = array[i]; // 暂存当前元素的值 ?
      
      ? ? ? ? ?while(hight>=low){ // 当左边界小于或等于右边界时进行查找 ?
      
      ? ? ? ? ? ? ?int mid = ( low + hight ) / 2; // 计算中间位置 ?
      
      ? ? ? ? ? ? ?if (array[mid] > temp){ // 如果中间位置的元素大于要插入的元素 ?
      
      ? ? ? ? ? ? ? ? ? ? hight = mid - 1; // 将右边界缩小到中间位置的左边 ?
      
      ? ? ? ? ? ? ?}else{ ?
      
      ? ? ? ? ? ? ? ? ?low = mid + 1; // 如果中间位置的元素小于或等于要插入的元素,将左边界扩大到中间位置的右边 ?
      
      ? ? ? ? ? ? ? } ?
      
      ? ? ? ? ? } ?
      
      ? ? ? ? ? // 将找到的位置及其右侧的所有元素向右移动一位,为要插入的元素腾出空间 ?
      
      ? ? ? ? ? for (int j = i-1; j > hight; j--) { ?
      
      ? ? ? ? ? ? ? array[j+1] = array[j]; ?
      
      ? ? ? ? ? } ?
      
      ? ? ? ? ? // 将找到的位置赋值给要插入的元素 ?
      
      ? ? ? ? ? array[hight+1] = temp; ?
      
      ? ? } ?
      
      }

  1. 冒泡排序 (Bubble Sort)
    • 原理:该算法通过重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
    • 步骤:通过不断地遍历数组,比较相邻的两个元素,若顺序错误则交换它们的位置。这个过程会重复进行,直到没有元素需要交换,即数组已排序完成。
    • 时间复杂度:最坏情况下是O(n^2),最好情况和平均情况下是O(n)
    • 代码:
// 冒泡排序函数 ?

void bobber_sort(int* array , int n){ ?

? ? int temp; // 用于交换元素的临时变量 ?

? ? for(int i=0;i<n;i++){ // 外层循环控制排序的轮数 ?

? ? ? ? for(int j=0;j<n-i-1;j++){ // 内层循环控制每轮排序的比较次数 ?

? ? ? ? ? ? if(array[j]>array[j+1]){ // 如果当前元素大于其相邻的下一个元素 ?

? ? ? ? ? ? ? ? temp=array[j]; // 临时存储当前元素的值 ?

?? ? ? ? ? ? ? array[j]=array[j+1]; // 将当前元素的值设置为下一个元素的值 ?

? ? ? ? ? ? ? ? array[j+1]=temp; // 将下一个元素的值设置为临时存储的值,完成交换 ?

? ? ? ? ?? } ?

??? ? } ?

??? ? } ?

? }
  1. 快速排序 (Quick Sort)
    • 原理:使用分治法策略。选择一个基准元素,重新排列数组,使得基准的左侧都比基准小,右侧都比基准大。然后对基准的左侧和右侧分别递归进行这个过程。
    • 步骤:选择一个基准值,通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均小于另一部分的关键字,然后分别对这两部分继续进行排序,以达到整个序列有序。
    • 时间复杂度:最坏情况下是O(n^2),但平均情况和最好情况下是O(n log n)
    • 代码:

// 快速排序函数 ?

void quick_sort(int* array , int low , int high){ ?

? ? int i = low, j = high; // 初始化两个指针,i指向数组的起始位置,j指向数组的末尾位置 ?

? ? int key = array[i]; // 选择数组的第一个元素作为基准值 ?

?

? ? while (i < j){ // 当两个指针没有相遇时,继续循环 ?

? ? ? ? while(i < j && array[j] >= key){ // 从右向左移动j指针,直到找到一个小于基准值的元素或j指针已经到了数组的起始位置 ?

? ? ? ? ? ? j--; ?

? ? ? ? } ?

? ? ? ? array[i] = array[j]; // 将找到的元素复制到i指针的位置 ?

?

? ? ? ? while(i < j && array[i] <= key){ // 从左向右移动i指针,直到找到一个大于基准值的元素或i指针已经到了数组的末尾位置 ?

? ? ? ? ? ? i++; ?

? ? ? ? } ?

? ? ? ? array[j] = array[i]; // 将找到的元素复制到j指针的位置 ?

? ? } ?

? ? array[i] = key; // 将基准值放到正确的位置上 ?

?

? ? if (i-1 > low) { // 如果基准值左边的元素个数大于1,递归进行排序 ?

? ? ? ? quick_sort(array, low, i-1); ?

? ? } ?

? ? if (i+1 < high){ // 如果基准值右边的元素个数大于1,递归进行排序 ?

? ? ? ? quick_sort(array, i+1, high); ?

? ? } ?

}

  1. 简单选择排序 (Selection Sort)
    • 原理:在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。如此反复,直到所有元素均排序完毕。
    • 步骤:首先在未排序序列中找到最小元素,存放到已排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。如此反复,直到所有元素均排序完毕。
    • 时间复杂度:最坏和平均情况下是O(n^2)
    • 代码:

// 简单选择排序函数 ?

void select_sort(int* array , int n){ ?

? ? int i, j; // 循环控制变量 ?

? ? int min; // 待排序数组中最小值的下标 ?

? ? int tmp; // 临时变量,用于交换元素 ?

?

? ? for (i = 0; i < n - 1; ++i){ // 外层循环,控制排序的轮数,从0到n-2 ?

? ? ? ? min = i; // 假设当前位置的元素是最小的 ?

?

? ? ? ? // 内层循环,从当前位置的下一个元素开始,查找最小值的位置 ?

? ? ? ? for (j = i + 1; j < n; ++j){ ?

? ? ? ? ? ? if (array[j] < array[min]){ ?

? ? ? ? ? ? ? ? min = j; // 更新最小值的位置 ?

? ? ? ? ? ? } ?

? ? ? ? } ?

?

? ? ? ? // 如果找到了更小的值,则交换当前位置和最小值位置的元素 ?

? ? ? ? tmp = array[i]; // 临时存储当前位置的元素值 ?

? ? ? ? array[i] = array[min]; // 将最小值位置的元素值赋给当前位置 ?

? ? ? ? array[min] = tmp; // 将临时存储的值赋给最小值位置 ?

? ? } ?

} // 简单选择排序结束
  1. 堆排序 (Heap Sort)
    • 原理:利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质(即子节点的键值或索引总是小于(或大于)它的父节点)。
    • 步骤:首先将无序数组构建成一个大顶堆(或小顶堆),然后将堆顶元素(最大值或最小值)与堆尾互换,之后将剩余的元素重新调整为大顶堆(或小顶堆),以此类推,直至整个数组有序。
    • 时间复杂度O(n log n)
      代码:
void swap(int* a, int* b) {//交换函数

? ? int tmp = *a;

? ? *a = *b;

? ? *b = tmp;

}



void maxheap_sort(int a[], int start,int end) {

? ? int c = start; // 当前(current)节点的位置

? ? int l = 2 * c + 1; // 左(left)孩子的位置

? ? int tmp = a[c]; // 当前(current)节点的大小

? ? for (; l <= end; c = l, l = 2 * l + 1) {

? ? ? ? // “l”是左孩子,“l+1”是右孩子

? ? ? ? if (l < end && a[l] < a[l + 1]) {

? ? ? ? ? ? l++; // 左右两个孩子中选择较大者,即m_heap[l+1]

? ? ? ? }

? ? ? ? if (tmp >= a[l]) {

? ? ? ? ? ? break; // 调整结束

? ? ? ? }

? ? ? ? else {// 交换值

? ? ? ? ? ? a[c] = a[l];

? ? ? ? ? ? a[l] = tmp;

? ? ? ? }

? ? }

}



void heap_sort(int a[], int n) {

? ? int i;

? ? // 从(n/2-1)--> 0 逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆

? ? for (i = n / 2 - 1; i >= 0; i--) {

? ? ? ? maxheap_sort(a, i, n - 1);

? ? }

? ? // 从最后一个元素开始对序列进行调整,不断地缩小调整的范围直到第一个元素

? ? for (i = n - 1; i > 0; i--) {

? ? ? ? // 交换a[0]和a[i]。交换后,a[i]是a[0..i]中最大的。

? ? ? ? swap(&a[0], &a[i]);

? ? ? ? // 调整a[0..i-1],使得a[0..i-1]仍然是一个最大堆。

? ? ? ? // 即,保证a[i-1]是a[0..i-1]中的最大值。

? ? ? ? maxheap_sort(a, 0, i - 1);

? ? }

}

文章来源:https://blog.csdn.net/weixin_75276735/article/details/135530130
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。