目录
前言
上期我们对快速排序进行了分析,了解了它的思想,原理和实现代码及它的特性。今天我们来了解一下插入排序中的直接插入排序。
什么是直接插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动
直接插入排序的实现
基本思想
1. 每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
2. 第一趟比较前两个数,然后把第二个数按大小插入到有序表中
3. 第二趟把第三个数据与前两个数从后向前扫描,把第三个数按大小插入到有序表中;
4. 依次进行下去,进行了(n-1)趟以后就完成了整个排序过程。
具体代码
#include <stdio.h> //直接插入排序 void InsertSort(int* arr, int len) { //定义变量用来循环 int i = 0; int j = 0; //哨兵变量 即用来存放需要查入的元素 int tmp = 0; //第一层循环 循环len-1次 //因为当数组只有一个元素时它就是有序的不用比较 //所以从第二个元素开始比较 即i=1 for (i = 1; i < len; i++) { //将需要插入的元素放入tmp中 tmp = arr[i]; //第二层循环 和要插入的元素比较 //要比较的就是插入元素前面的 即j=i-1 for (j = i - 1; j >= 0; j--) { //如果插入元素前一个大于插入元素就将它们交换 if (arr[j] > arr[j + 1]) { arr[j + 1] = arr[j]; arr[j] = tmp; } //如果不大于插入元素的时候就跳出 //因为要插入元素前面的子数组的元素已经是有序的 //只要大于子数组后面的元素,前面的就不用比较了 else break; } } } int main() { //待排序数组 int arr[] = { 3,8,7,2,6,9,1,4 }; //直接插入排序函数 InsertSort(arr, sizeof(arr) / sizeof(arr[0])); //打印 int i = 0; for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } return 0; }
直接插入排序的原理
直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的趟术和数值。内层循环为待比较数值确定其最终位置。直接插入排序将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较,直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。
这里我们以代码中的数组为例:我们将数组{ 3,8,7,2,6,9,1,4}进行升序。我们将3元素从无序表中拿出来放到有序表中,有序表中无元素,它仍然有序。注意(比较中是从后往前,比较的数组是有序的,如果待插入的元素大于比较元素,则比较元素前面的就不用比较了。)这时开始正式比较,第一次比较8大于3,不用交换直接插入,有序表3,8。第二次比较7小于8交换,7大于3不交换,直接插入,有序表3,7,8。第三次比较2小于8交换,2小于7交换,2小于3交换,前面没有元素了,插入。有序表2,3,7,8。第四次交换比较6小于8交换,6小于7交换,6大于3不交换,插入。有序表2,3,6,7,8。第五次交换比较9大于6不交换,直接插入。有序表2,3,6,7,8,9。第六次交换比较1小于9交换,1小于7交换,1小于6交换,1小于3交换,1小于2交换。有序表1,2,3,6,7,8,9。第7次交换比较4小于9交换,4小于8交换,4小于7交换,4小于6交换,第五次交换比较9大于6不交换,直接插入。有序表2,3,6,7,8,9。第六次交换比较1小于9交换,1小于7交换,1小于6交换,1小于3交换,1小于2交换。有序表1,2,3,6,7,8,9。第7次交换比较4小于9交换,4小于8交换,4小于7交换,4小于6交换,4大于3不交换,直接插入,有序表1,2,3,4,6,7,8,9。这时数组就已经变成有序的了。
画图分析:
直接插入排序的特点和性能
复杂度
直接插入排序的时间复杂度为 O (n^2) ,空间复杂度为 O (1) 。 在排序过程中,如果待排序的序列已经近似有序,则直接插入排序的效率会很高,因为少需移动已排序部分的元素。但如果待排序序列的规模很大或者是随机分布的,那么直接插入排序的效果会不如其他高效率排序算法,如快速排序等。
稳定性
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。如果碰见一个和插入元素相等的,那么将会把待插入元素放在相等元素的后面。所以,相等元素的相对的前后顺序没有改变,所以插入排序是稳定的。
二分查找代码的应用
下面还有一个插入排序中的二分查找,大家可以看看
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #define MaxSize 100 #define ElemType int #define Status int using namespace std; //顺序表数据结构 typedef struct { ElemType data[MaxSize];//顺序表元素 int length; //顺序表当前长度 }SqList; //***************************基本操作函数*******************************// //初始化顺序表函数,构造一个空的顺序表 Status InitList(SqList &L) { memset(L.data,0,sizeof(L));//初始化数据为0 L.length=0; //初始化长度为0 return 0; } //创建顺序表函数 初始化前n个数据 bool CreatList(SqList &L,int n) { if(n<0||n>MaxSize)false;//n非法 for(int i=0;i<n;i++) { scanf("%d",&L.data[i]); L.length++; } return true; } //插入函数 位置i插入数据 i及之后元素后移 1=<i<=length+1 bool InsertList(SqList &L,int i,ElemType e) { if(i<1||i>L.length+1) //判断位置是否有效 { printf("位置无效!!!\n"); return false; } if(L.length>=MaxSize)//判断存储空间是否已满 { printf("当前存储空间已满!!!\n"); return false; } for(int j=L.length;j>=i;j--)//位置i及之后元素后移 { L.data[j]=L.data[j-1]; } L.data[i-1]=e; L.length++; return true; } //删除函数 删除位置i的元素 i之后的元素依次前移 bool ListDelete(SqList &L,int i) { if(i<1||i>L.length) { printf("位置无效!!!\n"); return false; } for(int j=i;j<=L.length-1;j++)//位置i之后元素依次前移覆盖 { L.data[j-1]=L.data[j]; } L.length--; return true; } //查找函数 按位置从小到大查找第一个值等于e的元素 并返回位置 int LocateElem(SqList L,ElemType e) { for(int i=0;i<L.length;i++)//从低位置查找 { if(L.data[i]==e) return i+1; } return 0; } //二分查找函数 int Binary_search(SqList L,ElemType key) { int low = 0;int mid = 0;int high = L.length-1; while(low<=high) { mid = (low+high)/2; if(key==L.data[mid]) { return mid; } else if(key>L.data[mid]) { low = mid+1; } else { high = mid-1; } } return -1; } //********************************功能函数*****************************************// //输出功能函数 按位置从小到大输出顺序表所有元素 void PrintList(SqList L) { printf("当前顺序表所有元素:"); for(int i=0;i<L.length;i++) { printf("%d ",L.data[i]); } printf("\n"); } //创建顺序表函数 void Create(SqList &L) { int n;bool flag; printf("请输入要创建的顺序表长度(>1):"); scanf("%d",&n); printf("请输入%d个数(空格隔开):",n); flag=CreatList(L,n); if(flag){ printf("创建成功!\n"); PrintList(L); } else printf("输入长度非法!\n"); } //插入功能函数 调用InsertList完成顺序表元素插入 调用PrintList函数显示插入成功后的结果 void Insert(SqList &L) { int place;ElemType e;bool flag; printf("请输入要插入的位置(从1开始)及元素:\n"); scanf("%d%d",&place,&e); flag=InsertList(L,place,e); if(flag) { printf("插入成功!!!\n"); PrintList(L); } } //删除功能函数 调用ListDelete函数完成顺序表的删除 调用PrintList函数显示插入成功后的结果 void Delete(SqList &L) { int place;bool flag; printf("请输入要删除的位置(从1开始):\n"); scanf("%d",&place); flag=ListDelete(L,place); if(flag) { printf("删除成功!!!\n"); PrintList(L); } } //查找功能函数 调用LocateElem查找元素 void Search(SqList L) { ElemType e;int flag; printf("请输入要查找的值:\n"); scanf("%d",&e); flag=LocateElem(L,e); if(flag) { printf("该元素位置(从1起)为:%d\n",flag); } else printf("未找到该元素!\n"); } //简单选择排序功能函数 为二分做准备 void SelectSort(SqList &L) { int min;int temp; for(int i=0;i<L.length;i++) { min=i; for(int j=i+1;j<L.length;j++) { if(L.data[j]<L.data[min])min=j; } if(min!=i) { temp=L.data[min]; L.data[min]=L.data[i]; L.data[i]=temp; } } } //二分查找功能函数 调用Binary_search void Binary(SqList L) { int key;int place; SelectSort(L); //二分查找前先排序 PrintList(L); printf("请输入要查找的值:\n"); scanf("%d",&key); place=Binary_search(L,key); if(place>=0)printf("位置(从0起)为:%d\n",place); else printf("未找到!\n"); } //菜单 void menu() { printf("********1.创建 2.插入*********\n"); printf("********3.删除 4.查找*********\n"); printf("********5.输出 6.二分查找*********\n"); printf("********7.退出\n"); } int main() { SqList L;int choice; InitList(L); while(1) { menu(); printf("请输入菜单序号:\n"); scanf("%d",&choice); if(7==choice) break; switch(choice) { case 1:Create(L);break; case 2:Insert(L);break; case 3:Delete(L);break; case 4:Search(L);break; case 5:PrintList(L);break; case 6:Binary(L);break; default:printf("输入错误!!!\n"); } } return 0; }