插入排序(Insertion sort)是一种简单直观且稳定的排序算法,时间复杂度O(n^2)。对于近乎有序的数组,是比较快的。算法思想:将一个数据插入到已经排好序的有序数据中合适的位置中,直到全部插完为止。类似我们在玩扑克牌的时候,发到手里的牌,我们按照一张一张插入到合适的位置。
一. c语言版
【原版1】
//插入排序 /* 插入一个数都要将它和之前的已经完成排序的序列进行重新排序, 也就是要找到新插入的数对应原序列中的位置。 那么也就是说,每次插入一个数都要对原来排序好的那部分序列进行重新的排序。 整体思路: a. 默认第一个数arr[0]是有序的,剩下的arr[len-2]区域是无序的 b. 每次从无序区域取出一个元素,插入前面已经排好序的区域中,合适的位置 8 6 2 =>一开始我们假设8是有序的 =>插入6元素,发现6比前一个元素小,交换位置6 8 2 ,6继续向比较已经到头,小循环结束 =>插入2元素,发现2比前面的8要小,交换位置6 2 8, 2继续向前比较小于6,交换位置2 6 8 小循环结束 =>然后整体循环结束,整个数组有序2 6 8 */ void InsertSort1(int *arr,int n) { int i,j; //假设第一个元素是有序的 for(i=1; i<n; i++){ for(j = i;j>0;j--){ if(arr[j] < arr[j-1]){//判断是否与前一个元素小 swap(&arr[j],&arr[j-1]);//如果小于就交换 }else{//如果大于或者等于,就不用再向前循环,因为前面已经是有序的 break;//跳出循环 } } } }
【直接插入排序】
/* 优化 【直接插入排序】向左比较 由于之前的插入,每次如果小的话,都需要进行交换位置,也是比较耗时,所以我们这次先确定位置 8 6 2 一开始8是有序的 =>8 现在开始插入6 先让elem = 6 复制了一份, 然后向前寻找到elem合适的位置,由于8比6还要大,8向后移动赋值一位 =>8 8,然后再向左到头了,找到位置 然后插入(赋值) 6 => 6 8 现在开始插入2 elem = 2 复制了一份 然后向左移动, 8>2,然后把8向后移动一为6 8 8 然后与之前的元素比较6>2 然后把6向后移动一位6 6 8 然后到头了,找到合适位置,然后赋值为2 变成2 6 8 =>2 6 8 简化一下说明: [1]默认第一个元素是有序的,arr[0] [2]取下一个元素记作elem,向前已经排好序的元素序列中,也就是向数组左边的比较。 [3]如果该元素大于elem新的要插入的元素,就向后移动到下一个元素。 [4]然后重复步骤3,直到找到已排序的元素小于或者等于新的元素的位置。 [5]就插入到该位置后面,由于等于的元素不会交换相对位置,所以排序是稳定的。 [6]重复2~5 */ void InsertSort2(int *arr,int n) { int i,j; //假设第一个元素是有序的 for(i=1; i<n; i++){ int elem = arr[i]; for(j = i;j>0 && elem <arr[j-1] ;j--){ //看一下当前elem的值 要比前面的值小,那么就不应该放到这里 arr[j] = arr[j-1];//那么j就不是我们要找的,向后移动一个元素 } arr[j] = elem;//j就是我们要找的合适的位置 } }
【二分折半插入排序】
/* 折半插入(Binary Insert Sort)和直接插入类似 唯一的区别就是在有序列表中比较查找插入位置时用的是二分法。 因为前面部分是有序列表,所以是最理想的二分法。 */ void InsertSort3(int *arr, int len) { int left,right,mid; int elem; int i,j; for(i=1;i<len;i++){ right = i-1; left = 0; //二分获取插入位置 while(left<=right){ mid = (left+right)/2; if(arr[mid] > arr[i]){ right = mid - 1; }else{ left = mid + 1; } } //得到位置,其他元素后移, 直接插入 elem = arr[i]; for(j=i;j>right+1;j--){ arr[j] = arr[j-1]; } arr[right+1] = elem; } }
【完整代码】
#include<stdio.h> #include<string.h> #include<time.h> #define MAX 100000 void InsertSort1(int *arr,int n);//插入排序 ,每次后面小的话,都要交换位置 void InsertSort2(int *arr,int n);//优化,先确定位赋值 ,减少交换次数 void InsertSort3(int *arr,int n);//折半查找法 void getRandArray(int *arr,int n,int Range); //随机生成一个长度为n一维数组 随机数的范围在[0 Range) void getNearRandArray(int *arr,int n,int times);//随机生成一个部分有序的数组 长度为n 无序交换次数 void swap(int *a,int *b); //交换两个变量 void dump(int arr[],int n); //打印 int main() { clock_t begin , end; double time; int arr1[MAX],arr2[MAX],arr3[MAX]; getRandArray(arr1,MAX,100); //getNearRandArray(arr1,MAX,5); memcpy(arr2,arr1,sizeof(arr1));//拷贝 memcpy(arr3,arr1,sizeof(arr1)); begin = clock();//开始记录 InsertSort1(arr1,MAX); end = clock(); //结束记录 time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【插入排序1】【cost time is %lf 】\n",MAX,time); begin = clock(); InsertSort2(arr2,MAX); end = clock(); time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【插入排序2】【cost time is %lf 】\n",MAX,time); begin = clock();//开始记录 InsertSort3(arr3,MAX); end = clock(); time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【插入排序3】【cost time is %lf 】\n",MAX,time); return 0; } //插入排序 /* 时间复杂度O(n^2) 稳定的排序算法。 对于近乎有序的数组,是比较快的。适用于少量数据的排序。 算法思想:将一个数据插入到已经排好序的有序数据中合适的位置中,直到全部插完为止。 插入一个数都要将它和之前的已经完成排序的序列进行重新排序, 也就是要找到新插入的数对应原序列中的位置。 那么也就是说,每次插入一个数都要对原来排序好的那部分序列进行重新的排序。 整体思路: a. 默认第一个数arr[0]是有序的,剩下的arr[len-2]区域是无序的 b. 每次从无序区域取出一个元素,插入前面已经排好序的区域中,合适的位置 8 6 2 =>一开始我们假设8是有序的 =>插入6元素,发现6比前一个元素小,交换位置6 8 2 ,6继续向比较已经到头,小循环结束 =>插入2元素,发现2比前面的8要小,交换位置6 2 8, 2继续向前比较小于6,交换位置2 6 8 小循环结束 =>然后整体循环结束,整个数组有序2 6 8 */ void InsertSort1(int *arr,int n) { int i,j; //假设第一个元素是有序的 for(i=1; i<n; i++){ for(j = i;j>0;j--){ if(arr[j] < arr[j-1]){//判断是否与前一个元素小 swap(&arr[j],&arr[j-1]);//如果小于就交换 }else{//如果大于或者等于,就不用再向前循环,因为前面已经是有序的 break;//跳出循环 } } } } /* 优化 【直接插入排序】向左比较 由于之前的插入,每次如果小的话,都需要进行交换位置,也是比较耗时,所以我们这次先确定位置 8 6 2 一开始8是有序的 =>8 现在开始插入6 先让elem = 6 复制了一份, 然后向前寻找到elem合适的位置,由于8比6还要大,8向后移动赋值一位 =>8 8,然后再向左到头了,找到位置 然后插入(赋值) 6 => 6 8 现在开始插入2 elem = 2 复制了一份 然后向左移动, 8>2,然后把8向后移动一为6 8 8 然后与之前的元素比较6>2 然后把6向后移动一位6 6 8 然后到头了,找到合适位置,然后赋值为2 变成2 6 8 =>2 6 8 简化一下说明: [1]默认第一个元素是有序的,arr[0] [2]取下一个元素记作elem,向前已经排好序的元素序列中,也就是向数组左边的比较。 [3]如果该元素大于elem新的要插入的元素,就向后移动到下一个元素。 [4]然后重复步骤3,直到找到已排序的元素小于或者等于新的元素的位置。 [5]就插入到该位置后面,由于等于的元素不会交换相对位置,所以排序是稳定的。 [6]重复2~5 */ void InsertSort2(int *arr,int n) { int i,j; //假设第一个元素是有序的 for(i=1; i<n; i++){ int elem = arr[i]; for(j = i;j>0 && elem <arr[j-1] ;j--){ //看一下当前elem的值 要比前面的值小,那么就不应该放到这里 arr[j] = arr[j-1];//那么j就不是我们要找的,向后移动一个元素 } arr[j] = elem;//j就是我们要找的合适的位置 } } /** 【折半插入排序】 折半插入(Binary Insert Sort)和直接插入类似 唯一的区别就是在有序列表中比较查找插入位置时用的是二分法。 因为前面部分是有序列表,所以是最理想的二分法。 */ void InsertSort3(int *arr, int len) { int left,right,mid; int elem; int i,j; for(i=1;i<len;i++){ right = i-1; left = 0; //二分获取插入位置 while(left<=right){ mid = (left+right)/2; if(arr[mid] > arr[i]){ right = mid - 1; }else{ left = mid + 1; } } //得到位置,其他元素后移, 直接插入 elem = arr[i]; for(j=i;j>right+1;j--){ arr[j] = arr[j-1]; } arr[right+1] = elem; } } //随机生成一个数组 void getRandArray(int *arr,int n,int Range) { srand(time(NULL));//设置随机种子 int i = 0; for (i=0; i<n; i++){ arr[i] = rand() % Range;//范围在Range以内 } } //随机生成一个比较有序的数组 先有序,然后随机交换位置 void getNearRandArray(int *arr,int n,int times) { int i = 0; for(i;i<n;i++){//先构造一个有序的数组 arr[i] = i; } srand(time(NULL));//设置种子 for(i=0;i<times;i++){//尽量有序 循环交换多少次 int posx = rand()%(n-1);//在[1,n-1]上随机位置 int posy = rand()%(n-1); swap(&arr[posx],&arr[posy]);//随机交换两个位置的数 } } //交换两个值 利用指针交换位置 void swap(int *a,int *b) { int temp; temp = *a; *a = *b; *b = temp; } //打印输出数组 void dump(int *arr,int n) { int i=0; for(i=0;i<n;i++) printf("%d ",arr[i]); printf("\n");//换行 }
二. PHP版
【直接插入】
/* * 插入排序 * 时间复杂度 O(n^2) 稳定性 ,只有小于前面的数,才交换 * @param array $arr 要排序的数组 * @param int $len 数组的长度 * * @return array $arr 排好序的数组 */ public function insertSort($arr,$len) { $i=0; $temp=0; for($i=1; $i<$len ; $i++){ $temp = $arr[$i];//保存当前值 for($j=$i; $j>0; $j--){//向前比较 if($temp < $arr[$j-1]){//看一下当前值比前面的小,就赋值 $arr[$j] = $arr[$j-1]; } } $arr[$j] = $temp;//找到合适的位置 把当前的值给过去 } return $arr; }
【二分插入】
/** * 折半插入排序 */ public function binaryInsertSort($arr,$len) { $left=$right=$mid=0; $temp=0; $i=$j=0; for($i=1;$i<$len;$i++){ $right=$i-1; $left =0; //二分获取插入位置 while($left<=$right){ $mid = ($left+$right)/2; if($arr[$mid] > $arr[$i]){//那么就是在左边 $right = $mid - 1; }else{ $left = $mid + 1;//那么就是在右边 } } //得到位置$right,其他元素后移, $temp = $arr[$i]; for($j=$i;$j>$right+1;$j--){ $arr[$j] = $arr[$j-1]; } $arr[$right+1] = $temp;//插入 } return $arr; }
【完整代码】
<?php //插入排序 class Insert { const MIN = 0; const MAX = 1000;//设置随机数的范围 private $len = 10;//默认构造的数组长度 private $times= 10;//无序交换次数 public $arr = [];//要排序的数组 public $out = [];//排好序的数组 public function __construct($len=0,$times=0,$min=Insert::MIN,$max=Insert::MAX) { $this->len = empty($len) ? $this->len:$len;//判断要生成数组的长度 $this->times= empty($times) ? $this->times:$times; //无序交换次数 //$this->arr = $this->getRandArray($this->len,$min,$max);//生成无序数组 $this->arr = $this->getNearRandArray($this->len,$this->times,$min,$max);//生成相对有序数组 echo '数组长度:',$len,'<br/>'; echo '随机数范围:[',$min,',',$max,']','<br/>'; echo '无序交换次数:',$times,'<br/>'; $stime=microtime(true); $this->out = $this->insertSort($this->arr,$this->len);//调用插入排序 $etime=microtime(true); $total=$etime-$stime; echo "<br />[直接插入排序]执行时间为:{$total} 秒<br/>"; $stime=microtime(true); $this->out = $this->binaryInsertSort($this->arr,$this->len);//调用插入排序 $etime=microtime(true); $total=$etime-$stime; echo "<br />[二分插入排序]执行时间为:{$total} 秒<br/>"; // var_dump($this->arr,$this->out); } /* * 插入排序 * 时间复杂度 O(n^2) 稳定性 ,只有小于前面的数,才交换 * @param array $arr 要排序的数组 * @param int $len 数组的长度 * * @return array $arr 排好序的数组 */ public function insertSort($arr,$len) { $i=0; $temp=0; for($i=1; $i<$len ; $i++){ $temp = $arr[$i];//保存当前值 for($j=$i; $j>0; $j--){//向前比较 if($temp < $arr[$j-1]){//看一下当前值比前面的小,就赋值 $arr[$j] = $arr[$j-1]; } } $arr[$j] = $temp;//找到合适的位置 把当前的值给过去 } return $arr; } /* * 折半插入排序 */ public function binaryInsertSort($arr,$len) { $left=$right=$mid=0; $temp=0; $i=$j=0; for($i=1;$i<$len;$i++){ $right=$i-1; $left =0; //二分获取插入位置 while($left<=$right){ $mid = ($left+$right)/2; if($arr[$mid] > $arr[$i]){//那么就是在左边 $right = $mid - 1; }else{ $left = $mid + 1;//那么就是在右边 } } //得到位置$right,其他元素后移, $temp = $arr[$i]; for($j=$i;$j>$right+1;$j--){ $arr[$j] = $arr[$j-1]; } $arr[$right+1] = $temp;//插入 } return $arr; } //交换两个变量 private function swap(&$a,&$b) { $temp = 0;//借助第三个变量 $temp = $a; $a = $b; $b = $temp; } /** * 生成部分有序的数组 * * @params $len 数组长度 * @params $times 数组无序交换的次数 * @params $min $max 随机数的范围 * * return array */ private function getNearRandArray($len,$times,$min,$max) { $arr = [];//生成部分有序的数组 for($i=0; $i<$len; $i++){ $arr[$i] = $i; } for($i=0; $i<$times; $i++){//打乱有序 循环交换多少次 $posx = mt_rand($min,$max)%($len-1);//在[1,len-1]上随机位置 $posy = mt_rand($min,$max)%($len-1); $this->swap($arr[$posx],$arr[$posy]);//随机交换两个位置的数 } return $arr; } /** * 生成随机数组 * * @params $len 随机数组的长度 * @params $min $max 随机数的范围 */ private function getRandArray($len,$min,$max) { $arr = [];//生成无序的数组 for($i=0; $i<$len; $i++){ $arr[$i] = mt_rand($min,$max); } return $arr; } } new Insert(1000,5);//实例化类