P3:认识 O(NlogN) 排序
用递归方法求数组内最大值
public int findMax(int[] array, int l, int r) {
if (l == r) {
return array[l];
}
int mid = l + (r - l) >> 1;
int lMax = findMax(array, l, mid);
int rMax = findMax(array, mid + 1, r);
return Math.max(lMax, rMax);
}
求中点技巧
这个方法问题在于 (l + r) 可能会溢出,可以采用下面这个式子
这样解决了溢出问题,可以再进一步
用移位运算代替除法,有可能会更快(具体要看编译器优化)
递归的深度
array = [1,2,3,4,5]
| [1,2,3,4,5] | |||
| [1,2,3] | [4,5] | ||
| [1,2] | [3] | [4] | [5] |
| [1] | [2] |
先计算左边节点,在计算右边节点,子节点向上返回,和二叉树的后序遍历相同,栈的最大深度就等于树的深度
master公式求递归时间复杂度
master公式
上面那个递归中,a = 2,b = 2,d = 0。符合 master 公式
- log(b, a) > d 时间复杂度是
- log(b, a) = d 时间复杂度是
- log(b, a) < d 时间复杂度是
归并排序(merge sort)
public int[] mergeSort(int[] array, int l, int r) {
if (l == r) {
return array;
}
int mid = l + ((r - l) >> 1);
int[] lArray = mergeSort(array, l, mid);
int[] rArray = mergeSort(array, mid + 1, r);
return merge(array, l, r, mid);
}
private int[] merge(int[] array, int l, int r, int mid) {
int[] help = new int[r - l + 1];
int lPoint = l;
int rPoint = mid + 1;
for(int t = 0; t < help.length; t++) {
if(lPoint <= mid && rPoint <= r) {
if(array[lPoint] < array[rPoint]){
help[t] = array[lPoint++];
} else{
help[t] = array[rPoint++];
}
} else if(lPoint == (mid + 1) && rPoint <= r) {
help[t] = array[rPoint++];
} else if(rPoint == (r + 1) && lPoint <= mid>) {
help[t] = array[lPoint++];
}
}
for(int t = 0; t < help.length; t++) {
array[l + t] = help[t];
}
return array;
}
归并排序时间复杂度
a = b =2, d = 1; 时间复杂度 O(NlogN)
求一个数组的小和
现在有一个数组 [2,1,5,8,9,6,3,4],每个数字左边比它小的数就是它小和的个数,例如 6 左侧有 2,1,5 三个数,6的小和就是3,将数组中所有数的小和加起来就是数组的小和
public int smallSum(int[] array) {
return mergeSort(array, 0, array.length - 1);
}
public int mergeSort(int[] array, int l, int r) {
if (l == r) {
return 0;
}
int mid = l + ((r - l) >> 1);
int lSum = mergeSort(array, l, mid);
int rSum = mergeSort(array, mid + 1, r);
//把左子树的结果+右子树的结果+merge的结果返回
return lSum + rSum + merge(array, l, r, mid);
}
private int merge(int[] array, int l, int r, int mid) {
int[] help = new int[r - l + 1];
int sum = 0;
int lPoint = l;
int rPoint = mid + 1;
for(int t = 0; t < help.length; t++) {
if(lPoint <= mid && rPoint <= r) {
if(array[lPoint] < array[rPoint]){
//和归并排序相比,添加小和计算
sum += (r - rPoint + 1) * array[lPoint];
help[t] = array[lPoint++];
} else{
help[t] = array[rPoint++];
}
} else if(lPoint == (mid + 1) && rPoint <= r) {
help[t] = array[rPoint++];
} else if(rPoint == (r + 1) && lPoint <= mid) {
help[t] = array[lPoint++];
}
}
for(int t = 0; t < help.length; t++) {
array[l + t] = help[t];
}
return sum;
}
求逆序对数量(LeetCode剑指Offer51题)
public int reversePairs(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
return recursion(nums, 0, nums.length - 1);
}
private int recursion(int[] nums, int l, int r) {
if(l == r) {
return 0;
}
int mid = l + ((r - l) >> 1);
int lNum = recursion(nums, l, mid);
int rNum = recursion(nums, mid + 1, r);
return lNum + rNum + merge(nums, l, r, mid);
}
private int merge(int[] nums, int l, int r, int mid) {
int[] help = new int[r - l + 1];
int num = 0;
int lPoint = l;
int rPoint = mid + 1;
for(int t = 0; t < help.length; t++) {
if(lPoint <= mid && rPoint <= r) {
if(nums[lPoint] > nums[rPoint]) {
num += (r - rPoint + 1);
help[t] = nums[lPoint++];
} else {
help[t] = nums[rPoint++];
}
} else if(lPoint == (mid + 1) && rPoint <= r) {
help[t] = nums[rPoint++];
} else if(lPoint <= mid && rPoint == (r + 1)) {
help[t] = nums[lPoint++];
}
}
for(int t = 0; t < help.length; t++) {
nums[l + t] = help[t];
}
return num;
}
快速排序
问题一
给定一个数组 arr 和一个数 num,把小于等于 num 的数放在数组左边,把大于等于 num 的数放在数组右边
public int[] sort(int[] arr, int num) {
if(arr == null || arr.length = 0) {
return arr;
}
int tmp = 0;
int l = 0;
int r = arr.length - 1;
while(l < r) {
//左右指针一起向中间移动,找到左边大于 num 右边小于等于 num 的位置
while(l < r && arr[l] <= num) {
l++;
}
while(l < r && arr[r] > num) {
r--;
}
//交换值
tmp = arr[l];
arr[l] = arr[r];
arr[r] = tmp;
l++;
r--;
}
}
问题二
给定一个数组 arr 和一个数 num,把小于 num 的数放在数组左边,把大于等于 num 的数放在数组右边,把等于 num 的数放在数组中间
public int[] sort(int[] arr, int num) {
//小于 num 的左边界
int lBoard = -1;
//大于 num 的右边界
int rBoard = arr.length;
//指针
int lPoint = 0;
while (lPoint < rBoard) {
if (arr[lPoint] < num) {
swap(arr, lBoard + 1, lPoint);
lPoint++;
lBoard++;
} else if (arr[lPoint] == num) {
lPoint++;
} else {
swap(arr, lPoint, rBoard - 1);
rBoard--;
}
}
return arr;
}
private void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
快排算法(qsort)
public int[] qsort(int[] arr) {
if(arr == null || arr.length <2) {
return arr;
}
return qsort(arr, 0, arr.length - 1);
}
public int[] qsort(int[] arr, int l, int r) {
if(l == r) {
return arr;
}
int num = arr[l];
int[] board = partition(arr, l , r, num);
if (board[0] >= l && board[0] <= r) {
qsort(arr, l, board[0]);
}
if (board[1] >= l && board[1] <= r) {
qsort(arr, board[1], r);
}
return arr;
}
private int[] partition(int[] arr, int l, int r, int num) {
//小于 num 的左边界
int lBoard = l - 1;
//大于 num 的右边界
int rBoard = r + 1;
//指针
int lPoint = l;
while (lPoint < rBoard) {
if (arr[lPoint] < num) {
swap(arr, lBoard + 1, lPoint);
lPoint++;
lBoard++;
} else if (arr[lPoint] == num) {
lPoint++;
} else {
swap(arr, lPoint, rBoard - 1);
rBoard--;
}
}
int[] res = new int[]{lBoard, rBoard};
return res;
}
private void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}