几种排序方法总结

交换函数

//交换函数
void Swap(int* array, int i,int j) {
 int tmp = 0;
 tmp = array[i];
 array[i] = array[j];
 array[j] = tmp;
}

冒泡排序

时间复杂度:最坏O(n^2) 平均O(n^2) 最好O(n)
空间复杂度:O(1)
稳定性:稳定
数据敏感:敏感

void bubbleSort(int* array, int n) {
 while (n) {
  //对于已经有序的序列,通过标签提前结束排序过程
  int flag = 1;
  for (int i = 0; i < n - 1; i++) {
   if (array[i] > array[i + 1]) {
    Swap(array, i, i + 1);
    flag = 0;
   }
  }
  //flag=1,说明一轮冒泡的过程中没有反升数据交换,数据已经有序
  if (flag == 1) {
   break;
  }
  n--;
 }
}

插入排序

时间复杂度:最坏O(n^2) 平均O(n^2) 最好O(n)
空间复杂度:O(1)
稳定性:稳定
数据敏感:敏感

void insertSort(int* array, int n) {
 for (int i = 0; i < n - 1; i++) {
  //有序数列的最后一个位置end
  int end = i;
  //待排序的第一个数据key
  int key = array[end + 1];
  while (end >= 0 && array[end] > key) {
   array[end + 1] = array[end];
   end--;
  }
  //这里的end+1的目的是把end拉回到有序数列的最后一个位置
  //若没有进行while里面的end--,则array[end + 1]值不变
  array[end + 1] = key;
 }
}

希尔排序

时间复杂度:最坏O(n^1.3) 平均O(n^1.3) 最好O(n)
空间复杂度:O(1)
稳定性:不稳定 —> 分组时相同值的元素不一定可以分到同一组,预排序是可能导致相对位置发生变化
数据敏感:敏感

void shellSort(int* array, int n) {
 //gap表示步长,分组进行多组插入排序
 int gap = n;
 while (gap > 1) {
  //保证最后一次的步长为1
  gap = gap / 2;
  for (int i = 0; i < n - gap; i++) {
   //通过步长gap进行逻辑分组
   //组内进行插入排序
   //不同组的元素交替进行排序
   int end = i;
   int key = array[end + gap];
   while (end >= 0 && array[end] > key) {
    array[end + gap] = array[end];
    end = end - gap;
   }
   //这里的end+gap的目的是把end拉回到每一组当前有序数列的最后一个位置
      //若没有进行while里面的end = end - gap,则array[end + gap]值不变
   array[end + gap] = key;
  }
 }
}

选择排序

时间复杂度:最坏O(n^2) 平均O(n^2) 最好O(n^2)
空间复杂度:O(1)
稳定性:稳定
数据敏感:不敏感

void selectSort(int* array, int n) {
 for (int i = 0; i < n; i++) {
  //start表示未排序的最左边
  int start = i;
  //min表示未排序的数据中最小值的位置
  int min = start;
  //每次从未排序的数据中选择最小的数据,放到未排序的最左边(成为已排序的最后一个位置)
  for (int j = start + 1; j < n; j++) {
   if (array[j] < array[min]) {
    min = j;
   }
  }
  Swap(array, start, min);
 }
}

//选择排序第二个版本
void selectSort2(int* array, int n) {
 int begin = 0;
 int end = n - 1;
 while (begin < end) {
  //每次选择未排序中的最大值和最小值
  //把选择出来的最大值放到未排序的最右边,最小值放到未排序的最左边
  int max = begin, min = begin;
  for (int i = begin + 1; i <= end; i++) {
   //这里加个=,是为了保证最大值有相同数据时,优先获取位置靠后的数据,保证数据稳定性
   if (array[i] >= array[max]) {
    max = i;
   }
   if (array[i] < array[min]) {
    min = i;
   }
  }
  Swap(array, min, begin);
  //如果最大值位置发生了变化,需要更新
  if (max == begin) {
   max = min;
  }
  Swap(array, max, end);
  begin++;
  end--;
 }
}

堆排序

时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定 --> 调整的过程中相对位置可能会发生变化
数据敏感:不敏感

//向下调整建大堆
void shiftDown(int* array, int n, int parent) {
 int child = 2 * parent + 1;
 while (child < n) {
  if (child + 1 < n&&array[child + 1] > array[child]) {
   child++;
  }
  if (array[child] > array[parent]) {
   Swap(array, child, parent);
   parent = child;
   child = 2 * parent + 1;
  }
  else {
   break;
  }
 }
}

