一.快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
方法一:韩顺平讲解
public static void quickSort(int[] arr,int left, int right) {
int l = left; //左下标
int r = right; //右下标,这里起始传参数时要,输入数组最后一个下标。
//pivot 中轴值
int pivot = arr[(left + right) / 2];
int temp = 0; //临时变量,作为交换时使用
//while循环的目的是让比pivot 值小放到左边
//比pivot 值大放到右边
while( l < r) {
//在pivot的左边一直找,找到大于等于pivot值,才退出
while( arr[l] < pivot) {
l += 1;
}
//在pivot的右边一直找,找到小于等于pivot值,才退出
while(arr[r] > pivot) {
r -= 1;
}
//如果l >= r说明pivot 的左右两的值,已经按照左边全部是
//小于等于pivot值,右边全部是大于等于pivot值
if( l >= r) {
break;
}
//交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交换完后,发现这个arr[l] == pivot值 相等 r--, 前移
if(arr[l] == pivot) {
r -= 1;
}
//如果交换完后,发现这个arr[r] == pivot值 相等 l++, 后移
if(arr[r] == pivot) {
l += 1;
}
}
// 如果 l == r, 必须l++, r--, 否则为出现栈溢出
if (l == r) {
l += 1;
r -= 1;
}
//向左递归
if(left < r) {
quickSort(arr, left, r);
}
//向右递归
if(right > l) {
quickSort(arr, l, right);
}
}
//方法二:递归的另一种写法,这里的划分的选取的最左边的值做参考;剑指offer-取第k小的值
private int[] quickSort(int[] arr, int l, int r) {
// 子数组长度为 1 时终止递归
if (l >= r) return;
// 哨兵划分操作(以 arr[l] 作为基准数)
int i = l, j = r;
while (i < j) {
while (i < j && arr[j] >= arr[l]) j--;
while (i < j && arr[i] <= arr[l]) i++;
swap(arr, i, j);
}
swap(arr, i, l);
// 递归左(右)子数组执行哨兵划分
quickSort(arr, l, i - 1);
quickSort(arr, i + 1, r);
}
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
1.快排变形:求数组的k个小值
2.快排:比较值字符变形
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
题目链接:力扣
class Solution {
public String minNumber(int[] nums) {
//1.将数组转为字符数组
String[] strs = new String[nums.length];
for(int i =0;i<nums.length;i++){
strs[i]=String.valueOf(nums[i]);
}
//2.快排
quickSort(strs,0,strs.length-1);
//3.将字符数组转为字符串,此时借助stringbuffer
StringBuilder res = new StringBuilder();
for(String num:strs){
res.append(num);
}
return res.toString();
}
//根据本题特定的规则进行快排
public void quickSort(String[] strs,int l,int r){
//跳出递归的条件
if(l>=r) return;
int i = l;
int j = r;
String tem=strs[i];
while(i<j){
while(i<j && (strs[j]+strs[l]).compareTo(strs[l]+strs[j])>=0) j--;
while(i<j && (strs[i]+strs[l]).compareTo(strs[l]+strs[i])<=0) i++;
//交换两个值
tem = strs[i];
strs[i]=strs[j];
strs[j]=tem;
}
//交换l,i值
strs[i]=strs[l];
strs[l]=tem;
//向左右递归
quickSort(strs,l,i-1);
quickSort(strs,i+1,r);
}
}
二、归并排序
1、归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
public static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;
int j = mid+1;
int t = 0;
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t] = arr[i];
t += 1;
i += 1;
}else {
temp[t] = arr[j];
t += 1;
j += 1;
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t] = arr[i];
t += 1;
i += 1;
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t] = arr[j];
t += 1;
j += 1;
}
t = 0;
int tempLeft = left;
while(tempLeft <= right){
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
1.在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
题目链接:力扣
class Solution {
int count=0;
public int reversePairs(int[] nums) {
//逆序对可以使用归并排序时,进行并时,进行统计
//数组按照从小到大排序
mergeSort(nums,0,nums.length-1);
return count;
}
//temp是用来临时储存数组的
public void mergeSort(int[] nums,int left,int right){
int mid = left +(right-left)/2; //这里求的是中间值,不是平均值
if(left<right){
mergeSort(nums,left,mid);//分
mergeSort(nums,mid+1,right);//分
merge(nums,left,mid,right);//治
}
}
//进行合并
public void merge(int[] nums,int left,int mid,int right){
int[] temp = new int[right-left+1];
//i,j表示两个数组的起始位置
int i = left;
int j = mid+1;
int t=0;
while(i<=mid && j <= right ){
if(nums[i] > nums[j]){
count += (mid -i +1);//这里逆序对,i右边的值都是大的
temp[t++]=nums[j];
j++;
}else{
temp[t++]=nums[i];
i++;
}
}
//将左边数组填充到temp中
while(i<=mid){
temp[t]=nums[i];
t += 1;
i +=1;
}
//将右边数组填充到temp中
while(j<=right){
temp[t]=nums[j];
t +=1;
j +=1;
}
//将数组拷贝到nums数组中
t =0;
int templeft = left;//原来数组的起始位置
while(templeft<=right){
nums[templeft] = temp[t];
t += 1;
templeft +=1;
}
}
}