直接插入排序到底有多“直男”

简介: 直接插入排序到底有多“直男”

目录

前言

什么是直接插入排序

直接插入排序的实现

基本思想

具体代码

直接插入排序的原理

直接插入排序的特点和性能

复杂度

稳定性

二分查找代码的应用


前言

上期我们对快速排序进行了分析,了解了它的思想,原理和实现代码及它的特性。今天我们来了解一下插入排序中的直接插入排序。

什么是直接插入排序

插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增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;
}


目录
相关文章
|
4月前
|
存储 算法 搜索推荐
15.百万考生成绩如何排序?计数排序给你好看
15.百万考生成绩如何排序?计数排序给你好看
33 0
15.百万考生成绩如何排序?计数排序给你好看
|
4月前
|
图计算 索引
leetcode-42:接雨水
leetcode-42:接雨水
29 0
|
8月前
【动态规划刷题 6】 删除并获得点数&& 粉刷房子
【动态规划刷题 6】 删除并获得点数&& 粉刷房子
|
4月前
|
算法 测试技术 C#
C++二分查找、离线算法:最近的房间
C++二分查找、离线算法:最近的房间
|
11月前
小明的背包2-动态规划
小明的背包2-动态规划
67 0
|
11月前
[蓝桥杯 2016 省 B] 交换瓶子
[蓝桥杯 2016 省 B] 交换瓶子
39 0
|
11月前
|
Java C++
多重背包(小明的背包3)
多重背包(小明的背包3)
|
11月前
|
测试技术
Leecode 42. 接雨水
Leecode 42. 接雨水
66 1
|
11月前
|
人工智能 编译器 BI
[蓝桥杯 2018 省 B]递增三元组
[蓝桥杯 2018 省 B]递增三元组
116 0
|
存储 算法 JavaScript
双指针解决【接雨水】问题
本篇将带来双指针算法经典题目之:接雨水问题;