排序

#include <iostream>
#include <stack>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cassert>

void PrintArr(int arr[], int n)
{
  for(int i = 0; i < n; ++i)
  {
    std::cout << arr[i] << " ";
  }
  std::cout << std::endl;
}

//冒泡排序
//n 为排序元素的个数
void BoubbleSort(int arr[], const int n)
{
  //冒泡的总的趟数是n-1次
  for(int i = 0; i < n -1; ++i)
  {
    //每趟冒泡前将flag置为0
    int flag = 0; 
    //每一趟则冒泡的次数是n-i-1次,后面已经排好序的元素不需要冒泡了.
    for(int j = 0; j < n - i - 1; ++j)
    {
      if(arr[j] > arr[j+1])
      {
        std::swap(arr[j], arr[j+1]);
        flag = 1;
      }
    }
    //如果某一趟冒泡中未发生交换,则说明元素都已经有序,直接退出.
    if(flag == 0)
      return;
  }
}

//选择排序
void SelectSort(int arr[], const int n)
{
  //本应该有几个书就扫描几次,但是实际上有n个数,扫描n-1次就可以了.
  for(int i = 0; i < n -1; ++i)
  {
    //标记最小元素的下标,最开始假设第一个数就是最小的.
    int min = i;
    //每次都是在未排序的元素中找一个最小的.
    //[0,i]是已经排序的,
    //[i,n)都是未排序的
    //每次找最小的数的时候要遍历到最后
    for(int j = i + 1; j < n; ++j)
    {
      //如果j下标的元素小于最小的,则更新最小数的下标.
      if(arr[j] < arr[min])
        min = j;
    }
    //每次扫描结束,把真正最小的数放到已经有序的序列后
    std::swap(arr[i], arr[min]);
  }
}

//插入排序
void InsertSort(int arr[], const int n)
{
  //在无序的元素中取一个插入到有序的序列中
  //开始取第一个元素作为有序序列
  int j = 0;
  for(int i = 1; i < n; ++i)
  {
    //有序序列[0, i-1], 无序序列[i, n)
    //取出无序序列中的第一个元素
    int tmp = arr[i];
    //升序
    //将取出的无序元素依次和有序序列中的元素比较
    j = i;
    //while(j > 0 && tmp <= arr[j-1]) //循环1
    //for(j = i; j > 0 && tmp <= arr[j-1]; --j) //循环2
    for(j = i - 1; j >= 0 && tmp <= arr[j]; --j) //循环3
    {
      //如果该元素一直小于有序列表中的元素,则将有序列表中的元素依次往后移动,
      //直到找到合适该元素的位置
      //arr[j] = arr[j-1]; //循环1\循环2
      arr[j+1] = arr[j]; //循环3
      //--j; //循环1
    }
    //找到合适该元素的位置了,则将它放入到该位置
    //arr[j] = tmp; //循环1\循环2
    arr[j+1] = tmp; //循环3
  }
}

//希尔排序
void ShellSort(int arr[], const int n)
{
  //计算分组的增量,当增量为1,则正好是对所有元素进行排序,并结束.
  for(int group = n / 2; group > 0; group /= 2)
  {
    //根据增量,对元素进行分组
    for(int i = 0; i < group; ++i)
      //对每组进行插入排序
      //每组的第一个元素是有序序列
      for(int j = i + group; j < n; j += group)
      {
        //取无序序列中的第一个元素和有序序列中的元素依次比较
        if(arr[j] < arr[j-group])
        {
          int tmp = arr[j];
          int k;
          //进行比较的时候,k是要大于该组的起始位置i.
          //这里很容易出差,网上好多都是在这里写错了.
          //如果k>0的话,就会存在越界,产生段错误.
          for(k = j; k > i && tmp <= arr[k-group]; k-= group)
          {
            arr[k] = arr[k-group];
          }
          arr[k] = tmp;
#if 0
          k = j;
          while(k > i && tmp <= arr[k-group])
          {
            arr[k] = arr[k-group];
            k -= group;
          }
          arr[k] = tmp;
#endif
        }
      }
  }
}



