几种排序的总结(含重复用元素): https://blog.csdn.net/weixin_46544385/article/details/107131501
快排一:
遇到相同的元素,元素可能在前半部分,也可能在后半部分。
8,8,0,9,4,10,5
8,0,9,4,10,8
最小的K个数(剑指LeetCode)
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k==0 || arr.length == 0){
return new int[0];
}
return quickSort(arr,0,arr.length-1,k-1);
}
int[] quickSort(int[] arr,int low,int high,int k){
int j = partition(arr,low,high);
if(j==k){
return Arrays.copyOfRange(arr,0,j+1);
}
return j>k?quickSort(arr,low,j-1,k):quickSort(arr,j+1,high,k);
}
int partition(int[] arr,int low,int high){//pivot是value,不是索引index
int pivot = arr[low];
int i = low,j = high+1;
while(true){
while(++i<=high && arr[i]<pivot);
while(--j>=low && arr[j]>pivot);
if(i>=j){
break;
}
int tmp = arr[i];arr[i] = arr[j];arr[j]=tmp;
}
//最后用j交换第一个元素,为什么用j呢?因为j是走到arr[j]<=pivot停止的,j指向的元素<=pivot,可将j指向的元素调到第一个位置;
arr[low] = arr[j];
arr[j] = pivot;
return j;
}
}
剑指 Offer 45. 把数组排成最小的数
总结一:含重复元素的快排模板
题目:912. 排序数组
例子,8 15 4 9 0 8 4 10 8
重复元素可能在左边也可能在右边。
912 快速排序
class Solution {
//快排
public int[] sortArray(int[] nums) {
int i = 0,j = nums.length-1;
quickSort(nums,i,j);
return nums;
}
public void quickSort(int[] nums,int begin,int end){
if(begin < end){
int mid = partition(nums,begin,end);
quickSort(nums,begin,mid-1);
quickSort(nums,mid+1,end);
}
}
public int partition(int[] nums,int low,int high){
int pivot = nums[low];
int i = low, j= high;
while(i<j){
while(i<j && pivot <= nums[j]) j--;
nums[i] = nums[j];
while(i<j && pivot >= nums[i]) i++;
nums[j] = nums[i];
}
nums[i] = pivot;
return i;
}
}
912 堆排序
class Solution {
//堆排序
//初建堆:完美二叉树的叶子节点是堆,从下往上建堆,当前节点的左子树和右子树都是堆
//调整堆,每输出堆顶元素,就将堆从上往下调整
//升序排列:小顶堆还是大顶堆取决于获取元素的方式
public int[] sortArray(int[] nums) {
createHeap(nums);
return nums;
}
//初建堆
public void createHeap(int[] nums){
//初建堆
for(int i=nums.length/2-1;i>=0;i--){
adjustHeap(nums,i,nums.length-1);
}
//堆排序,每次交换根节点与最后一个节点,最后一个节点固定,不参与调整,并从根节点向下调整堆
//从这可以看出:小顶堆得到降序,大顶堆得到升序
for(int i=nums.length-1;i>=0;i--){
int tmp = nums[i];
nums[i] = nums[0];
nums[0] = tmp;
//堆的最后一个元素与堆顶元素交换
adjustHeap(nums,0,i-1);
}
}
//调整堆:调整以begin为根节点的堆,大顶堆
//下标从0开始的完美二叉树,父节点为f,子节点分别为f*2+1,f*2+2
public void adjustHeap(int[] nums,int begin,int end){
int pivot = nums[begin];
int s = 0,f = begin;//s代表son,f代表father
for(s = f *2+1,f = begin;s <= end;){
if(s<end && nums[s]<nums[s+1]) s++;//找最大的孩子下标
if(pivot > nums[s]) break;//最大的孩子(两个孩子)小于要往下调整的pivot,则不再往下调整,接下来可令父节点的值为为pivot
nums[f] = nums[s];//将较大的孩子换到父节点
f = s;//父节点等于较大孩子的索引
s = f * 2 + 1;//孩子的索引为父节点索引*2
}
nums[f] = pivot;//结束,父节点的值为pivot
}
}
912 归并排序
class Solution {
//归并排序,时间O(NlogN),空间O(N)
//拆分数组,归并两个有序数组
public int[] sortArray(int[] nums) {
int[] tmp = new int[nums.length];
mergeSort(nums,0,nums.length-1,tmp);
return nums;
}
//归并排序
void mergeSort(int[] nums,int begin, int end,int[] tmp){
if(begin < end){
int mid = begin + ((end - begin) >> 1);
mergeSort(nums,begin,mid,tmp);
mergeSort(nums,mid+1,end,tmp);
merge(nums,begin,mid,end,tmp);
}
}
//合并两个数组,并将合并的数组放到nums的对应位置
void merge(int[] nums,int begin,int mid,int end,int[] tmp){
//将nums中begin-end的元素复制到临时数组tmp中
//System.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
System.arraycopy(nums,begin,tmp,begin,end-begin+1);
//合并两个有序数组
int i = begin,j = mid+1,k = begin;
while(i<=mid && j <=end){
if(tmp[i] <= tmp[j]) {//这里要写<=,不然会得到不稳定排序
nums[k++] = tmp[i++];
}else{
nums[k++] = tmp[j++];
}
}
while(i<=mid){
nums[k++] = tmp[i++];
}
while(j<=end){
nums[k++] = tmp[j++];
}
}
}