算法思想:首先了解什么是堆,这里对于堆的定义有两种,一种为最大堆,即父节点都大于子节点,如图一所示;另一种为最小堆,即父节点都小于子节点,如图二所示。
用堆排序进行升序排序, 我们使用大堆来完成。我们将数组初始化成最大堆, 记录数组长度为 len, 只需将堆顶元素与 len-1 位置元素调换位置, 这样此时堆中最大元素就确定了, 此时最大值已经确定, 只需要比较剩余的, 即 len--, 再次调用adjust(arr,0,len),直到 len 等于 0 上述操作执行完毕, 整个排序也就结束了。
如图所示即为一次调整最大堆:
代码实现:
//堆排序
public static void adjust(int[] arr,int start, int end){//一次调整大根树
int tmp = arr[start];
for (int i = 2 * start + 1; i <= end; i = 2*i + 1) {
//1.找到左右孩子最大值的下标
if( (i < end) && (arr[i] < arr[i + 1])){
i++; //nlog2n(以二为底N)
}
if(arr[i] > tmp){
arr[start] = arr[i];
start = i;
}else if(arr[i] < tmp){
break;
}
}
arr[start] = tmp;
}
public static void heapSort(int[] arr){ //nlog2n(以二为底N) // 空间复杂O(1)
for (int i = (arr.length - 1 - 1)/2; i >= 0; i--) {
adjust(arr,i,arr.length - 1);
}
for (int i = 0; i < arr.length - 1; i++) {//交换根节点与孩子节点
int temp = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = arr[0];
arr[0] = temp;
//length - 1 - i - 1 有序之后,不需要调整最后一个有序数据
adjust(arr,0,arr.length - 1 - i - 1);
}
}
基本思想:设置两个搜索指针 low 和 high, 它们分别指向首尾, 一般将 low 位置元素设置为支点元素, 从 high 位置开始搜索比支点小的记录, 并将其交换到 low 位置处, low 向后移动一个位置, 然后从 low 位置开始搜索比支点大的位置, 并将其交换到 high 位置处,high 向前移动一个位置如此继续, 直到 low 和 high 相等结束, 这时支点前面的数全小于它, 后面全比它大, 然后将支点前面和后面的序列继续进行排序, 直到完成排序。
一次快排过程如图所示:
代码实现:
//时间复杂度 logN
//一次快速排序(递归实现) 时间复杂度N
public static int partion(int[] arr,int low,int hight){
int temp = arr[low];
while(low < hight){
while (low < hight && arr[hight] >= temp){
hight--;
}
if(low == hight){
break;
}else{
arr[low] = arr[hight];
}
while(low < hight && arr[low] <= temp){
low++;
}
if(low == hight){
break;
}else{
arr[hight] = arr[low];
}
}
arr[low] = temp;
return low;
}
public static void quick(int[] arr,int start,int end){
if(end - start + 1 <= 16){
insert1(arr,start,end);
}
//三数取中法大大减少递归次数
//meedofThree(arr,start,end);
int par = partion(arr,start,end);
//找左边是否有两个数据以上
if(par > start + 1){
quick(arr,start,par - 1);
}
//找右边是否有两个数据以上
if(par < end - 1){
quick(arr,par + 1,end);
}
}
//空间复杂度为N 时间复杂度N * logN
public static void quickSort(int[] arr){
quick(arr,0,arr.length -1);
}
非递归代码实现:
//快排非递归实现(使用栈完成)
public static void quickUnRecursion(int[] arr){
int[] stack = new int[arr.length * 2];
int top = 0;
int low = 0;
int high = arr.length - 1;
//先进行一次快排
int par = partion(arr,low,high);
//1.判断当前par左右两边是否有两个数据以上
if(par > low + 1){
stack[top++] = low;
stack[top++] = par - 1;
}
if(par < high - 1){
stack[top++] = par + 1;
stack[top++] = high;
}
//以上代码执行完毕,两边的数对已将全部入栈
//判断栈是否为空,不为空取出两个数对,进行partion()
while(top > 0){
high = stack[--top];
low = stack[--top];
par = partion(arr,low,high);
if(par > low + 1){
stack[top++] = low;
stack[top++] = par - 1;
}
if(par < high - 1){
stack[top++] = par + 1;
stack[top++] = high;
}
}
}
基本思想: 分而治之(divide - conquer);每个递归过程涉及两个步骤。第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素。第二, 合并: 合并两个排好序的子序列,生成排序结果。
过程如下图:
代码实现:
//归并排序(使用分治算法)
public static void mergeSort(int[] arr,int start,int end){
if(start == end){
return;
}
int mid = (start + end) / 2;
mergeSort(arr,start,mid);
mergeSort(arr,mid+1,end);
//合并的过程,此时一定为一个有序序列
merge(arr,start,mid,end);
}
public static void merge(int[] arr,int start,int mid,int end){
int[] str = new int[arr.length];
int index = start;
int start2 = mid + 1;
int i = start;
while(start <= mid && start2 <= end){
if(arr[start] <= arr[start2]){
str[index++] = arr[start++];
}else{
str[index++] = arr[start2++];
}
}
while(start <= mid){
str[index++] = arr[start++];
}
while(start2 <= end){
str[index++] = arr[start2++];
}
while(i <= end){
arr[i] = str[i];
i++;
}
System.out.println(Arrays.toString(arr));
}