我们都有过玩扑克牌的经验吧。。当你一张张拿起扑克牌的时候,你是不是按一定顺序的?当你拿起一张新的扑克牌的时候,就会把他插在某两个牌之间,你是为什么插在那里?是因为他的大小在两扑克牌之间是吧。插入排序的原理和你玩扑克牌插入的原理是一样的。通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
其实现算法如下:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置后
- 重复步骤2~5
第二种是将元素交换,程序看起来比较简洁。具体的说明在代码里有体现了。
下面来说说二分插入排序,我们都知道,对于已经排好序的数组来说,查找元素最好的方法就是二分法,那么对于插入排序,我们已经知道对于任意一个正在遍历的元素它前面的元素都是已经排好序的了,那么在找插入位置的时候就可以使用二分法。
二分法原理:搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
我们这里并不是要找到和key相等的元素,而是要找到比key小的元素的上界,那么这里二分查找就需要进行小小的改变。首先,我们查找的时候返回下标的条件不再是找到相等的元素返回该元素下标或没有找到相等元素饭后返回-1,而是在 begin > end 的时候返回 end,为什么是 end 而不是 begin 呢?因为在这里我的判断条件是if (arrayL[mid] <= key),假设我们的前面被搜索部分是1,3,4,而我们要插入的数是 2,那么根据遍历到最后一步的前一步的情况就是如图:,此时因为arrayL[mid] > key。所以最后一步的递归时变为这样 bin_search(arrayL, 1, 0, key) [mid = 1], 所以应该返回end,其他情况下一样。你可以尝试把上面的 <= 变成 < ,然后情况差不多的,,只是返回的值的条件判断和返回值有点变化了。
下面的是三个insert方式的源码:
#include <iostream> #include <stdio.h> using namespace std; void insertSort_1(int *arrayL, int len) { int k = 0; for (int i = 1; i != len; i++) { if (arrayL[i] < arrayL[i - 1]) { k = i - 1; int temp = arrayL[i]; while (k >= 0 && arrayL[k] > temp) { arrayL[k + 1] = arrayL[k]; k--; } arrayL[k + 1] = temp; } } } void swap(int& a, int& b) { int temp = b; b = a; a = temp; } void insertSort_2(int* arrayL, int len) { for (int i = 1; i != len; i++) //如果发现有后面的比前面的大的...这里用数据交换代替数据后移。 //如果a[j]前一个数据a[j-1] > a[j],就交换a[j]和a[j-1],再j--直到a[j-1] <= a[j]。 for (int j = i - 1; j >= 0 && arrayL[j] > arrayL[j + 1]; j--) swap(arrayL[j], arrayL[j + 1]); } int bin_search(int *arrayL, int begin, int end, int key) { int mid = (begin + end) / 2; //找到不大于key的上界的位置 if (begin > end) { return end; } else { if (arrayL[mid] <= key) return bin_search(arrayL, mid + 1, end, key); else return bin_search(arrayL, begin, mid - 1, key); } } void insertSort_3(int *arrayL, int len) { for (int i = 1; i != len; i++) { if (arrayL[i] < arrayL[i - 1]) { int temp = arrayL[i]; int k = bin_search(arrayL, 0, i - 1, arrayL[i]); for (int j = i - 1; j > k; j--) arrayL[j + 1] = arrayL[j]; //在小于key的上界的前面插入 arrayL[k + 1] = temp; } } } void output(int *arrayL, int len) { for (int i = 0; i != len; i++) printf("%d ", arrayL[i]); } int main(int argc, char const *argv[]) { int lenOfArray; int *arrayL; printf("Enter the lenth of the array for in_1: "); scanf("%d", &lenOfArray); printf("Enter the element to sort for in_1: "); arrayL = new int[lenOfArray]; for (int i = 0; i != lenOfArray; i++) scanf("%d", &arrayL[i]); insertSort_1(arrayL, lenOfArray); output(arrayL, lenOfArray); delete[] arrayL; printf(" Enter the lenth of the array for in_2: "); scanf("%d", &lenOfArray); printf(" Enter the element to sort for in_2: "); arrayL = new int[lenOfArray]; for (int i = 0; i != lenOfArray; i++) scanf("%d", &arrayL[i]); insertSort_2(arrayL, lenOfArray); output(arrayL, lenOfArray); delete[] arrayL; printf(" Enter the lenth of the array for in_3: "); scanf("%d", &lenOfArray); printf(" Enter the element to sort for in_3: "); arrayL = new int[lenOfArray]; for (int i = 0; i != lenOfArray; i++) scanf("%d", &arrayL[i]); insertSort_3(arrayL, lenOfArray); output(arrayL, lenOfArray); delete[] arrayL; return 0; }