作者主页:paper jie的博客_CSDN博客-C语言,算法详解领域博主
本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。
本文录入于《算法详解》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将算法基础知识一网打尽,希望可以帮到读者们哦。
其他专栏:《系统解析C语言》《C语言》《C语言-语法篇》
内容分享:本期将对八大排序中的直接插入排序进行详细的讲解,各位看官姥爷快搬好小板凳坐好叭。
-------- 不要998,不要98,只要一键三连,三连买不了吃亏,买不了上当
目录
前言
什么是直接插入排序
直接插入排序的实现
基本思想
具体代码
直接插入排序的原理
直接插入排序的特点和性能
复杂度
稳定性
二分查找代码的应用
前言
上期我们对快速排序进行了分析,了解了它的思想,原理和实现代码及它的特性。今天我们来了解一下插入排序中的直接插入排序。
什么是直接插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增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交换,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;
}