算法复习,代码是最好的说明!
一、冒泡排序:
算法:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
代码:
/**
* 冒泡排序
* @param array
*/
void bubbleSort_1(int[] array){
for (int i = array.length-1; i >0 ; i--) {
for (int j = 0; j <i ; j++) {
if (array[j]>array[j+1]){
swap(array,j,j+1);
}
}
}
}
/**
* 冒泡排序改进,标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程
* @param array
*/
void bubbleSort_2(int[] array){
boolean isSwaped;
for (int i = array.length-1; i >0 ; i--) {
isSwaped = false;
for (int j = 0; j <i ; j++) {
if (array[j]>array[j+1]){
swap(array,j,j+1);
isSwaped = true;
}
}
if (!isSwaped)
break;
}
}
/**
* 冒泡排序改进,设置一个pos指针,pos后面的数据在上一轮排序中没有发生交换,下一轮排序时,就对pos之后的数据不再比较
* @param array
*/
void bubbleSort_3(int[] array){
int end = array.length - 1;
while (end>0) {
int pos = 1;
for (int j = 1; j <=end; j++) {
if (array[j-1] > array[j]) {
swap(array, j-1, j);
pos = j;
}
}
end = pos - 1;
}
}
/**
* 鸡尾酒排序,也叫定向冒泡排序,是冒泡排序的一种改进。此算法与冒泡排序的不同处在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素。他可以得到比冒泡排序稍微好一点的效能。
* @param array
*/
void bubbleSort_4(int[] array){
int left = 0;
int right = array.length-1;
while (left < right){
for (int i = left; i <right ; i++) {
if (array[i] > array[i+1]) swap(array,i,i+1);
}
right--;
for (int i = right; i >left ; i--) {
if (array[i] < array[i-1]) swap(array,i,i-1);
}
left++;
}
}
void swap(int[] array,int i,int j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
二、选择排序:
算法:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
代码:
/**
* 选择排序
* @param array
*/
void selectionSort_1(int[] array){
for (int i = 0; i <array.length-1 ; i++) {
int min = i;
for (int j = i+1; j < array.length; j++) {
if (array[j]<array[min]){
min = j;
}
}
if (min!=i) swap(array,min,i);
}
}
/**
* 选择排序改进,每趟排序确定两个最值——最大值与最小值,这样就可以将排序趟数缩减一半。
* @param array
*/
void selectionSort_2(int[] array){
int left = 0;
int right = array.length-1;
while (left<right){
int min = left;
int max = right;
for (int i = left; i <=right; i++) {
if (array[i]<array[min]) min = i;
if (array[i]>array[max]) max = i;
}
if (min!=left) swap(array,min,left);
if (max!=right&&max!=left) swap(array,max,right);
left++;
right--;
}
}
三、插入排序:
1、直接插入排序:
算法:
从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置后
重复步骤2~5
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
代码:
/**
* 插入排序,写法一
* @param array
*/
void insertionSort_1(int[] array){
for (int i = 1; i < array.length; i++) {
int get = array[i];
int j = i-1;
while (j>=0&&array[j]>get){
array[j+1] = array[j];
j--;
}
array[j+1] = get;
}
}
/**
* 插入排序,写法二
* @param array
*/
void insertionSort_2(int[] array){
for (int i = 1; i < array.length; i++) {
for (int j = i; j >0 ; j--) {
if (array[j]<array[j-1]) swap(array,j,j-1);
else break;
}
}
}
2、二分插入排序:
算法:
利用二分法的思想对插入排序进行改进。
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(n^2) | O(nlogn) | O(n^2) | O(1) | 不稳定 |
代码:
/**
* 插入排序改进二分法插入
* @param array
*/
void insertionSortDichotomy(int[] array){
for (int i = 1; i < array.length; i++) {
int get = array[i];
int left = 0;
int right = i - 1;
while (left<=right){
int mid = (left+right)/2;
if (array[mid]<get)
left = mid + 1;
else
right = mid - 1;
}
for (int j = i-1; j >=left; j--) {
array[j+1] = array[j];
}
array[left] = get;
}
}
3、希尔插入排序:
算法:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
代码:
/**
* 插入排序改进希尔排序,写法一
* @param array
*/
void shellSort_1(int[] array){
for (int gap = array.length/2; gap >0 ; gap/=2) {
for (int i = 0; i < gap; i++) {
for (int j = i+gap; j < array.length; j+=gap) {
int get = array[j];
int k = j-gap;
while (k>=0&&array[k]>get){
array[k+gap]=array[k];
k-=gap;
}
array[k+gap] = get;
}
}
}
}
/**
* 插入排序改进希尔排序,写法二
* @param array
*/
void shellSort_2(int[] array){
for (int gap = array.length/2; gap >0 ; gap/=2) {
for (int i = 0; i < gap; i++) {
for (int j = i+gap; j < array.length; j+=gap) {
for (int k = j; k >=gap ; k-=gap) {
if (array[k]<array[k-gap]) swap(array,k,k-gap);
else break;
}
}
}
}
}
四、归并排序:
算法:
归并排序的实现分为递归实现与非递归(迭代)实现。归并排序算法主要依赖归并(Merge)操作,归并操作指的是将两个已经排序的序列合并成一个序列的操作
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
代码:
/**
* 将两个已经排序的序列合并成一个序列的操作
* @param array
* @param left
* @param mid
* @param right
*/
void merge(int[] array,int left,int mid,int right){
int[] temp = new int[right-left+1];
int index = 0;
int i = left;
int j = mid+1;
while (i<=mid&&j<=right)
temp[index++] = array[i] <= array[j] ? array[i++] : array[j++];
while (i<=mid)
temp[index++] = array[i++];
while (j<=right)
temp[index++] = array[j++];
for (int k = 0; k <right-left+1; k++)
array[left+k] = temp[k];
}
/**
* 递归实现
* @param array
* @param left
* @param right
*/
void MergeSortRecursion(int[] array,int left,int right){
if (left==right) return;
int mid = (left+right)/2;
MergeSortRecursion(array,left,mid);
MergeSortRecursion(array,mid+1,right);
merge(array,left,mid,right);
}
/**
* 非递归实现
* @param array
*/
void MergeSortIteration(int[] array){
int left,mid,right;
for (int i = 1; i < array.length; i*=2) {
left = 0;
while (left+i < array.length){
mid = left + i - 1;
right = mid + i < array.length ? mid + i : array.length - 1;
merge(array,left,mid,right);
left = right + 1;
}
}
}
五、快速排序:
算法:
从序列中挑出一个元素,作为"基准"(pivot)。
把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
分析:
平均 | 最好 | 最坏 | 辅助存储 | 稳定性 |
O(nlogn) | O(nlogn) | O(n^2) | O(nlogn) | 不稳定 |
代码:
/**
*
* @param array
* @param left
* @param right
* @return
*/
int partition_1(int[] array, int left, int right){
int pivot = array[right];
int tail = left-1;
for (int i = left; i < right; i++) {
if (array[i] < pivot) swap(array,++tail,i);
}
swap(array,++tail,right);
return tail;
}
/**
* 改进将pivot的选择随机化,不再是固定选择左端点,或采用三支取中
* @param array
* @param left
* @param right
* @return
*/
int partition_2(int[] array, int left, int right){
swap(array,right, left+new Random().nextInt(right-left+1));
int pivot = array[right];
int tail = left-1;
for (int i = left; i < right; i++) {
if (array[i] < pivot) swap(array,++tail,i);
}
swap(array,++tail,right);
return tail;
}
/**
* 改进使用两个索引i和j,分别从左右两端进行扫描,i扫描到大于等于pivot的元素就停止,j扫描到小于等于pivot的元素也停止,交换两个元素,持续这个过程直到两个索引相遇,此时的pivot的位置就落在了j,然后交换pivot和j的位置
* @param array
* @param left
* @param right
* @return
*/
int partition_3(int[] array, int left, int right){
swap(array,right, left+new Random().nextInt(right-left+1));
int pivot = array[right];
int i = left - 1;
int j = right;
while (true){
do {
i++;
}while (i<=right&&array[i]<pivot);
do {
j--;
}while (j>=left&&array[j]>pivot);
if (j < i) break;
swap(array,i,j);
}
swap(array,++j,right);
return j;
}
/**
*
* @param array
* @param left
* @param right
*/
void quickSort(int[] array, int left, int right){
if (left>=right) return;
int pivot_index = partition_3(array,left,right);
quickSort(array,left,pivot_index-1);
quickSort(array,pivot_index+1,right);
}
参考链接:
http://www.cnblogs.com/eniac12/p/5329396.html#s5
http://blog.csdn.net/morewindows/article/details/6668714
http://blog.csdn.net/hguisu/article/details/7776068
http://www.blogjava.net/killme2008/archive/2010/09/08/quicksort_optimized.html
如有错误,欢迎纠正!