//堆排序
void heapSort(int* array, int n) {
 for (int parent = (n - 2) / 2; parent >= 0; parent--) {
  shiftDown(array, n, parent);
 }
 int size = n;
 while (size) {
  //循环尾删
  Swap(array, 0, size - 1);
  size--;
  shiftDown(array, size, 0);
 }
}

快速排序

时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定 --> 调整的过程中相对位置可能会发生变化
数据敏感:不敏感

选取基准值优化
三数取中法

int getMid(int* array, int begin, int end) {
 //每次从开头、中间、结尾选择三数之中大小处于中间位置的数字,作为快速排序的基准值
 int mid = begin + (end - begin) / 2;
 if (array[begin] < array[mid]) {
  if (array[mid] < array[end]) {
   return mid;
  }
  //array[mid]>=array[end]
  else {
   return end;
  }
 }
 else if (array[begin] >= array[mid]) {
  if (array[mid] > array[end]) {
   return mid;
  }
  else {
   //array[begin]大小处在中间
   if (array[begin] < array[end]) {
    return begin;
   }
   //array[end]大小处在中间
   else {
    return end;
   }
  }
 }
}

快速排序的三种划分子区间方法

//方法一:hora划分法
int partion(int* array, int begin, int end) {
 //选择一个基准值
 //用三数取中法
 int mid = getMid(array, begin, end);
 //交换起始位置和取中法取得的值
 Swap(array, begin, mid);
 int key = array[begin];
 int start = begin;
 //划分
 //基准值在首,则先从后向前找
 //基准值在尾,则先从前向后找
 while (begin < end) {
  //从后向前找第一个小于基准值的位置
  while (begin < end && array[end] >= key) {
   end--;
  }
  //从前向后找第一个大于基准值的位置
  while (begin < end && array[begin] <= key) {
   begin++;
  }
  Swap(array, end, begin);
 }
 //当begin和end相遇时,让基准值和相遇位置交换
 Swap(array, begin, start);
 //最后返回基准值位置
 return begin;
}

//方法二:挖坑法
int partion2(int* array, int begin, int end) {
 //选择一个基准值
 //用三数取中法
 int mid = getMid(array, begin, end);
 //交换起始位置和取中法取得的值
 Swap(array, begin, mid);
 int key = array[begin];
 while (begin < end) {
  //从后向前找第一个小于基准值的数据
  while (begin < end && array[end] >= key) {
   end--;
  }
  //拿小于基准值的数据填前面的坑
  array[begin] = array[end];
  while (begin < end && array[begin] <= key) {
   begin++;
  }
  //拿大于基准值的数据填后面的坑
  array[end] = array[begin];
 }
 //当begin和end相遇时,坑中填基准值
 array[begin] = key;
 //最后返回基准值位置
 return begin;
}

//方法三:前后指针法
int partion3(int *array, int begin, int end) {
 //用三数取中法
 int mid = getMid(array, begin, end);
 //交换起始位置和取中法取得的值
 Swap(array, begin, mid);
 //最后一个小于基准值的位置
 int prev = begin;
 //新发现的下一个小于基准值的位置
 int cur = prev + 1;
 //选择一个基准值
 int key = array[begin];
 while (cur <= end) {
  //新发现的小数据和尾指针指向的位置不连续,则中间含有大于基准值的数据,故进行交换
  //大数据向后移动,小数据向前移动
  if (array[cur] < key && ++prev != cur) {
   Swap(array, prev, cur);
  }
  cur++;
 }
 Swap(array, prev, begin);
 return prev;
}

递归实现快速排序

void quickSort(int* array, int begin, int end) {
 if (begin >= end) {
  return;
  }
 //划分第一次区间
 //int keyPos = partion(array, begin, end);
 //int keyPos = partion2(array, begin, end);
 int keyPos = partion3(array, begin, end);
 //递归划分接下来的子区间
 quickSort(array, begin, keyPos - 1);
 quickSort(array, keyPos + 1, end);
}

非递归实现快速排序
栈实现

void quickSortNoR(int* array, int n){
 Stack st;
 stackInit(&st, 10);
 //起始区间入栈: 先右后左
 if (n > 1){
  stackPush(&st, n - 1);
  stackPush(&st, 0);
 }
 //遍历栈,划分栈中的每一个区间
 while (stackEmpty(&st) != 1){
  //获取栈顶区间
  int begin = stackTop(&st);
  stackPop(&st);
  int end = stackTop(&st);
  stackPop(&st);
  //划分
  int keyPos = partion3(array, begin, end);
  //子区间入栈: 先入右区间
  // 右: keyPos + 1, end
  if (keyPos + 1 < end){
   stackPush(&st, end);
   stackPush(&st, keyPos + 1);
  }
  //左: begin ,  keyPos - 1
  if (begin < keyPos - 1){
   stackPush(&st, keyPos - 1);
   stackPush(&st, begin);
  }
 }
}

