- 小和问题(归并排序)
- 逆序对问题(归并排序)
- partition分数组(快速排序)
- 荷兰国旗问题(快速排序
- 给定一个数组,求如果排序之后,相邻两个数的最大差值,要求时间复杂度为O(N),且不能使用基于比较排序(桶排序)
- 用递归的方式求最大值
1.小和问题(归并排序)
//测试
int[] arr = {1,2,3,4};
int sum = smallSum(arr);
Log.d("jiang", String.valueOf(sum));
//归并排序
public static int smallSum(int[] arr){
if(arr == null || arr.length <2){
return 0;
}
return mergeSort(arr,0,arr.length - 1);
}
private static int mergeSort(int[] arr, int L, int R) {
if(L == R){
return 0;
}
int mid = L + (R-L)/2;
return mergeSort(arr,L,mid) + mergeSort(arr,mid +1 ,R) + merge(arr,L,mid,R);
}
private static int merge(int[] arr, int L, int mid, int R) {
//记录小和
int sum = 0;
//拷贝数组
int[] help = new int[R - L + 1];
//拷贝数组指针
int i = 0;
//左指针
int pl = L;
//右指针
int p2 = mid + 1;
while (pl <= mid && p2 <= R){
sum += arr[pl] < arr[p2] ? (R-p2+1)*arr[pl] : 0;
help[i++] = arr[pl] < arr[p2] ? arr[pl++] : arr[p2++];
}
while (pl <= mid){
help[i++] = arr[pl++];
}
while(p2 <= R){
help[i++] = arr[p2++];
}
for (i = 0;i<help.length;i++){
arr[L + i] = help[i];
}
return sum;
}
2.逆序对问题(归并排序)
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的所有逆序对
//测试
int[] arr = {4,3,2,1};
reverseOrderPair(arr);
//归并排序
public static void reverseOrderPair(int[] arr){
if (arr == null || arr.length < 2){
return;
}
mergeSort(arr,0,arr.length -1);
}
private static void mergeSort(int[] arr, int L, int R) {
if (L == R){
return;
}
int mid = L + (R - L)/2;
mergeSort(arr,L,mid);
mergeSort(arr,mid+1,R);
merge(arr,L,mid,R);
}
private static void merge(int[] arr, int L, int mid, int R) {
//拷贝数组
int[] help = new int[R - L +1];
//拷贝数组指针
int i = 0;
//左指针
int m = L;
//右指针
int n = mid + 1;
//
while (m <= mid && n <= R){
//如果左指针>右指针
//那么当前左边的数 必定> 右边当前所指的数,及其以后的数
if (arr[m] > arr[n]){
for (int j = n;j <= R;j++){
Log.d("jiang","(" + arr[m] +","+ arr[j] + ")");
}
}
//从大到小的顺序排序
help[i++] = arr[m] > arr[n] ? arr[m++] : arr[n++];
}
while (m <= mid){
help[i++] = arr[m++];
}
while (n <= R){
help[i++] = arr[n++];
}
//拷贝回原数组
for(i = 0;i < help.length;i++) {
arr[L + i] = help[i];
}
}
3. partition分
(1)双指针法
思路:左指针指向arr[0],右指针指向arr[arr.length - 1]
从左指针开始移动,遇到 arr[i] < num ->左指针下移
遇到 arr[i] > num ->左指针当前数与右指针当前数做交换,且右指针上移
直到左指针与右指针重合,结束
//测试
public static void main(String[] args) {
int[] arr = {9,1,3,5,7,0};
doublePointer(arr,0,arr.length -1,3);
for (int a : arr){
System.out.println(a);
}
}
//双指针法
private static void doublePointer(int[] arr, int L, int R, int num) {
//左指针
int Left = L;
//右指针
int Right = R;
while (Left < Right){
if (arr[Left] <= num){
Left++;
}else {
swap(arr,Left++,Right--);
}
}
}
private static void swap(int[] arr, int left, int right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
时间复杂度:
O(N)
(2)类似快排方法
思路:
假设 小于num的区间一开始为arr[-1]~arr[-1]
则下标arr[-1]~arr[-1]上的数 都小于 num
小标从arr[0]开始
当arr[i] > num 时,指针下移,不做操作
当arr[i] < num时,指针当前数 与 小于区间下一个数作交换,小于区间扩大一位,指针下移
直到当前下标指向右边界停止
//测试
public static void main(String[] args) {
int[] arr = {9,1,3,5,7,0};
partition(arr,0,arr.length -1,7);
for (int a : arr){
System.out.println(a);
}
}
//快排
private static void partition(int[] arr,int L,int R,int num) {
//小于区间指针
int x = L -1;
//当前指针
int cur = L;
while (cur <= R){
if(arr[cur] > num){
cur++;
}else {
swap(arr,++x,cur++);
}
}
}
private static void swap(int[] arr, int x, int cur) {
int tmp = arr[x];
arr[x] = arr[cur];
arr[cur] = tmp;
}
时间复杂度:
O(N)
4. 荷兰国旗问题
思路:
类似使用快排的方式解决Partition分数组问题
小区间从arr[-1]~arr[-1]开始
大区间从arr[arr.length] ~ arr[arr.length]开始
当前指针指向arr[0]
当 arr[cur] < num,arr[cur]与arr[less+1]作交换,cur下移
当arr[cur] = num,不作操作,cur下移
当arr[cur] > num,arr[cur] 与arr[more-1]作交换,cur不变
直到cur=more-1停止
//测试
public static void main(String[] args) {
int[] arr = {5,8,5,6,0,1,2,3,4,7};
etherlandnsNationalFlag(arr,0,arr.length-1,3);
for (int a : arr){
System.out.println(a);
}
}
//利用类似快排的方式
private static void etherlandnsNationalFlag(int[] arr, int L, int R, int num) {
//当前指针
int cur = 0;
//左小于区间
int less = L -1;
//右大于区间
int more = R +1;
while (cur < more){
if (arr[cur] < num){
swap(arr,++less,cur++);
}else if (arr[cur] == num){
cur++;
}else {
swap(arr,--more,cur);
}
}
}
private static void swap(int[] arr, int x, int cur) {
int tmp = arr[x];
arr[x] = arr[cur];
arr[cur] = tmp;
}
8.给定一个数组,求如果排序之后,相邻两个数的最大差值,要求时间复杂度为O(N),且不能使用基于比较排序(计数排序)
时间复杂度为O(N)说明只遍历一次数组
!!!借用了桶的概念,但是不是桶排序
思路:
1.有N个数,就准备N+1个桶
2.遍历找到最小值和最大值,如果min = max ,差值为0
3.如果min != max,将min放在下标为[0]的1号桶,max放在小标为[N]的N+1号桶
4.将中间的数按比例放在中间桶中
5.记录各个桶的最小值和最大值
6.遍历:用当前非空桶的最小值减去上一个非空的的最大值找到最大差值
//给定一个数组,求如果排序之后,相邻两个数的最大差值,要求时间复杂度为O(N),且不能使用非基于比较排序(桶排序概念)
public class MaxGap8 {
public static void main(String[] args) {
int[] arr = new int[]{0,99,87,56,48,56,12,78,65};
int i = maxGap(arr);
System.out.println(i);
}
public static int maxGap(int[] nums){
if (nums == null || nums.length < 2){
return 0;
}
int len = nums.length;
//保证在第一次比较的时候会进行交换
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
//找到最小值和最大值
for (int i = 0;i < len;i ++){
min = Math.min(min,nums[i]);
max = Math.max(max,nums[i]);
}
if (max == min){
return 0;
}
//表示 N+1 个桶 -> 1号桶下标为0,2号桶下标为1.....
boolean[] hasNum = new boolean[len + 1];
int[] maxs = new int[len + 1];
int[] mins = new int[len + 1];
//标记桶下标
int bid = 0;
for (int i = 0;i < len;i++){
//算出应该放入的桶的下标
bid = bucket(nums[i],len,min,max);
//如果 hasNum[bid]=false,表示当前桶内没有数,应当放入num[i]
//如果 hasNum[bid]=true, 表示当前桶内有数, 应该比较mins[bid]和num[i]的大小
mins[bid] = hasNum[bid] ? Math.min(mins[bid],nums[i]) : nums[i];
maxs[bid] = hasNum[bid] ? Math.min(maxs[bid],nums[i]) : nums[i];
hasNum[bid] = true;
}
int res = 0;
int lastMax = maxs[0];
for (int i = 1;i <= len;i++){
if (hasNum[i]){
res = Math.max(res,mins[i] - lastMax);
lastMax = maxs[i];
}
}
return res;
}
//确定放在哪个桶里
public static int bucket(long num, long len, long min, long max) {
//(num-min)/(max-min)算出来的是比例
//之所以*len 不是(len+1) ,是因为这是数组,1号桶下标为0,2号桶下标为1.....
return (int) ((num - min) * len / (max - min));
}
}
6.用递归的方式求最大值
public class _6_getMax {
public static void main(String[] args) {
int arr[] = {9,8,3,0,2,1,5,7};
int max = getMax(arr,0,arr.length-1);
System.out.println(String.valueOf(max));
}
public static int getMax(int[] arr,int L,int R){
if (L == R){
return arr[L];
}else{
int mid = L + (R-L)/2;
int maxLeft = getMax(arr,L,mid);
int maxRight = getMax(arr,mid+1,R);
return Math.max(maxLeft,maxRight);
}
}
}