5.常见排序算法思想
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的数据元素调整为“有序”的数据元素。
内部排序
1 插入排序
插入排序通常分为:直接插入排序和折半插入排序,通常我们所说的插排,就是直接插入排序。
算法思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成。
实现思路:
-
第一个数据认定为已排序好的数据,从第二个数据开始,进行插入操作
-
第一轮取出第二个数据,作为待插入数据,在已经排序好的序列中从后往前向前扫描,找到合适的插入位置
-
插入数据,此时已排序好的序列元素+1
-
第二轮同理,给第三个数据找到插入位置,并插入
-
依次类推,直到排序结束
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//从小到大
void insertSort(int arr[] , int len)
{
for (int i = 1; i < len;i++)
{
if (arr[i] < arr[i-1])
{
int temp = arr[i]; //将待插入元素,保存在临时的temp中
int j = i - 1;
for (; j >= 0 && temp < arr[j];j--)
{
//数据进行后移
arr[j + 1] = arr[j];
}
//将j+1 的位置数据 赋值为 temp
arr[j + 1] = temp;
}
}
}
void printArray(int arr[], int len)
{
for (int i = 0; i < len;i++)
{
printf("%d\n", arr[i]);
}
}
void test01()
{
int arr[] = { 5, 9, 6, 1, 3, 7 };
int len = sizeof(arr) / sizeof(int);
insertSort(arr, len);
//打印数组
printArray(arr,len);
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
插入排序改版
改版思想:
-
整体实现思想和插排相同,都是将数据分为两个区域,已排好的区域和待排序的区域
-
区别在于,插入排序传统写法是先找到带插入数据位置,再进行插入
-
而改版的方式通过两两数据交换,进行数据的定位
void insertSort2(int arr[] , int len)
{
for (int i = 1; i < len;i++)
{
for(int j=i;j>=1;j--)
{
if(arr[j]<arr[j-1])
{
int temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
else
{
break;
}
}
}
}
折半插入排序
算法简介:直接插入排序时,查找插入位置时需要逐个对比,折半插入是利用二分的思想,查找插入位置
void insertSort(int arr[],int len)
{
for(int i=1;i<len;i++)
{
//将待插入的数据,保存在临时变量temp中
int temp =arr[i];
//二分查找插入位置low和high下标的声明
int low=0;
int high=i-1;
while(low<=high)
{
//计算中间位置
int mid =(low+high)/2;
if(temp<arr[mid])
{
high=mid-1;
}
else
{
low=mid+1;
}
}
//通过while循环后,找到了插入的位置,这个就是high+1的位置
for(int j=i-1;j>=high+1;j--)
{
//数据元素后移
arr[j+1]=arr[j];
}
//插入数据
arr[high+1]=temp;
}
}
插入排序算法总结
直接插入与折半插入对比:
-
原始数据越接近有序,直接插入排序速度越快,折半没有这个性质
-
折半查找比顺序查找快,所以折半插入平均性能来说比直接插入排序要快
-
当n较大时,总关键码比较次数比插入排序的最坏情况要好很多,但比最好情况要差
-
折半插入排序的数据移动次数与直接插入排序相同,依赖于数据的初始排列
-
减少了比较次数,但没有减少移动次数
-
平均性能优于直接插入排序
-
2 冒泡排序
基本思想:**每轮不断将元素进行两两比较,并按前小后大规则交换**(以升序为例)
实现思路:
-
比较相邻元素,若前者大于后者,两元素进行交换
-
对每组相邻元素,实现上述步骤,在第一轮结束后,最后一个元素即为最大值
-
重复上述步骤,每次比较次数减1,直到无须比较,排序结束
代码实现:
#include<iostream>
using namespace std;
//打印数组
void PrintArray(int arr[],int len)
{
for(int i=0;i<len;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
//冒泡排序
void BubbleSort(int arr[],int len)
{
//外层循环len-1次
for(int i=0;i<len-1;i++)
{
//内层循环len-i-1
for(int j=0;j<len-i-1;j++)
{
//相邻元素,前者大于后者,进行交换
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
PrintArray(arr,len);
}
}
//冒泡排序优化版本
void BubbleSort2(int arr[],int len)
{
bool flag=true; //true代表交换过 false代表未交换
//外层循环len-1次
for(int i=0;i<len-1&&flag==true;i++)
{
flag=false; //每轮初始化转态为真
//内层循环len-i-1
for(int j=0;j<len-i-1;j++)
{
//相邻元素,前者大于后者,进行交换
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if (flag ==false)
{
break;
}
PrintArray(arr,len);
}
}
void test01()
{
int arr[8]={4,3,10,34,0,123,12,13};
//数组中元素的个数
int len =sizeof(arr)/sizeof(int);
//调用冒泡排序
BubbleSort(arr,len);
//打印数组
PrintArray(arr,len);
}
int main()
{
test01();
return 0;
}
3 选择排序
基本思想:在待排序的数据中选出最大(小)的元素放在其最终的位置
实现思路:
-
第一轮认定第一个值就是最小值,并记录下标为min,然后从剩余的n-1个元素如果有比min下标还小的值,更新min
-
如果min最终不是第一个值,第一个值与min指向的值互换,否则不互换
-
通过第一轮结束,第一个值为最小值
-
第二轮用同样的方式找第二个最小值,依次类推,直到排序结束
#include<iostream>
using namespace std;
//打印数组
void PrintArray(int arr[],int len)
{
for(int i=0;i<len;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
//选择排序
void SelectSort(int arr[],int len)
{
//外层循环len-1次
for(int i=0;i<len-1;i++)
{
int min=i; //认定当前第i个元素就是最小值
for(int j=i+1;j<len;j++)
{
if(arr[j]<arr[min]) //如果后面的元素更小,更新最小值下标
{
min=j; //最小值下标更新
}
}
if(i!=min)
{
int temp=arr[i];
arr[i]=arr[min];
arr[min]=temp;
}
}
}
int main()
{
int arr[8]={4,3,10,34,0,123,12,13};
//数组中元素的个数
int len =sizeof(arr)/sizeof(int);
//调用冒泡排序
SelectSort(arr,len);
//打印数组
PrintArray(arr,len);
return 0;
}
4 希尔排序
基本思想:
将整个排序序列分割若干子序列,分别进行直接插入排序待整个序列中的数据基本有序时,再对全体数据进行一次直接插入排序
实现思路:
-
以增量序列5、3、1为例,进行分割子序列
-
第一轮增量为5,将数组分为3段
-
每段取出一个元素,组成新的小组,并进行小组内的插入排序
-
第二轮增量为3,将数组分为5段
-
每段取出一个元素,组成新的小组,并进行小组内的插入排序
-
最后一轮增量为1,直接做插入排序,排序完毕
小结:
-
一次移动,移动位置较大,跳跃式地接近排列后的最终位置
-
随着增量的减小,每一轮排序完毕后数组越来越接近完美序列
-
最后一次只需要少量移动
-
增量序列必须是递减的,最后一个必须是1
-
增量序列应该是互质的
5 快速排序
基本思想:
通过一趟排序将数据划分为独立的两个部分,即前半区和后半区。其中,前半区中的数据均不大于后半区的数据,然后分别对这两部分记录继续进行快速排序,直到整个序列有序。
实现思路:
-
先从数列中挑出一个数据,做为基准值(通常第一个元素),pivot:枢轴、中心点
-
所有比基准值小的数据放在前面,比它大的数据放在后面,形成左右两个子表。(相同的数可以放任一边)
-
通过一轮排序,基准值找到其最终位置。
-
对各子表重新选择基准值,并重复上述操作(递归),直到排序结束。
小结:
-
选定一个基准值作为参考,所有元素与之比较,小的调到其左边,大的调到其右边。
-
每一趟排序的过程是采用从两头向中间交替式逼近法。
-
由于每趟中对各子表的操作都相似,可采用递归算法。
6 归并排序
归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将两个或多个已经有序的序列合并成一个;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
7 堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似 完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
8 基数排序
基于关键字各位的大小进行排序