快速排序:
/* 快速排序 */
//直接交换,better
static int partition(Integer arr[],int low,int high){
int temp,pivot = arr[high];
while(low < high){
/* 查找顺序要和基准选取方向相反;
pivot = arr[high] 则必须先从low开始;
反之 pivot = arr[low], 则必须先从high开始查找 */
while(arr[low] <= pivot && low < high)
low++;
arr[high] = arr[low];//直接覆盖原先的元素
while(arr[high] >= pivot && low < high)
high--;
arr[low] = arr[high];
}
// 校准,将基准移至正确位置
arr[low] = pivot;
System.out.println("low="+low+";high="+high);
return low;
}
static void quickSort(Integer []arr,int low,int high){
if(low < high){
int pivotpos = partition(arr,low,high);
quickSort(arr,low,pivotpos-1);
quickSort(arr,pivotpos+1,high);
}
}
另一种:
static int partition(Integer a[], int low, int high) {
int i = low, j = high;
int temp, pivot = a[i];
while (i < j) {
/*
* 查找顺序要和基准选取相反; pivot = arr[high] 则必须先从low开始;
* 反之 pivot =arr[low],则必须先从high开始查找
*/
/**
* 注意:
* 1,与pivot比较必须是>=,否则在a[j]或a[i]==pivot时,会造成死循环
* 2,内层循环的i<j,保证i,j不会越过对方,因此跳出最外层循环时,i==j
*/
while (a[j] >= pivot && i < j)
j--;
while (a[i] <= pivot && i < j)
i++;
// i,j位置元素,两边交换,此时无需判断i<=j,此条件在该循环中恒成立
int t = a[i];
a[i] = a[j];
a[j] = t;
}
// 校准,将基准值移至正确位置,此时i==j
a[low] = a[i];
a[i] = pivot;
System.out.println("i=" + i + ";j=" + j);// i j必相等
return i;// 返回基准所在位置划分位置
}
static void quickSort(Integer[] a, int low, int high) {
if (low < high) {
int pivotpos = partition(a, low, high);
quickSort(a, low, pivotpos - 1);
quickSort(a, pivotpos + 1, high);
}
}
public static void main(String[] args) {
Integer a[] = { 8,1,4,9,0,3,5,2,7,6};
quickSort(a, 0, a.length - 1);
Arrays.toString(a);
}
第二种
low处自始至终放的是pivot没动,最后需要把i,j指向位置的元素放到low处;
解释为什么满足放到low处:
内层两个while,先从high走,再走low;最后一次while时肯定是先不满足第一个while,第二个while又是i==j不满足;不满足第一个while但是又进得了外层的while,只能说明最后i(或j)指向的元素不满足>=pivot,即元素肯定小于pivot,所以必定满足放到low处。
这也就是为什么“查找顺序要和基准选取的方向相反”
归并排序:
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
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];
}
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); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。
https://visualgo.net/sorting 排序可视化
堆排序
计算堆化时间复杂度时,将父节点与子节点的比较简易化,看本质(其实就是从上到下的单支链表!因为选择堆化结点时,是先从n/2处向前遍历。从上到下比较时,调整的必是或左或右的一颗子树。其实无论是否下面的一堆化,向下调整时,每次选择必走的是单支的啊。。。),其实是该节点的深度(树高H-本层高度h)
http://blog.csdn.net/yuzhihui_no1/article/details/44258297
该博客代码,时间复杂度 均解释十分详细