常见排序算法总结

1、常见的数据结构排序算法总结如下:


<span style="font-size:18px;">#include"iostream"

using namespace std;


/*
一、直接插入排序
基本思想:
前i-1个数是有序的,将第i个数插入到合适的位置。
1、设置标记temp = arr[i];
2、对i进行插入排序,从j=i-1个数开始向前查找,
若arr[j] > temp ,则 arr[j+1] = arr[j];
直到循环结束,arr[j+1]=temp;即找到第i个元素
的插入位置是j+1.
时间复杂度O(n^2)
空间复杂度O(1)
是否稳定:是
*/

void InsertSort(int arr[],int length)
{
 int temp, j;
 for (int i = 1; i < length; i++)
 {
  temp = arr[i];//标记要插入的数
  j = i - 1;//从i-1的位置开始,从后往前查找
  while (j >= 0 && arr[j] > temp)//这个判断条件保证稳定
  {
   arr[j + 1] = arr[j];
   j--;
  }
  arr[j + 1] = temp;
 }
}
/*==========================================*/
/*
二、折半插入排序

基本思想:将直接插入排序的查找过程改为折半查找,
查找的速度更快,但元素移动的次数没有改变

时间复杂度O(n^2)
空间复杂度O(1)
是否稳定:是
*/

void BinaryInsertSort(int arr[], int length)
{
 int low = 0, high = 0, mid = 0, temp = 0;
 for (int i = 1; i < length; i++)
 {
  temp = arr[i];
  low = 0;
  high = i - 1;
  while (low<=high)
  {
   mid = (low + high) >> 1;
   if (arr[mid] >= temp)
    high = mid - 1;
   else
    low = mid + 1;
  }
  //上述二分查找返回的位置是最左边的最接近的数
  //low的位置 即为插入的位置
  for (int j = i - 1; j >= low; j--)
  {
   arr[j + 1] = arr[j];
  }
  arr[low] = temp;
 }
}
/****************************************************/

/*
三、希尔排序
基本思想:希尔排序本质上是分组插入排序,在直接插入排序中
当待排序的记录基本有序的情况下,时间复杂度为O(n)。
希尔排序的思想是:使得待排序的记录先基本有序,然后再排序。
时间复杂度:O(n的1.5次方)
空间复杂度:O(1)
是否稳定:否

希尔排序算法步骤:
1、选择一个增量序列:t1,t2,t3,...,tk,其中t1>t2,tk=1;
2、按增量序列个数k,对序列进行k趟直接插入排序;
3、每趟排序,根据对应的增量dk,将待排序将待排序列分割
成若干长度为m 的子序列,分别对各子表进行直接插入排序。
仅增量因子为1 时,整个序列作为一个表来处理,
表长度即为整个序列的长度。
*/
//注意对比希尔排序 与 直接插入排序 的区别!!!
void shellInsert(int arr[], int length, int dk)
{
 int temp, j;
 for (int i = dk; i < length; i++)
 {
  temp = arr[i];
  j = i - dk;
  while (j>=0 && arr[j] > temp)
  {
   arr[j + dk] = arr[j];
   j = j - dk;
  }
  arr[j + dk] = temp;
 }
}

void ShellSort(int arr[], int length, int dlta[], int dltaLength)
{
 for (int i = 0; i < dltaLength; i++)
 {
  shellInsert(arr, length, dlta[i]);
 }
}
/********************************************************/

/*
四、冒泡排序
基本思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的
两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,
比较相邻的两个数据,(以升序为例)如果大就交换,
得到一个最大数据在末尾;
然后进行第二趟,只扫描前n-1个元素,
得到次大的放在倒数第二位。以此类推,最后得到升序序列。
如果在扫描过程中,发现没有交换,说明已经排好序列,
直接终止扫描(优化)。所以最多进行n-1趟扫描。
时间复杂度:O(n^2)
空间复杂度:O(1)
是否稳定:是
*/

void BubbleSort(int arr[], int length)
{
 int temp;
 bool change = true;

 for (int i = 0; i < length - 1 && change; i++)
 {
  change = false;
  for (int j = 0; j < length - 1- i; j++)
  {
   if (arr[j + 1] < arr[j])
   {
    change = true;
    temp = arr[j];
    arr[j] = arr[j + 1];
    arr[j + 1] = temp;
   }
  }
 }
}
/*******************************************/

