常用排序算法记录(C++实现)

排序过程中,对尚未确定最终位置的元素进行一遍处理,叫做“一趟”

内部排序的比较

image

插入排序

思想: 如果自己前面的元素比自己大,那么开始比较更前面的元素,放在合适的位置。

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

  int i, j;
  for(i = 1; i < n; ++i){
    if(a[i] < a[i - 1]){
      int temp = a[i];
      // 依次查看前面是不是还有比自己大的元素
      for(j = i - 1; j >= 0 && a[j] > temp; --j){
        a[j + 1] = a[j];
      } // for
      a[j + 1] = temp;
    } // if
  } // for

}

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

  int i, j;
  for(i = 1; i < n; ++i){
    if(a[i] < a[i - 1]){
      int temp = i;
      int low = 0;
      int high = i - 1;

      // 二分查找
      while(low <= high){
        int mid = (high - low) / 2 + low;
        // 为了保证插入排序稳定性,在相等时候low = mid + 1
        if(a[mid] == temp || a[mid] < temp){
          low = mid + 1;
        }else{
          high = mid - 1;
        } // else
      } // while

      for(j = i - 1; j >= low; --j){
        a[j + 1] = a[j];
      }
      a[low] = temp;
    }
  }

}

希尔排序

思想: 设定间隔d,将间隔为d的元素分为一小组,然后每次进行组内排序,并且递减d的值

冒泡排序

思想: 依次比较相邻的两个元素,如果逆序,则将他们交换交换

快速排序

思想: 采用“分治”的策略,选定一个元素为枢轴,然后所有比枢轴大的在右边,比枢轴小的在左边。

注:408原题中说,对所有尚未确定最终位置的所有元素进行一遍处理称为“一趟”排序,因此一次“划分”一趟排序。一次划分可以确定一个元素的最终位置,而一趟排序也许可以确定多个元素的最终位置。

#include <cstdio>
#include <algorithm>

int a[10];

using namespace std;

int getMid(int start, int end){

  int mid = start + (end - start) / 2;
  int Sum = a[start] + a[mid] + a[end];
  int Max = max(max(a[start], a[mid]), a[end - 1]);
  int Min = min(min(a[start], a[mid]), a[end - 1]);
  int Mid = Sum - Max - Min;
  if(Mid == a[start])
    return start;
  else if(Mid == a[mid])
    return mid;
  else
    return end - 1;

}

int partition(int start, int end){

  int pos = getMid(start, end);

  int key = a[pos];
  swap(a[pos], a[end - 1]);
  int i = start, j = end - 1;

  while(i < j){
    // 两个指针轮流遍历
    for(; i < j; ++i){
      if(a[i] > key){
        a[j] = a[i];
        break;
      } // if
    } // for

    for(; i < j; --j){
      if(a[j] < key){
        a[i] = a[j];
        break;
      } // if
    } // for
  } // while

  a[i] = key;

  return i;

}

void quickSort(int start, int end){

  if(start + 1 < end){
    int pos = partition(start, end);
    quickSort(start, pos);
    quickSort(pos + 1, end);
  }

}

int main(){

  int n;
  scanf("%d", &n);
  for(int i = 0; i < n; ++i)
    scanf("%d", a + i);
  quickSort(0, n);
  for(int i = 0; i < n; ++i)
    printf("%d%c", a[i], " \n"[i == n - 1]);
  return 0;

}

简单选择排序

思想: 每次找出待排序元素中最小的一个,与第一个交换

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

  for(int i = 0; i < n - 1; ++i){
    int min = i;
    for(int j = i + 1; j < n; ++j){
      if(a[j] < a[min]){
        min = j;
      }
      if(min != i){
        swap(a[i], a[min]);
      }
    } // for
  }

}

堆排序

堆的数组形式即是该二叉树的\(\text{bfs}\)序列

建堆

依次取数组\(\lfloor n / 2\rfloor\)(n为数组元素个数)处及之前的元素,检查其是否满足堆的要求。如果不满足,将它和更符合要求的子结点互换,不断调整。

\(\lfloor n / 2\rfloor\)(n为数组元素个数)处及之前的元素在树上表现为非叶子结点,越靠右下的非叶子结点越先调整。

排序

思想: 先建立堆,然后取堆顶元素与最后一个待排序元素进行交换,再调整堆,重复此过程,直到进行完\(n-1\)趟停止(相当于不断从优先队列中取元素)

时间复杂度:\(\text{O(n) + O(n}\log_{2}\text{n)}\)

空间复杂度:\(\text{O(1)}\)

是不稳定排序

归并排序

思想: 采用“分治的方法”,将待排序数组细分成各个只有两个元素的数组,然后不断进行有序合并以完成排序

公式:

对于2路归并排序中,待排序元素为\(\text{n}\)个,则需要进行\(\lceil \log_2n \rceil\)趟归并排序

对于\(N\)个元素进行\(k\)路归并排序时,排序的趟数\(m\)满足\(k^m = N\),从而\(m = \log_k N\),又因为\(m\)是整数,所以\(m = \lceil \log_kN \rceil\)

void myMerge(int a[], int left, int right){

  int mid = left + (right - left) / 2;
  int nl = mid - left, nr = right - mid;
  int l[nl], r[nr];
  for(int i = 0; i < nl; ++i)
    l[i] = a[left + i];
  for(int i = 0; i < nr; ++i)
    r[i] = a[mid + i];

  int k = left, i = 0, j = 0;
  while(i < nl && j < nr && k < right){
    if(l[i] <= r[j]){
      a[k] = l[i];
      ++i, ++k;
    }else{
      a[k] = r[j];
      ++j, ++k;
    } // else
  } // while

  while(i < nl && k < right){
    a[k] = l[i];
    (void) (++i), ++k;
  } // while
  while(j < nr && k < right){
    a[k] = r[j];
    (void) (++j), ++k;
  } // while

}

void mergeSort(int *a, int left, int right){

  if(left + 1 < right){
    int mid = left + (right - left) / 2;
    mergeSort(a, left, mid);
    mergeSort(a, mid, right);
    myMerge(a, left, right);
  }
}

基数排序(桶排序)

思想: 首先按照最低位大小进行排序,然后再按照次高位进行排序,如此往复,直到最高位排序完成(即\(\text{LSD}\)最高位优先原则)

假定需要\(\text{r}\)个队列(\(r\)个队头指针\(r\)个队尾指针),进行\(\text{d}\)趟分配和收集

时间复杂度:\(\text{O(d(n + r))}\)

空间复杂度:\(\text{O(r)}\)

posted @ 2023-01-02 22:21  Frodo1124  阅读(22)  评论(0编辑  收藏  举报