排序算法

1.冒泡排序

  比较相邻的元素,如果第一个比第二个大,则交换这两个的值,对每一对相邻的元素进行同样的操作,从第一对到最后一对,这样最大的元素就到最后面了。对于所有的元素再次进行同样的操作,不断重复,最后就会完成排序。

原理图:

void sort(int *a,int size)
{
    int i,j,tmp;
    for(i=0;i<size-1;i++)
    {
        for(j=0;j<size-i-1;j++)
        {
            if(a[j] > a[j+1])
            {
                tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
        }
    }
}

2.选择排序

  选定第一个元素作为标志位,后面的元素插进来时都要与它进行比较,如果是升序排序的话,比它大的就放后面,比它小的就放前面,将标志位改为比它小的元素,始终保证最前面的元素是最小的;降序排序就反过来,比它小的直接放后面,比它大的放前面,再将标志位改为比它大的元素。从第一个元素开始为标志位,下一轮的标志位是第二个元素(前面的已经排好序了),之后第三个,第四个....直到最后一个。这样就排好序了。

//选择排序--升序
void select_sort(int *arr,int size)
{
  for(int i=0;i<size;i++)
  {
    for(int j=i+1;j<size;j++)
    {
      if(arr[i] > arr[j])
      {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
      }
    }
  }
}

3.插入排序

  插入排序比较直观一点,跟选择排序有点类似,第二个元素与第一个元素进行比较,大的放后面,小的放前面,这样前面两个就已经排好序了,第三个元素就从第一个元素开始比较,比第一个小就放在前面,比第二个大就放在后面,否则就放在中间,之后的每一个元素都是这样,分别与前面已经排好的每一个元素进行比较,最后放在比它小并且后面的值比它大的位置。(升序)

void insert_sort(int *a,int size)
{
    int i,j,tmp;
    for(i=1;i<size;i++)
    {
        if(a[i]<a[i-1])
        {
            tmp = a[i];
            for(j=i-1;j>=0 && a[j] > tmp;j--)
            {
                a[j+1] = a[j];
            }
            a[j+1] = tmp;
        }
    }
}

 4.快速排序

  基本思想:第一遍排序将数列分成两个部分,前半部分比中间的key值小,后半部分比中间的key值大,然后再在两部分之间再找个key值,再分别进行排序,不断的分成两部分,最后排序完成。

  步骤:从数列中挑出一个key值(随便选),分别从数列两端开始比较,i、j分别指向数列第一个和最后一个元素,第i个元素如果比key小,则i指向下一个元素,直到指向一个比key大的元素,然后把该元素跟key交换位置,交换之后,key还是原来的值,只是位置变了;接下来i不动,从最后一个元素开始,j指向的元素跟key比较,元素比key小,就交换位置,比key大则j指向前面一个元素,直到遇到比key小的元素;交换之后又从i开始....i跟j轮流开始,直到i=j,在把key的值填入这个位置,这样整个数列就以key分成两半了。接下来用同样的方法,在前半部和后半部分别找个key进行排序。

int quick_sort(int *a,int low,int high)
{
  if(low < high)
  {
    int i = low;
    int j = high;
    int k = a[low];
    
    while(i < j)
    {
      while(i < j && a[j] >= k)
      {
        j--;
      }
      if(i < j)
      {
        a[i++] = a[j];
      }
      while(i < j && a[i] < k)
      {
        i++;
      }
      if(i < j)
      {
        a[j--] = a[i];
      }
    }
    a[i] = k;
    //递归调用
    quick_sort(a,low,i-1);
    quick_sort(a,i+1,high);
  }
}

 5.希尔排序

  基本思想:希尔排序实际上是插入排序的改进版,在插入排序的基础上增加了分组,会优先比较距离比较远的元素,又被称为缩小增量排序。

  步骤:按一定的间隔进行划分数列,然后在每一组中进行插入排序,第一次的间隔为size/n,即每隔(size/n)的距离取一个数字,将这些数字分为一组,就有很多组,分别进行快速排序,这些数组在原来的大的数列中的位置并没有变,只是每一小组中的位置换了而已,例如:第一组的位置是第1、4、7个位置,这个小组内部排好序之后的数字也是按顺序填入这三个位置中。第二次为size/2n...直到间隔为1,之后再排一次插入排序就可以了。

void shell_sort(int a[],int n)
{
  int i,j,k;
  int tmp,gap;
  for(gap = n/2;gap > 0;gap /=2)//选取步长
  {
    for(i = 0;i < gap;i++)//直接插入排序
    {
      for(j = i+gap;j < n;i+=gap)//每次都加上步长
      {
        if(arr[j] < arr[j-gap])
        {
          tmp = arr[j];
          k = j-gap;
          while(k >= 0 && arr[k] > tmp)//记录后移
          {
            arr[k+gap] = arr[k];
            k-=gap;
          }
        arr[k+gap] = tmp;//找到位置插入
      }
    }
  }
}

6.归并排序

  基本思想:归并排序是建立在归并操作上的排序算法,将数列分成很多的小的数列,将小数列内部排好序之后,再两两合并成多个数列,这些数列内部再进行排序,再两两合并...最后排好序了,其他排序都不需要额外申请空间,归并排序需要申请空间。

  步骤:将长度为n的数列分成两个长度为n/2的子数列,将这两个子数列再进行拆分,变成四个子数列,这四个子数列内部排好序后,前两个跟后两个子数列分别合并并且排序,得到两个排好序的子数列,再进行合并并且排序,就得到一个完整的排好序的数列了。

//分组归并
void merge(int arr[],int begin1,int end1,int begin2,int end2,int tmp[])
{
  int k = begin1;
  int i = begin1,j = begin2;
  while(i <= end1 && j <= end2)
  {
    if(a[i] <= a[j])
      tmp[k++] = a[i++];
    else
      tmp[k++] = a[i++];
  }
  //将左边的元素放到tmp中
  while(i <= end1)
    tmp[k] = a[i++];
  while(j <= end2)
    tmp[k++] = a[j++];
  //将tmp中的数据拷贝到愿数组中相应的区间
  memcpy(a+begin1,tmp+begin1;sizeof(int)*(end2-begin1+1));
}

//归并排序
void merge_sort(int *a,int ldft,int dight,int *tmp)
{
  if(lefi > = right)return;
  //mid将数组一分为二
  int mid = left + ((right - left)>>1);
  //左边归并排序,不断递归
  mergsort(a,left,mid,tmp);
  //右边归并排序,不断递归
  mergsort(a,mid+1,right,tmp);
  //合并两个子数组
  merge(a,left,mid,mid+1,right,tmp);
}

7.二叉树排序

  基本思想,创建一个二叉树序树(把第一个数字放在根节点,取后面的数字与根节点比较,大的放右边,小的放左边),然后中序遍历得到有序数列。

#include <stdio.h>
#include <sys/time.h>
#include <stdbool.h>
#include <stdlib.h>

int get_random_data(int type)
{
    //1.获取系统时间
    struct timeval tv;
    gettimeofday(&tv, NULL);
    //设置随机数因子
    srandom(tv.tv_usec);
    return random()%type;
}

//设计一个树结构
typedef struct BTree{
    int data;
    struct BTree *lchild;
    struct BTree *rchild;
}Bitree;

Bitree* create_tree_node(int data)
{
    Bitree * root = (Bitree*)malloc(sizeof(Bitree));
    root->lchild = NULL;
    root->rchild = NULL;
    root->data = data;
    return root;
}

Bitree *create_sort_tree(Bitree* root,  int data)
{
    if(root == NULL)
    {
        root = create_tree_node(data);
        return root;
    }

    //root不为空
    if(data > root->data)//把data插入到右边
    {
        root->rchild = create_sort_tree(root->rchild, data);
    
    }
    else//插入根的左边
    {
        root->lchild = create_sort_tree(root->lchild, data);
    }
    
    return root;
}

void mid_tree(Bitree* root)
{
    if(root == NULL)return ;

    if(root->lchild !=  NULL)
    {
        mid_tree(root->lchild);
    }
    printf("%d ", root->data);
    if(root->rchild != NULL)
    {
        mid_tree(root->rchild);
    }
}


int main(void)
{
    Bitree *root = NULL;
    for(int i=0; i<100; i++)
    {
        int data = get_random_data(100);
        printf("%d ", data);
        root = create_sort_tree(root, data);
    }
    printf("
");


    //通过中序号遍历
    mid_tree(root);
    printf("
");
    return 0;
}

还有什么堆排序、计数排序、桶排序,都太难了,我不会,就不写了。

8.各种排序的复杂度如下图所示(下面这部分是引用自某不认识大佬的博客的图图片,地址贴在后面)

 稳定:如果a原本在b的前面,而a=b,排序之后a仍然在b的前面。

不稳定:同上,但是排序后a在b的后面。

时间复杂度:对排序算法的总操作次数,反应出当要排序的数列长度n变化时,操作次数呈现的规律。

空间复杂度:指的是算法在计算机内执行的时候所需的存储空间的度量,也是数据规模为n的函数。

PS:如果我哪里写错了,请指正,互相学习一下。

复杂度的那部分内容转自:https://www.cnblogs.com/onepixel/articles/7674659.html

原文地址:https://www.cnblogs.com/smallqizhang/p/12439313.html