//快速排序
//优化:
//1.key三数取中法
//取最后一个,第一个,中间的一个,这三个数作比较,取出中间的值作为key
//为了防止代码改动,可以将取出的key先交换放到right-1的位置.
//2.可以优化递归的次数,当每个分组中的元素少于16个时,则使用插入排序.
//左右指针交换法
int Partion1(int arr[], int left, int right)
{
  int key = arr[right-1]; //取最后一个数为基准数
  int begin = left;
  int end = right - 1;
  while(begin < end)
  {
    //从左边找大于key的值,找的时候始终要保证begin < end.
    //不然会越界(基准值是最大的,且刚好是最后一个元素)
    while(begin < end && arr[begin] <= key) //找大的所以要小于等于
      ++begin;

    //从右边找小于key的值,找的时候始终要保证begin < end.
    //不然会越界(基准值是最大的,且刚好是最后一个元素)
    while(begin < end && arr[end] >= key)//找小的所以要大于等于
      --end;

    //如果2个数找到了,则交换2个数的位置
    if(begin < end) //排除循环越界退出的情况
    {
      std::swap(arr[end], arr[begin]);
     //int tmp = arr[end];
     //arr[end] = arr[begin];
     //arr[begin] = tmp;
    }
  }

  //left == begin 
  //比key大的都在右边,小的都在左边
  //key为基准的这个数放到他自己的位置
  if(end != right-1) //防止自己和自己交换,
                    //不然会进入死循环(如果找的key刚好是最大的)
    std::swap(arr[end], arr[right-1]);

  return end;
}

//左右指针挖坑法(和交换法很像)
int Partion2(int arr[], int left, int right)
{
  int key = arr[right-1];
  int begin = left;
  int end = right - 1;
  while(begin < end)
  {
    //从左找大于key的值
    while(begin < end && arr[begin] <= key)
      ++begin;
    //找到之后,用它将前面的坑填上
    if(begin < end) //确保是找到了
    {
      arr[end] = arr[begin];
      --end;
    }
    //找小于的
    while(begin < end && arr[end] >= key)
      --end;
    if(begin < end)
    {
      arr[begin] = arr[end];
      ++begin;
    }
  }

  //begin == end 
  if(end != right - 1)
    arr[end] = key;

  return end;
}

//前后指针法
int Partion3(int arr[], int left, int right)
{
  int key = arr[right-1];
  int cur = left;
  //开始时pre在cur 的后面一个位置
  //[注意]不能直接让pre = -1,因为cur的起始位置不一定在0处
  int pre = cur - 1;
  //prev 在cur 的后面,
  //让cur 一直往后走:
  //  如果cur 位置的数比key小,则让prev走一步.
  //      再比较prev和cur 是不是在同一位置,如果不在同一位置,
  //      则交换prev 和cur 处的元素.
  //      在同一位置就cur 往后走一步.
  //  如果cur位置的数比key 大,cur往后走一步.
#if 0 
  while(cur < right)
  {
    if(arr[cur] < key)
    {
      ++pre;
      if(pre != cur)
      {
        std::swap(arr[pre], arr[cur]);
      }
    }
    ++cur;
  }
#endif

  while(cur < right)
  {
    if(arr[cur] < key && ++pre != cur)
      std::swap(arr[pre], arr[cur]);

    ++cur;
  }
  
  //防止取的key刚好为最大值,刚好在最后一个位置,自己和自己交换
  if(++pre != right - 1)
    std::swap(arr[pre], arr[right-1]);

  return pre;
}

void QuickSortNotR(int arr[], int left, int right)
{
  std::stack<int> s;
  //先进后出
  //右侧先入栈
  s.push(right);
  //左侧入栈
  s.push(left);
  while(!s.empty())
  {
    left = s.top(); //取栈顶元素
    s.pop();//出栈
    right = s.top();
    s.pop();
    //只要有1个以上元素就继续
    if(left < right)
    {
      int div = Partion1(arr, left, right);
      //先将右区间入栈
      s.push(right);
      s.push(div + 1);
      //左区间入栈
      s.push(div);
      s.push(left);
    }//endif
  }//endwhile
}

