一、快速排序
快速排序被参考文献一种的大神比喻成挖坑填坑排序算法,这个比喻非常形象
(1)首先将左边的第一个数挖出记为temp,这就形成了一个坑,从右边开始寻找,当有数比temp这个数小的时候就拿右边的这个数填充左边这个坑,然后他自己的位置又形成了一个坑。
(2)然后再从左向右找,当有数大于temp的时候,就将左边这个数填入刚刚右边形成的坑。
(3)当两个指针i=j的时候将temp值放入。此时temp的坐标都是小于他的数,右边都是大于他的数。
public static int AdjustArray(int s[], int left, int right){
int i = left, j = right;
int temp = s[left];//s[left]就是s[i]的第一个坑
while(i < j){
//从右向左寻找小于x的数来填s[i]
while(i < j && s[j] > temp){
j--;
}
if(i < j){
s[i] = s[j];//将s[j]的数填到s[i]中,同时s[j]处形成一个新的坑
i++;
}
//开始从左向右寻找大于temp的数据来填s[j]的坑
while(i < j && s[i] < temp){
i++;
}
if(i < j){
s[j] = s[i];//将s[i]填到s[j]中,s[i]就形成了一个新的坑
j--;
}
}
//退出时,i = j,将x填到这个坑中
s[i] = temp;
return i;
}
/**
* 快速排序算法一
* @param s
* @param left
* @param right
*/
public static void quickSort1(int s[], int left, int right){
if(left < right){
int i = AdjustArray(s, left, right);
quickSort1(s, left, i - 1);
quickSort1(s, i + 1, right);
}
}
/**
* 快速排序算法二
* @param s
* @param left
* @param right
*/
public static void quickSort2(int s[], int left, int right){
if(left < right){
int i = left;
int j = right;
int temp = s[left];
while(i < j){
while(i < j && s[j] >= temp){
j--;
}
if(i < j){
s[i++] = s[j];
}
while(i < j && s[i] <= temp){
i++;
}
if(i < j){
s[j--] = s[i];
}
}
s[i] = temp;
quickSort2(s, left, i-1);
quickSort2(s, i+1, right);
}
}
二、直接插入排序
(1)找出算法中的基本语句
算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
(2)计算基本语句的执行次数的数量级
只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数
(3)用大Ο记号表示算法的时间性能
将基本语句执行次数的数量级放入大Ο记号中。
如下是两种快速排序的实现形式,第一种是严格按照定义来的,第二种是优化后的
/**
* 直接插入排序算法1
* @param a
* @param n
*/
public static void Insertsort1(int a[], int n){
int i, j, k;
for(i = 1; i < n; i++){
//给a[i]在a[0]--a[i-1]出寻找合适的位置
for(j = i-1; j >= 0; j--){
if(a[j] < a[i]){
break;
}
}
if(j != i-1){
//将比a[i]大的数向后移动
int temp = a[i];
for(k = i - 1; k > j; k--){
//当执行完最后一次的时候k=j了
a[k+1] = a[k];
}
//将a[i]防盗正确的位置上
a[k+1] = temp;
}
}
}
/**
* 直接插入排序算法2
* @param a
* @param n
*/
public static void Insertsort2(int a[], int n){
int i, j;
for(i = 1; i < n; i++){
if(a[i] < a[i-1]){
int temp = a[i];
for(j = i -1; j >=0 && a[j] > temp; j--){
a[j+1] = a[j];
}
a[j+1] = temp;
}
}
}
三、直接选择排序
选择排序和插入排序时非常类似的,只不过其将排序的过程放到了选择上
也就是不断从剩余的元素中选择出最小的元素
如下也是两种直接插入排序的算法,第二种是采用交换的方式
/**
* 直接选择排序算法1
* @param a
* @param n
*/
public static void Selectsort1(int a[], int n){
int temp;
for(int i = 0; i < n; i++){
for(int j = n-1; j > i; j--){
if(a[j] < a[j-1]){
temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
}
}
}
}
/**
* 直接选择排序
* @param a
* @param n
*/
public static void Selectsort2(int a[], int n){
int i, j, minIndex;
for(i = 0; i < n; i++){
minIndex = i;
for(j = i+1; j < n; j++){
if(a[j] < a[minIndex]){
minIndex = j;
}
}
//交换两个元素的位置
int temp = a[i];
a[i] = a[minIndex];
a[minIndex] = temp;
}
}
四、归并排序
归并排序是一种比较好的排序方式,其使用的是分制的思想,和快速排序的时间复杂度是类似的,其更加的稳定
(1)将数据不断的分成两组两组,然后再将这些有序的数组不断的合并
(2)最终形成一个大的有序数组
/**
* 将数组拆分成左右两个有序的数组
* @param a
* @param first
* @param mid
* @param last
* @param temp
*/
public static void merageArray(int a[], int first, int mid, int last, int temp[]){
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
//将建立的新数组temp
while(i <= m && j <= n){
if(a[i] < a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
//将数据进行复制
for (i = 0; i < k; i++)
a[first + i] = temp[i];
}
/**
* 归并排序
* @param a
* @param first
* @param last
* @param temp
*/
public static void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
merageArray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
//往堆的二叉树中插入节点,新加入i节点,其父节点为(i+1)/2
public static void MinHeapFixup(int a[], int i){
int j, temp;
temp = a[i];
j = (i + 1)/2; //其父节点
while(j > 0 && i != 0){
//如果其父节点比他小,跳出循环
if(a[j] < temp){
break;
}
//否则将大一的点的子节点向下移动
a[i] = a[j];
i = j;
j = (i - 1)/2;
}
//最后将元素插入到适当的位置
a[i] = temp;
}
//堆的删除操作
// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
public static void MinHeapFixdown(int a[], int i, int n){
//[1]最后一个结点和第一个结点交换
int temp = a[i], j;
//[2]找到左右孩子中小的
j = 2 * i + 1;
while(j < n){
//在左右孩子中寻找最小的
if (j + 1 < n && a[j + 1] < a[j]){
j++;
}
//如果他比最小的子结点小,跳出循环
if(temp <= a[j]){
break;
}
//否则将小的子节点向上移动
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
a[i] = temp;
}
public static void MinheapsortTodescendarray(int a[], int n)
{
for (int i = n-1; i >= 1; i--)
{
Swap(a, i, 0);
MinHeapFixdown(a, 0, i);
}
}
//交换数组中的两个数
public static void Swap(int a[], int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//堆的建立
public static void MakeMinHeap(int a[], int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
MinHeapFixdown(a, i, n);
}
//希尔排序算法1
public static void shellsort1(int a[], int n)
{
int i, j, gap;
for (gap = n / 2; gap > 0; gap /= 2) //步长
for (i = 0; i < gap; i++) //直接插入排序
{
for (j = i + gap; j < n; j += gap)
if (a[j] < a[j - gap])
{
int temp = a[j];
int k = j - gap;
while (k >= 0 && a[k] > temp)
{
a[k + gap] = a[k];
k -= gap;
}
a[k + gap] = temp;
}
}
}
下面是 比较好理解的版本,最好看这个
//希尔排序
public static void shellSort(int num[]){
int i, j, gap;
//得到长度
int n = num.length;
//步长逐渐变短
for(gap = n/2; gap > 0; gap /= 2){
//按照步长来进行遍历
for(i = gap; i < n; i += gap){
//还是插入排序的思想,从后往前进行遍历和交换
for (j = i - gap; j >= 0; j -= gap){
if(num[j] > num[j + gap]) {
int temp = num[j];
num[j] = num[j + gap];
num[j + gap] = temp;
}
}
}
}
}
/**
* 希尔排序
* @param arrays 需要排序的序列
*/
public static void sort(int[] arrays){
if(arrays == null || arrays.length <= 1){
return;
}
//增量
int incrementNum = arrays.length/2;
while(incrementNum >=1){
for(int i=0;i<arrays.length;i++){
//进行插入排序
for(int j=i;j<arrays.length-incrementNum;j=j+incrementNum){
if(arrays[j]>arrays[j+incrementNum]){
int temple = arrays[j];
arrays[j] = arrays[j+incrementNum];
arrays[j+incrementNum] = temple;
}
}
}
//设置新的增量
incrementNum = incrementNum/2;
}
}
如下是几种排序算法的对比图