排序算法(插入、希尔、选择、冒泡、归并, 快速)

排序算法

常见的排序算法

  • 内部排序

    • 插入查找
    • 希尔排序
    • 选择排序
    • 冒泡排序
    • 归并排序
    • 快速排序
  • 外部排序


1. 插入排序

思路

插入已经排好序的前面部分, 相当于在前面排好序的部分进行search.

复杂度分析:
  • 最好情况: O(n)
  • 最差情况: O( n 2 n^2 n2)
  • 平均情况: O( n 2 n^2 n2)
代码:
void insertSort(int *arr,int num){
  int tmp;
  for(int i = 1; i < num; i++) {
    tmp = arr[i]; //i 为未排序的位置
    for(int j = i - 1; j >= 0 ;j--) {
      //j 为未排序i前的位置
      if(tmp < arr[j]) {
        arr[j+1] = arr[j];
        if( j == 0) arr[j] = tmp;
      } else{
        arr[j+1] = tmp;
        break;
      }
    }
  }
}

2. 希尔排序

思路

在插入排序只中, 逆序的个数 决定了交换的次数, 平均逆序的数量C(n, 2) = O( n 2 n^2 n2)
所以, 就是要降低逆序的次数, 通过分组的方法.
分组的方式不是简单的逐段分割, 而是像个某个增量的记录组成一个分组.

复杂度分析:
  • 最好情况:
  • 最差情况: O( n l o g n nlogn nlogn)~O( n 2 n^{2} n2)
  • 平均情况: O( n l o g n nlogn nlogn)~O( n 2 n^{2} n2)
代码:
void shellSort(int *arr,int num, int *add, int addSize) {

  int tmp;
  int addtion; // 记录当前增量如[5,3,1]

  for(int i = 0; i < addSize; i++ ) {
    addtion = add[i];
    for(int k = addtion ; k < num;k++) {
      tmp = arr[k]; //k 为未排序的位置
      for(int q = k - addtion ; q >= 0; q = q - addtion) { 
      	//q为未排序k前的位置
        if(arr[q] > tmp) {
          arr[q+addtion] = arr[q];
          if((q-addtion) < 0) arr[q] = tmp; // 判断是否是每组第一个数
        } else{
          arr[q+addtion] = tmp;
          break;
        }
      }
    }
  }
}

3. 选择排序

思路

在未排序的无序子序列中找到最小或最大的

复杂度分析:
  • 时间复杂度: O( n 2 n^{2} n2)
代码:
void selectSort(int * arr, int num) {
  int currentMinIndex, tmp;
  for(int i = 0; i < num - 1; i++) {
    currentMinIndex = i;
    for(int j = i+1; j < num; j++) {
      if(arr[j] < arr[currentMinIndex]) currentMinIndex = j;
    }
    if(currentMinIndex != i) {
      tmp = arr[i];
      arr[i]= arr[currentMinIndex];
      arr[currentMinIndex] = tmp;
    }

  }
}

4. 冒泡排序

思路

相邻的两个元素比较, 可以把当前无序的元素中最大最小的一直向后移.
优化:

  1. 当一趟中不存在交换时, 排序可以结束,利用一个flag变量可以实现.
  2. 每趟可以确定的有序子序列的位置为最后一次交换的位置. 利用一个变量记录最后一次交换的下标,使得带冒泡的位置为这个下标.
复杂度分析:
  • 时间复杂度: O( n 2 n^{2} n2),
  • 最好情况: 正序, 比较一趟即可,n-1次, 移动0次.
  • 最差情况: 逆序, 比较次数 ∑ i = 1 n − 1 ( n − 1 ) = 1 2 ( n 2 − n ) \sum^{n-1}_{i=1}(n-1)=\frac{1}{2}(n^2-n) i=1n1(n1)=21(n2n), 移动次数 3 2 ( n 2 − n ) \frac{3}{2}(n^2-n) 23(n2n)
代码:
void bubbleSort(int *arr,int num) {
  int tmp;
  int flag = 1; //记录一趟中是否有交换
  int lastChangeIndex = 0;
  int j,m = num - 1;
  while(m>0 && flag == 1){ //上一趟不存在交换时, 已经是有序的了
    flag = 0;
    for(j = 0; j < m; j++ ) {
      if(arr[j] > arr[j+1]){
        flag = 1;
        tmp = arr[j+1];
        arr[j+1] = arr[j];
        arr[j] = tmp;
        lastChangeIndex = j;
      }
    }
    m = lastChangeIndex; //经过一趟后, 有序子序列的前一个位置,等待冒泡
    //cout << m << endl;
  }
}

5. 归并排序

思路

两个有序的线性表合并过程

复杂度分析:
  • 时间复杂度: O( n l o g n nlogn nlogn) , 每一趟归并时间复杂度为O(n), 需要logn躺归并.
代码:
void merge(int *arr,int num, int left1,int right1,int left2,int right2) {
  int arrTmp[num];
  int k =0;
  int l1 = left1,l2 = left2;
  while(l1<=right1 && l2 <=right2) {
    if(arr[l1]>arr[l2]) {
      arrTmp[k++] = arr[l2++];
    }else{
      arrTmp[k++] = arr[l1++];
    }
  }

  while(l1 <= right1){
    arrTmp[k++] = arr[l1++];
  }
  while(l2 <= right2){
    arrTmp[k++] = arr[l2++];
  }

  for(int i = 0; i < k;i++) {
    arr[left1+i]= arrTmp[i];  //copy back to orignal table
  }
}

void helper(int *arr, int l,int r ){
  int mid;
  if(l < r) {
    mid = (l + r) / 2;
    helper(arr,l,mid);
    helper(arr,mid+1,r);
    merge(arr,r-l+1, l,mid,mid+1,r);
  }
}


void mergeSort(int *arr,int num) {
  helper(arr,0,num-1);
}

6. 快速排序

思路
  • 分制递归的思想
  • 取一个pivot值, 比它小为一个子序列, 比它大的为另一个子序列, 对每个子序列进行相同的操作.
  • pivot的取值很重要:
  1. 如果取第一个pivot = arr[left] , 当正序或者逆序的时候, 复杂度为O( n 2 n^{2} n2)
  2. pivot 取随机数, 但是随机数生成要花费时间.
  3. pivot= median(left, center, right);
复杂度分析:
  • 时间复杂度: O( n l o g n nlogn nlogn ),
  • 最好情况: O( n l o g n nlogn nlogn),每次取到中间值作为pivot.
  • 最差情况: O( n 2 n^{2} n2),每次取到极值做为pivot.
代码:
void sorter(int *arr,int left,int right) {
  int i = left,j = right, tmp;
  int pivot = arr[i];
  if(right <= left) return;
  while(i < j) {
    cout << i <<" "<< j<< endl;
    while(arr[j] >= pivot && i < j) j--; //j后面的值大于pivot
    if(i < j){
      tmp = arr[j];
      arr[j]=arr[i];
      arr[i]= tmp;
      i++;
    }
    while(arr[i] <= pivot && i < j) i++; //i前面的值小于pivot
    if(i < j){
      tmp = arr[j];
      arr[j]=arr[i];
      arr[i]= tmp;
      j--;
    }
  }
  sorter(arr,left,j-1);  //j是pivot的下标
  sorter(arr,j+1,right);
}

void quickSort(int *arr,int num){
  sorter(arr,0,num-1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值