队列实现

void quickSortNoR2(int* array, int n){
 Queue q;
 queueInit(&q);
 if (n > 1){
  //先左后右
  queuePush(&q, 0);
  queuePush(&q, n - 1);
 }
 while (queueEmpty(&q) != 1){
  //获取队头区间
  int begin = queueFront(&q);
  queuePop(&q);
  int end = queueFront(&q);
  queuePop(&q);
  //划分
  int keyPos = partion3(array, begin, end);
  //子区间入队
  if (begin < keyPos - 1){
   queuePush(&q, begin);
   queuePush(&q, keyPos - 1);
  }
  if (keyPos + 1 < end){
   queuePush(&q, keyPos + 1);
   queuePush(&q, end);
  }
 }
}

归并排序

时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定:稳定
数据敏感:不敏感

合并:需要知道两个有序子序列的区间: [begin, mid] [mid + 1, end]

void merge(int* array, int begin, int mid, int end, int* tmp) {
 int begin1 = begin, end1 = mid, begin2 = mid + 1, end2 = end;
 int idx = begin;
 //合并
 while (begin1 <= end1 && begin2 <= end2) {
  if (array[begin1] <= array[begin2]) {
   tmp[idx] = array[begin1];
   idx++;
   begin1++;
  }
  else {
   tmp[idx] = array[begin2];
   idx++;
   begin2++;
  }
 }
 //查看是否有剩余元素
 if (begin1 <= end1)
  memcpy(tmp + idx, array + begin1, sizeof(int)* (end1 - begin1 + 1));
 if (begin2 <= end2)
  memcpy(tmp + idx, array + begin2, sizeof(int)* (end2 - begin2 + 1));
 //拷贝到原始空间
 memcpy(array + begin, tmp + begin, sizeof(int)* (end - begin + 1));
}

递归实现归并排序

void mergeSortR(int* array, int begin, int end, int* tmp) {
 if (begin >= end) {
  return;
 }
 int mid = begin + (end - begin) / 2;
 //首先保证子区间有序,首先子区间的排序
 mergeSortR(array, begin, mid, tmp);
 mergeSortR(array, mid + 1, end, tmp);
 //最后合并子区间
 merge(array, begin, mid, end, tmp);
}

void mergeSort(int* array, int n) {
 int* tmp = (int*)malloc(sizeof(int)* n);
 mergeSortR(array, 0, n - 1, tmp);
 free(tmp);
}

非递归实现归并排序

void mergeSortNoR(int* array, int n){
 int* tmp = (int*)malloc(sizeof(int)* n);
 //待合并区间的元素个数
 int k = 1;
 //多轮的归并
 while (k < n){
  //一轮归并
  for (int i = 0; i < n; i += 2 * k){
   //[begin, mid]  [mid + 1, end]
   int begin = i;
   int mid = i + k - 1;
   //判断mid是否越界
   if (mid >= n - 1)
    continue;
   int end = i + 2 * k - 1;
   //判断end是否越界
   if (end >= n)
    end = n - 1;
   merge(array, begin, mid, end, tmp);
  }
  k *= 2;
 }
}

计数排序(只适合小范围数据, 如果范围大,空间复杂度较高)

时间复杂度:O(max(n,范围))
空间复杂度:O(范围)
稳定:一般教材认为是稳定的(欠妥)
数据敏感:不敏感

void countSort(int* array, int n){
 // 1, 2, 3,   1000000
 //统计范围
 int min = array[0], max = array[0];
 for (int i = 1; i < n; ++i){
  if (array[i] > max)
   max = array[i];
  if (array[i] < min)
   min = array[i];
 }
 int range = max - min + 1;
 //开辅助空间,进行计数
 int* countArr = (int*)malloc(sizeof(int)* range);
 //初始化为0
 memset(countArr, 0, sizeof(int)* range);
 //统计次数
 for (int i = 0; i < n; ++i){
  countArr[array[i] - min]++;
 }
 //恢复数据, 遍历计数数组
 int idx = 0;
 for (int i = 0; i < range; ++i){
  while (countArr[i]--){
   array[idx++] = i + min;
  }
 }
 free(countArr);
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值