排序——插入排序(C++)
2016-03-17 19:33
204 查看
插入排序分为直接插入排序和折半插入排序两种
设存在一个一维数组 array[0......n-1], 第 i 趟排序是将数组中下标为 i 的第 i + 1 个元素插入到一个已经按值有序排列的i个子序列的合适位置。
(1)第一个元素看成有序序列,从下标为 i = 1 的第二个元素开始,每一趟排序都将下标为 i 的元素插入到之前的有序序列中,然后 i = i + 1 进行下一个元素的插入排序,由此可见插入排序需要 n - 1 趟排序
(2)每次寻找插入元素 array[i] 的位置,是从有序序列的最后一个元素 array[i-1] 开始查找,直到找到某一位置的元素 array[j] (j >= 0 && j <= i-1)满足 array[j] <= array[i], 那么下标为j + 1 的位置就应该是插入元素 array[i]的合适位置。从有序序列的最后一个元素开始查找,边查找边移动元素,而不是先找到插入位置再移动元素,这样提高了效率。
(3)将array数组中下标 [j + 1,i - 1]的所有元素依次后移一位,空出 array[j+1]的位置。
(4)将array[i] 元素赋值给 array[j+1],至此完成插入排序的一趟排序。
例程:
总结:
1.在while循环时判断条件是temp < array[j],而不是temp <= array[j]; 这样是为了保证插入排序稳定性;
2. 最后插入元素temp的合适位置应该是 j + 1, 下标为 j 的位置应该是 temp >= array[j] 的第一元素,我们应该在该元素之后插入元素。
时间复杂度分析:
(1)最好情况:当原始序列是一个按值递增序列时,对应的每个i值只需要进行一次元素之间的比较,因而总的比较次数最少,为 n - 1,并且不需要移动元素;
(2)最坏情况:当原始序列是一个按值递减的序列时,对应的每个i值都需要进行 i - 1 次比较,总的元素之间的比较次数达到最大值,为1 + 2 + 3 + 4 + . . . + n - 1 = n(n-)/2;
(3)平均情况:序列的初始情况是随机的,即参加排序的序列中元素可能出现的各种排列的概率相同,则可取上述最小值和最大值的平均值作为插入排序时所进行的元素之间的比较次数,约为 n^2/4。
由此可见出入排序的时间复杂度为 O(n^2),插入排序属于稳定性排序方法。
折半查找:
(1)首先需要定义三个中间变量 low ,mid,high,用来保存已经排序好的序列的起始位置,中间位置,和末端位置。故在排序初始状态下,令:low = 0,high = i - 1,mid = (low + high) / 2;
(2)设待插入元素为array[i],比较 array[i]与array[mid]大小关系:
若:array[i] < array[mid],则 array[i] 的插入位置应该在 mid 位置的左侧,令 high = mid - 1;
若:array[i] >= array[mid], 则array[i]的插入位置应该在 mid位置的右侧,令 low = mid + 1;
(3)再令 mid = (low+high) / 2,一轮比较结束后,重复上述比较步骤。
在折半查找结束时,有 mid = low = high, 则:
(1)如果 array[i] < array[mid], 则应该在 array[mid]的前面插入元素array[i],也就是需要将序列中[low,i - 1]的元素依次后移一位后,再进行array[low] = array[i]的赋值操作。这时high = mid - 1,会导致 low > high;
(2)如果 array[i] >= array[mid],则应该在array[mid]的后面插入元素array[i],这时 low = mid + 1,也是将[low,i - 1]的元素一次后移一位后,再进行array[low] = array[i]的赋值操作,这时 low > high。
总之,折半查找循环结束的条件应该是 low > high。
例程:
1、直接插入排序
算法描述:设存在一个一维数组 array[0......n-1], 第 i 趟排序是将数组中下标为 i 的第 i + 1 个元素插入到一个已经按值有序排列的i个子序列的合适位置。
(1)第一个元素看成有序序列,从下标为 i = 1 的第二个元素开始,每一趟排序都将下标为 i 的元素插入到之前的有序序列中,然后 i = i + 1 进行下一个元素的插入排序,由此可见插入排序需要 n - 1 趟排序
(2)每次寻找插入元素 array[i] 的位置,是从有序序列的最后一个元素 array[i-1] 开始查找,直到找到某一位置的元素 array[j] (j >= 0 && j <= i-1)满足 array[j] <= array[i], 那么下标为j + 1 的位置就应该是插入元素 array[i]的合适位置。从有序序列的最后一个元素开始查找,边查找边移动元素,而不是先找到插入位置再移动元素,这样提高了效率。
(3)将array数组中下标 [j + 1,i - 1]的所有元素依次后移一位,空出 array[j+1]的位置。
(4)将array[i] 元素赋值给 array[j+1],至此完成插入排序的一趟排序。
例程:
#include <iostream> using namespace std; int simple_insert_sort(int array[], int n) { int j,temp; for (int i = 1; i < n; ++i) { temp = array[i]; j = i - 1; while (j >= 0 && temp < array[j]) { array[j + 1] = array[j]; --j; } array[j + 1] = temp; } return 0; } int main() { int array[] = { 22,43,21,5,32,89,76,51,11,9}; int arr_size = sizeof(array) / sizeof(int); simple_insert_sort(array, arr_size); for (int i = 0; i < arr_size; ++i) cout << array[i] << " "; cout << endl; system("pause"); return 0; }运行结果:
总结:
1.在while循环时判断条件是temp < array[j],而不是temp <= array[j]; 这样是为了保证插入排序稳定性;
2. 最后插入元素temp的合适位置应该是 j + 1, 下标为 j 的位置应该是 temp >= array[j] 的第一元素,我们应该在该元素之后插入元素。
时间复杂度分析:
(1)最好情况:当原始序列是一个按值递增序列时,对应的每个i值只需要进行一次元素之间的比较,因而总的比较次数最少,为 n - 1,并且不需要移动元素;
(2)最坏情况:当原始序列是一个按值递减的序列时,对应的每个i值都需要进行 i - 1 次比较,总的元素之间的比较次数达到最大值,为1 + 2 + 3 + 4 + . . . + n - 1 = n(n-)/2;
(3)平均情况:序列的初始情况是随机的,即参加排序的序列中元素可能出现的各种排列的概率相同,则可取上述最小值和最大值的平均值作为插入排序时所进行的元素之间的比较次数,约为 n^2/4。
由此可见出入排序的时间复杂度为 O(n^2),插入排序属于稳定性排序方法。
2、折半插入排序
从直接插入排序中可以看出,每一趟被插入的数值是一个按值有序的数值,依次可以利用较快的查找办法找到要插入元素的合适位置,这里我们采用折半查找的办法。折半查找:
(1)首先需要定义三个中间变量 low ,mid,high,用来保存已经排序好的序列的起始位置,中间位置,和末端位置。故在排序初始状态下,令:low = 0,high = i - 1,mid = (low + high) / 2;
(2)设待插入元素为array[i],比较 array[i]与array[mid]大小关系:
若:array[i] < array[mid],则 array[i] 的插入位置应该在 mid 位置的左侧,令 high = mid - 1;
若:array[i] >= array[mid], 则array[i]的插入位置应该在 mid位置的右侧,令 low = mid + 1;
(3)再令 mid = (low+high) / 2,一轮比较结束后,重复上述比较步骤。
在折半查找结束时,有 mid = low = high, 则:
(1)如果 array[i] < array[mid], 则应该在 array[mid]的前面插入元素array[i],也就是需要将序列中[low,i - 1]的元素依次后移一位后,再进行array[low] = array[i]的赋值操作。这时high = mid - 1,会导致 low > high;
(2)如果 array[i] >= array[mid],则应该在array[mid]的后面插入元素array[i],这时 low = mid + 1,也是将[low,i - 1]的元素一次后移一位后,再进行array[low] = array[i]的赋值操作,这时 low > high。
总之,折半查找循环结束的条件应该是 low > high。
例程:
#include <iostream> using namespace std; int bin_insert_sort(int array[], int n) { int j, temp, low, high, mid; for (int i = 1; i < n; ++i) { temp = array[i]; //将当前需要被插入元素保存在temp中 low = 0;high = i - 1; while (low <= high) //利用折半查找确定被插入元素array[i]的合适位置 { mid = (low + high) / 2; if (temp < array[mid]) high = mid - 1; else low = mid + 1; } for (j = i - 1; j >= low; --j) //找到合适位置low, 将[low, i-1]中的元素依次后移一位 array[j + 1] = array[j]; array[low] = temp; //插入元素array[i], 至此插入排序的一趟结束 } return 0; } int main() { int array[] = { 23,45,21,6,37,87,76,50,13,8}; int arr_size = sizeof(array) / sizeof(int); bin_insert_sort(array, arr_size); for (int i = 0; i < arr_size; ++i) cout << array[i] << " "; cout << endl; system("pause"); return 0; }运行结果:
相关文章推荐
- C语言中的static 详细分析
- C/C++内存分配
- c语言:通过指向结构体变量的指针变量输出结构体变量中成员的信息
- c++类的基础
- C++ 实现比较版本号
- C++ STL stack/queue
- C++ 学习之路(9):(附C++程序的多文件组成)堆栈示例
- C++第2次上机实验-C++程序的编写和运行
- 1013_三角形判断
- c++实现线性表
- c语言实现线性表
- C++中指针与数组的详解
- C语言-for循环
- C语言inline详细讲解
- C++编程采用windows API实现GBK转UTF-8
- c++基础
- VC++ 监控指定目录改变
- C++之伪函数
- C++项目中的extern "C" {}
- C++中颜色的设置