void QuickSort(int arr[], int left, int right)
{
  //有2个或2个以上的元素时才进行排序
  if(right - left > 1)
  {
    //int div = Partion1(arr, left, right);
    //int div = Partion2(arr, left, right);
    int div = Partion3(arr, left, right);

    //排序左边
    QuickSort(arr, left, div);
    //排序右边
    QuickSort(arr, div+1, right);
  }
}


//归并排序

void MergData(int arr[], int left, int mid, int right, int* tmp)
{
  int beg1 = left, end1 = mid;
  int beg2 = mid, end2 = right;
  int index = left; 
  while(beg1 < end1 && beg2 < end2)
  {
    if(arr[beg1] < arr[beg2])
      tmp[index++] = arr[beg1++];
    else 
      tmp[index++] = arr[beg2++];
  }

  while(beg1 < end1)
      tmp[index++] = arr[beg1++];

  while(beg2 < end2)
      tmp[index++] = arr[beg2++];
}

void _MergSort(int arr[], int left, int right, int* tmp)
{
  if(right - left > 1)
  {
    //int mid = left + ((right - left) >> 1);
    int mid = (left + right) / 2;
    //排左半部分
    _MergSort(arr, left, mid, tmp);
    //排右半部分
    _MergSort(arr, mid, right, tmp);
    //将2个部分合并到辅助空间tmp中
    MergData(arr, left, mid, right, tmp);
    //将排好序的元素从辅助空间拷贝到原空间arr中
    memcpy(arr + left, tmp + left, sizeof(arr[0]) * (right - left));
    //下面这个是错误的, 每次拷贝的时候不都是从0开始的
    //memcpy(arr, tmp, sizeof(arr[0]) * (right - left));
  }
}


void MergSort(int arr[], int left, int right)
{
  int *tmp = (int*)malloc(sizeof(arr[0]) * right);
  if(tmp == NULL)
  {
    perror("malloc");
    return;
  }

  _MergSort(arr, left, right, tmp);

  free(tmp);
}

void MergSortNotR(int arr[], int left, int right)
{
  (void)left;
  int *tmp = (int*)malloc(sizeof(arr[0]) * right);
  if(tmp == NULL)
  {
    perror("malloc");
    //assert(tmp);
    return;
  }
  
  int gap = 1;
  while(gap < right)
  {
    for(int i = 0; i < right; i += 2 * gap)
    {
      int begin = i;
      int mid = begin + gap;
      int end = mid + gap;

      if(mid >= right)
        mid = right;
      if(end >= right)
        end = right;

      MergData(arr, begin, mid, end, tmp);
    }

    memcpy(arr, tmp, sizeof(arr[0]) * right);
    gap *= 2;
  }

  free(tmp);
}

int main()
{
  int arr[] = {2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 11, 14, 13, 15, 12};
  PrintArr(arr, (sizeof(arr)/sizeof(arr[0])));

  //BoubbleSort(arr, (sizeof(arr)/sizeof(arr[0])));
  
  //SelectSort(arr, (sizeof(arr)/sizeof(arr[0])));
  
  //InsertSort(arr, (sizeof(arr)/sizeof(arr[0])));

  //ShellSort(arr, (sizeof(arr)/sizeof(arr[0])));
  
  //QuickSort(arr, 0, (sizeof(arr)/sizeof(arr[0])));
  //QuickSortNotR(arr, 0, (sizeof(arr)/sizeof(arr[0])));

  //MergSort(arr, 0, sizeof(arr) / sizeof(arr[0]));
  MergSortNotR(arr, 0, sizeof(arr) / sizeof(arr[0]));

  PrintArr(arr, (sizeof(arr)/sizeof(arr[0])));

  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值