/*
五、快速排序
基本思想:基本思想:冒泡排序每次只能消除一个逆序,
若要一次消除多个逆序,选择一个元素作为轴,
将序列分成左右两边,使得左边的序列都小(等)于轴,
右边的序列都大(等)于轴。对左右两个子序列继续划分,
直到序列有序。(二分二分二分...Duang~Duang~Duang)

选轴
分割子序列

递归过程(参照代码,三句话):
1.选轴
2.对左半部份做快速排序
3.对右半部份做快速排序
时间复杂度:平均O(n㏒n),最坏O(n²)。
空间复杂度:O(㏒n)
是否稳定:否{3, 2, 2}
*/

//划分过程
int QPartition(int arr[], int low, int high)
{
 int pivotloc = low;
 int temp = arr[pivotloc];

 while (low < high)
 {
  while (low < high && arr[high] >= temp)
   high--;
  if (low < high)
  {
   arr[low] = arr[high];
   low++;
  }

  while (low < high && arr[low] <= temp)
   low++;
  if (low < high)
  {
   arr[high] = arr[low];
   high--;
  }
 }
 arr[low] = temp;
 return low;
}

//递归过程
void QSort(int arr[], int low, int high)
{
 if (low < high)
 {
  int pivotloc = QPartition(arr, low, high);
  QSort(arr, low, pivotloc - 1);
  QSort(arr, pivotloc + 1, high);
 }
}

//快速排序
void QuickSort(int arr[], int length)
{
 QSort(arr, 0, length - 1);
}

/***********************************************/
/*
六、堆排序

基本思想:把待排序记录的关键字存放在数组r[1…n](注意在树的顺序表表示中,数组下标要从1开始)中,将r看成是一棵完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,其余记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
由无序序列建堆:待排序元素从下标1开始存储,调整堆从第n/2个元素(最后一个非叶子节点)开始向上调整,建堆完成后,形成大顶堆。
交换堆顶和最后一个元素后,重新调整堆:待排序列范围-1,然后重新调整[1,length],只需调整堆顶即可(其余部分仍然是大顶堆)。
时间复杂度:O(nlogn),最坏情况下也是O(nlogn)
空间复杂度:O(1)
是否稳定:否{5,5,3}

分析:堆排序是一种比较复杂的排序方法,很容易在笔试、面试中直接考察排序的代码。因此,要认真掌握,达到可以默写的程度才可以。剖析一下,堆排序的代码该怎么写。

实际上堆排序的过程可以分为两步:
1. 由n个元素的无序序列建大顶堆。实际上是从i/2(最后一个非叶子节点的下标是i/2)处向上调整堆,直到根节点。
调整完后就得到一个大顶堆。
2. 将根节点(下标1)和最后一个节点(下标n)交换后,此时最大的元素一定位于数组末尾(下标n),
除根节点外,其余的子树依然保持大顶堆的性质(节点1~n-1),因此只需要调整根节点就可以了。


可见,1,2两步都是在调整堆,只是调整的范围不一样:建堆时需要调整所有的非叶子节点,而选出一个最大元素后,
只需调整剩下元素中的根节点就可以。我们设调整堆的函数为 void HeapAdjust(int Record[], int begin, int end

根据以上分析,我们容易写出下面的代码:


*/
void HeapAdjust(int arr[], int begin, int end)
{
 int temp = arr[begin];

 //循环变量i记录当前调整节点的左子树下标
 for (int i = begin * 2; i < end; i *= 2)
 {
  //记下左右孩子节点中的最大值
  if (i < end && arr[i] < arr[i + 1])
   i++;
  //如果当前待调整节点比其左右孩子都大,无需调整
  if (temp >= arr[i])
   break;
  //交换较大值到调整节点
  arr[begin] = arr[i];
  //继续向下调整子树
  begin = i;
 }
 arr[begin] = temp;
}

void HeapSort(int arr[], int length)
{
 //下标从0开始,建堆
 for (int i = length / 2; i >= 0; i--)
  HeapAdjust(arr, i, length-1);
 for (int j = length-1; j > 0; j--)
 {
  int temp = arr[0];
  arr[0] = arr[j];
  arr[j] = temp;
  HeapAdjust(arr, 0, j - 1);
 }
}


int main(void)
{
 int arr[] = { 3, 2, 4, 6, 4, 1, 7, 5 };
 int dlta[] = { 3, 2, 1 };
 //InsertSort(arr, 8);
 //BinaryInsertSort(arr, 8);
 //ShellSort(arr, 8, dlta, 3);
 //BubbleSort(arr, 8);
 //QuickSort(arr, 8);
 HeapSort(arr, 8);
 for (int i = 0; i < 8; i++)
  cout << arr[i] << " ";
 cout << endl;
 return 0;
}</span><span style="font-size:18px;">
</span>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值