插入排序
来源:互联网 发布:谢娜主持的网络综艺 编辑:程序博客网 时间:2024/05/22 10:48
-------------------siwuxie095
插入排序法
它的工作原理如下:
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
参考链接:
参考链接1,参考链接2,参考链接3
程序 1:插入排序法的实现
SortTestHelper.h:
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
using namespace std;
//辅助排序测试
namespace SortTestHelper
{
//生成测试数据(测试用例),返回一个随机生成的数组:
//生成有n个元素的随机数组,每个元素的随机范围为[rangeL,rangeR]
int *generateRandomArray(int n,int rangeL, int rangeR)
{
//默认rangeL要小于等于rangeR
assert(rangeL <= rangeR);
int *arr =newint[n];
//对于数组中的每一个元素,将之随机成为rangeL和rangeR之间的随机数
//先设置随机种子:这里将当前的时间作为种子来进行随机数的设置
srand(time(NULL));
for (int i =0; i < n; i++)
{
//rand()函数+百分号+数的范围,即取中间的一个随机整数,再加上rangeL即可
arr[i] = rand() % (rangeR - rangeL +1) + rangeL;
}
return arr;
}
//生成一个近乎有序的数组
int *generateNearlyOrderedArray(int n,int swapTimes)
{
//先生成完全有序的数组
int *arr =newint[n];
for (int i =0; i < n; i++)
{
arr[i] = i;
}
//以当前时间为随机种子
srand(time(NULL));
//再随机挑选几对元素进行交换,就是一个近乎有序的数组了
for (int i =0; i < swapTimes; i++)
{
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
template<typename T>
void printArray(T arr[],int n)
{
for (int i =0; i < n; i++)
{
cout << arr[i] <<" ";
}
cout << endl;
}
//经过排序算法排序后,再次确认是否已经完全排序
template<typename T>
bool isSorted(T arr[],int n)
{
for (int i =0; i < n - 1; i++)
{
if (arr[i]>arr[i +1])
{
return false;
}
}
return true;
}
//衡量一个算法的性能如何,最简单的方式就是看这个算法在特定数据集上的执行时间
//(1)传入排序算法的名字,方便打印输出
//(2)传入排序算法本身,即函数指针
//(3)传入测试用例:数组和元素个数
template<typename T>
void testSort(string sortName,void(*sort)(T[], int), T arr[], int n)
{
//在排序前后分别调用clock()函数
//时间差就是该排序算法执行的时钟周期的个数
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
//endTime减去 startTime转为double类型,除以 CLOCKS_PER_SEC,其中:
//
//CLOCKS_PER_SEC是标准库中定义的一个宏,表示每一秒钟所运行的时钟周期
//的个数,而(endTime-startTime)返回的是运行了几个时钟周期
//
//这样,最终的结果就是在这段时间中程序执行了多少秒
cout << sortName <<":" <<double(endTime - startTime) / CLOCKS_PER_SEC
<<"s" << endl;
}
//复制数组
int *copyIntArray(int a[],int n)
{
int *arr =newint[n];
//copy()函数在std中:
//第一个参数是原数组的头指针,
//第二个参数是原数组的尾指针,
//第三个参数是目的数组的头指针
//
//注意:copy()函数运行时会报错,需要在:
//项目->属性->配置属性->C/C++->预处理器->预处理器定义
//在其中添加:_SCL_SECURE_NO_WARNINGS
copy(a, a + n, arr);
return arr;
}
}
#endif
InsertionSort.h:
#ifndef INSERTIONSORT_H
#define INSERTIONSORT_H
//插入排序是 O(n^2) 级别算法复杂度的排序算法,它的思想就类似于
//在玩扑克牌时插入牌的思想,即将拿到的一张牌插入到合适的位置,
//当最后一张牌也插入后,手中的整副扑克牌也就排序完成了
//插入排序:从小到大进行排序
template<typename T>
void insertionSort(T arr[],int n)
{
//对于插入排序来说,第一个元素(索引为0)根本不用考虑
//
//因为在插入排序的初始,第一个元素放在那里,它本身就已经
//有序了,不需要再把它插入到前面的任何位置,所以从第二个
//元素(索引为1)开始考察
for (int i =1; i < n; i++)
{
//寻找arr[i]合适的插入位置:
//对已经排好序的部分,插入时从后向前比较,
//最多只会考察到j=1时,因为是从后向前比较
//
//写法1:
for (int j = i; j >0; j--)
{
if (arr[j] < arr[j -1])
{
swap(arr[j], arr[j -1]);
}
else
{
//当arr[i]插入到合适位置后,就跳出当前循环
//继续对下一个元素进行考察
break;
}
}
////将for循环中的 if 条件加入到for循环中
//(内层循环的两种写法,可替代上面的写法1)
//写法2:
//for (int j = i; j > 0 && arr[j] > arr[j - 1]; j--)
//{
// swap(arr[j], arr[j - 1]);
//}
}
}
#endif
main.cpp:
#include"SortTestHelper.h"
#include"InsertionSort.h"
int main()
{
int n =10000;
int *arr = SortTestHelper::generateRandomArray(n,0, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr, n);
//SortTestHelper::printArray(arr, n);
delete []arr;
system("pause");
return0;
}
运行一览:
程序 2:插入排序法和选择排序法的比较
SortTestHelper.h:
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
using namespace std;
//辅助排序测试
namespace SortTestHelper
{
//生成测试数据(测试用例),返回一个随机生成的数组:
//生成有n个元素的随机数组,每个元素的随机范围为[rangeL,rangeR]
int *generateRandomArray(int n,int rangeL, int rangeR)
{
//默认rangeL要小于等于rangeR
assert(rangeL <= rangeR);
int *arr =newint[n];
//对于数组中的每一个元素,将之随机成为rangeL和rangeR之间的随机数
//先设置随机种子:这里将当前的时间作为种子来进行随机数的设置
srand(time(NULL));
for (int i =0; i < n; i++)
{
//rand()函数+百分号+数的范围,即取中间的一个随机整数,再加上rangeL即可
arr[i] = rand() % (rangeR - rangeL +1) + rangeL;
}
return arr;
}
//生成一个近乎有序的数组
int *generateNearlyOrderedArray(int n,int swapTimes)
{
//先生成完全有序的数组
int *arr =newint[n];
for (int i =0; i < n; i++)
{
arr[i] = i;
}
//以当前时间为随机种子
srand(time(NULL));
//再随机挑选几对元素进行交换,就是一个近乎有序的数组了
for (int i =0; i < swapTimes; i++)
{
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
template<typename T>
void printArray(T arr[],int n)
{
for (int i =0; i < n; i++)
{
cout << arr[i] <<" ";
}
cout << endl;
}
//经过排序算法排序后,再次确认是否已经完全排序
template<typename T>
bool isSorted(T arr[],int n)
{
for (int i =0; i < n - 1; i++)
{
if (arr[i]>arr[i +1])
{
return false;
}
}
return true;
}
//衡量一个算法的性能如何,最简单的方式就是看这个算法在特定数据集上的执行时间
//(1)传入排序算法的名字,方便打印输出
//(2)传入排序算法本身,即函数指针
//(3)传入测试用例:数组和元素个数
template<typename T>
void testSort(string sortName,void(*sort)(T[], int), T arr[], int n)
{
//在排序前后分别调用clock()函数
//时间差就是该排序算法执行的时钟周期的个数
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
//endTime减去 startTime转为double类型,除以 CLOCKS_PER_SEC,其中:
//
//CLOCKS_PER_SEC是标准库中定义的一个宏,表示每一秒钟所运行的时钟周期
//的个数,而(endTime-startTime)返回的是运行了几个时钟周期
//
//这样,最终的结果就是在这段时间中程序执行了多少秒
cout << sortName <<":" <<double(endTime - startTime) / CLOCKS_PER_SEC
<<"s" << endl;
}
//复制数组
int *copyIntArray(int a[],int n)
{
int *arr =newint[n];
//copy()函数在std中:
//第一个参数是原数组的头指针,
//第二个参数是原数组的尾指针,
//第三个参数是目的数组的头指针
//
//注意:copy()函数运行时会报错,需要在:
//项目->属性->配置属性->C/C++->预处理器->预处理器定义
//在其中添加:_SCL_SECURE_NO_WARNINGS
copy(a, a + n, arr);
return arr;
}
}
#endif
InsertionSort.h:
#ifndef INSERTIONSORT_H
#define INSERTIONSORT_H
//插入排序是 O(n^2) 级别算法复杂度的排序算法,它的思想就类似于
//在玩扑克牌时插入牌的思想,即将拿到的一张牌插入到合适的位置,
//当最后一张牌也插入后,手中的整副扑克牌也就排序完成了
//插入排序:从小到大进行排序
template<typename T>
void insertionSort(T arr[],int n)
{
//对于插入排序来说,第一个元素(索引为0)根本不用考虑
//
//因为在插入排序的初始,第一个元素放在那里,它本身就已经
//有序了,不需要再把它插入到前面的任何位置,所以从第二个
//元素(索引为1)开始考察
for (int i =1; i < n; i++)
{
//寻找arr[i]合适的插入位置:
//对已经排好序的部分,插入时从后向前比较,
//最多只会考察到j=1时,因为是从后向前比较
//
//写法1:
for (int j = i; j >0; j--)
{
if (arr[j] < arr[j -1])
{
swap(arr[j], arr[j -1]);
}
else
{
//当arr[i]插入到合适位置后,就跳出当前循环
//继续对下一个元素进行考察
break;
}
}
////将for循环中的 if 条件加入到for循环中
//(内层循环的两种写法,可替代上面的写法1)
//写法2:
//for (int j = i; j > 0 && arr[j] > arr[j - 1]; j--)
//{
// swap(arr[j], arr[j - 1]);
//}
}
}
#endif
SelectionSort.h:
#ifndef SELECTIONSORT_H
#define SELECTIONSORT_H
//选择排序:从小到大进行排序
template<typename T>
void selectionSort(T arr[],int n)
{
for (int i =0; i < n; i++)
{
//寻找[i,n]区间里的最小值
int minIndex = i;
for (int j = i +1; j < n; j++)
{
if (arr[j] < arr[minIndex])
{
minIndex = j;
}
}
//swap()函数:交换两个值,对于swap()函数来说:
//在C++ 11标准中,它就在std标准命名空间中,即标准库中,不用包含其它
//如果是比较老的标准,它在algorithm库中,需要include <algorithm>
swap(arr[i], arr[minIndex]);
}
}
#endif
main.cpp:
#include"SortTestHelper.h"
#include"InsertionSort.h"
#include"SelectionSort.h"
int main()
{
int n =10000;
int *arr1 = SortTestHelper::generateRandomArray(n,0, n);
int *arr2 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
SortTestHelper::testSort("Selection Sort", selectionSort, arr2, n);
delete []arr1;
delete []arr2;
system("pause");
return0;
}
//插入排序和选择排序的最大区别就是:
//
//对于第二重循环,也就是内层循环,插入排序可以提前结束,而
//选择排序则没有提前中止的机会
//
//
//所以理论上,插入排序应该比选择排序更快一些,但实际比较时
//却发现插入排序的性能比选择排序还要差,也就是用时更多
//
//这是因为:此时的插入排序在遍历的同时,也在不停的交换,而
//交换的操作比简单的比较操作更耗时,每一次交换,背后就有三
//次赋值的操作
运行一览:
程序 3:插入排序法的优化
SortTestHelper.h:
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
using namespace std;
//辅助排序测试
namespace SortTestHelper
{
//生成测试数据(测试用例),返回一个随机生成的数组:
//生成有n个元素的随机数组,每个元素的随机范围为[rangeL,rangeR]
int *generateRandomArray(int n,int rangeL, int rangeR)
{
//默认rangeL要小于等于rangeR
assert(rangeL <= rangeR);
int *arr =newint[n];
//对于数组中的每一个元素,将之随机成为rangeL和rangeR之间的随机数
//先设置随机种子:这里将当前的时间作为种子来进行随机数的设置
srand(time(NULL));
for (int i =0; i < n; i++)
{
//rand()函数+百分号+数的范围,即取中间的一个随机整数,再加上rangeL即可
arr[i] = rand() % (rangeR - rangeL +1) + rangeL;
}
return arr;
}
//生成一个近乎有序的数组
int *generateNearlyOrderedArray(int n,int swapTimes)
{
//先生成完全有序的数组
int *arr =newint[n];
for (int i =0; i < n; i++)
{
arr[i] = i;
}
//以当前时间为随机种子
srand(time(NULL));
//再随机挑选几对元素进行交换,就是一个近乎有序的数组了
for (int i =0; i < swapTimes; i++)
{
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
template<typename T>
void printArray(T arr[],int n)
{
for (int i =0; i < n; i++)
{
cout << arr[i] <<" ";
}
cout << endl;
}
//经过排序算法排序后,再次确认是否已经完全排序
template<typename T>
bool isSorted(T arr[],int n)
{
for (int i =0; i < n - 1; i++)
{
if (arr[i]>arr[i +1])
{
return false;
}
}
return true;
}
//衡量一个算法的性能如何,最简单的方式就是看这个算法在特定数据集上的执行时间
//(1)传入排序算法的名字,方便打印输出
//(2)传入排序算法本身,即函数指针
//(3)传入测试用例:数组和元素个数
template<typename T>
void testSort(string sortName,void(*sort)(T[], int), T arr[], int n)
{
//在排序前后分别调用clock()函数
//时间差就是该排序算法执行的时钟周期的个数
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
//endTime减去 startTime转为double类型,除以 CLOCKS_PER_SEC,其中:
//
//CLOCKS_PER_SEC是标准库中定义的一个宏,表示每一秒钟所运行的时钟周期
//的个数,而(endTime-startTime)返回的是运行了几个时钟周期
//
//这样,最终的结果就是在这段时间中程序执行了多少秒
cout << sortName <<":" <<double(endTime - startTime) / CLOCKS_PER_SEC
<<"s" << endl;
}
//复制数组
int *copyIntArray(int a[],int n)
{
int *arr =newint[n];
//copy()函数在std中:
//第一个参数是原数组的头指针,
//第二个参数是原数组的尾指针,
//第三个参数是目的数组的头指针
//
//注意:copy()函数运行时会报错,需要在:
//项目->属性->配置属性->C/C++->预处理器->预处理器定义
//在其中添加:_SCL_SECURE_NO_WARNINGS
copy(a, a + n, arr);
return arr;
}
}
#endif
InsertionSort.h:
#ifndef INSERTIONSORT_H
#define INSERTIONSORT_H
//插入排序是 O(n^2) 级别算法复杂度的排序算法,它的思想就类似于
//在玩扑克牌时插入牌的思想,即将拿到的一张牌插入到合适的位置,
//当最后一张牌也插入后,手中的整副扑克牌也就排序完成了
//插入排序:从小到大进行排序
template<typename T>
void insertionSort(T arr[],int n)
{
//对于插入排序来说,第一个元素(索引为0)根本不用考虑
//
//因为在插入排序的初始,第一个元素放在那里,它本身就已经
//有序了,不需要再把它插入到前面的任何位置,所以从第二个
//元素(索引为1)开始考察
for (int i =1; i < n; i++)
{
//寻找arr[i]合适的插入位置:
//对已经排好序的部分,插入时从后向前比较,
//最多只会考察到j=1时,因为是从后向前比较
//
//先将要插入的元素arr[i]保存到e中,在和已经排好序的部分进行比较时:
//(1)如果小于当前被比较的元素arr[j-1],则将当前被比较的元素向后移一位
//(2)如果大于当前被比较的元素arr[j-1],则当前位置j就是要插入的位置
T e = arr[i];
int j;//保存元素e应该插入的位置
for (j = i; j >0 && arr[j - 1] > e; j--)
{
arr[j] = arr[j -1];
}
//将之前保存在e中的元素插入到位置j
arr[j] = e;
}
}
#endif
main.cpp:
#include"SortTestHelper.h"
#include"InsertionSort.h"
int main()
{
int n =10000;
//随机数组arr
int *arr = SortTestHelper::generateRandomArray(n,0, n);
//近乎有序的数组arrx
int *arrx = SortTestHelper::generateNearlyOrderedArray(n,100);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arrx, n);
delete []arr;
delete []arrx;
system("pause");
return0;
}
//此时的插入排序,不但不再使用交换操作,更重要的是插入排序可以提前中止内层循环
//
//对于插入排序来说,提前中止内层循环是非常重要的一个性质
//
//提前中止的条件是:一旦找到合适的位置就中止
//
//假设这个数组本身就基本上是有序的,这种情况下,内层循环可以很快的找到它应该
//插入的位置,此时,插入排序的效率会非常的高
//
//可以简单模拟如下:
//(1)在生成这个数组时,让这个随机数的范围变小一些
//(2)在生成这个数组时,先生成一个完全有序的数组,再将该数组中的若干对元素交换
//
//对于一个近乎有序的数组来说,插入排序的性能要远远的优于选择排序
//
//事实上,插入排序对于近乎于有序的数组来说,它的性能比O(n*lgn)的级别的排序算法还
//要快,这也是为什么插入排序是有非常重要的实际意义
//
//因为很多时候,我们处理的真实的数据,是近乎有序的
//
//比如说:一套系统的日志,它的生成,就是按照时间去生成的,但又可能在中间
//产生了一些错误,或者是某一些任务时间过长,所以存在几个无序的元素,对于
//这样的一个系统日志,使用插入排序进行排序,性能会更好
//
//通过代码不难发现,在最优的情况下,即当要排序的内容是一个完全有序的数组时,
//插入排序将变成一个O(n)级别的算法,也就是说在内层循环中,每一次都执行一下,
//发现当前的位置就是合适的位置,直接结束内层循环,进入下一次循环,这是插入
//排序非常重要的一个性质
//
//也正是这个原因,插入排序也会在更加复杂的排序算法中作为一个子过程来进行优化
//
//
//
//选择排序的思想非常简单,它的缺点也非常明显:
//对于任何一个数组,选择排序,两层循环,每一层循环都必须完全的执行完成,
//正是因为如此,选择排序的效率在任何情况下都是
//非常慢的
//
//而插入排序,虽然它的最差的时间复杂度也是O(n^2)级别的,但是在数组近乎
//有序的情况下,插入排序的性能非常的高,甚至比O(n*lgn)级别的排序算法性能
//还要高,这使得插入排序有着非常重要的实际意义
//
//对插入排序的深入理解也告诉我们:O(n^2)级别的排序算法并非一无是处,
//对于近乎有序的数组,插入排序的效率相对比较高
运行一览:
【made by siwuxie095】
- 插入排序-【插入排序】
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- 插入排序
- UE4 自带的一些模块
- 按周显示的日历和按月显示的日历,你需要吗
- Windows7+WDK+VS2010+VisualDDK驱动开发环境搭建
- 为何编程如此之难?
- java动态创建xml文件
- 插入排序
- 从程序员代码“呼救”大破传销组织_聊掌握一门编程语言的重要性!
- Java 实例
- 1052. 卖个萌 (20) PAT
- CSDN日报20170526 ——《论程序员的时代焦虑与焦虑的缓解》
- 从程序员代码“呼救”大破传销组织_聊掌握一门编程语言的重要性
- C++内存对齐
- 神经网络学习二
- JAVA学习:第一阶段第一天(java基本概念和配置环境)