题目一:在一个无序数组中,求第k小的数
快排:荷兰国旗问题
BFPRT 有讲究的选择一个数
之后的步骤和快排一样
假设整个方法叫f函数 f(arr, k)
1)0~4一组 5~9一组 0~14一组 剩下的一组
2)marr[a,b,c,d,……] 每组选一个中位数 O(N)
3)求marr的中位数 N/5 f(marr, marr/2) -> x
即估计大于等于x的数至少有多少个
1)分组 O(1)
2)组中中位数 O(N)
3)f(marr, marr.length/2) 得出x T(N/5)
4)小于x 等于x 大于x O(N)
5)T(7N/10)
时间复杂度为O(N)
//在arr[begin..end]范围上,求如果排序的话,i位置的数是谁,返回
//i一定在begin~end范围上
public static int select(int[] arr, int begin, int end, int i){
if(begin == end){
return arr[begin];
}
//分组 + 组内排序 + 组成newarr + 选出newarr的上中位数 pivot
int pivot = medianOfMedians(arr, begin, end);
//根据pivot做划分值 <p ==p >p ,返回等于区域的左边界和有边界
//pivotRange[0] 等于区域的左边界
//pivotRange[1] 等于区域的右边界
int[] pivot = partition(arr, begin, end, pivot);
if(i >= pivotRange[0] && i <= pivotRange[1]){
return arr[i];
}else if(i < pivotRange[0]){
return select(arr, begin, pivotRange[0] - 1, i);
}else {
return select(arr, pivotRange[1] + 1, end, i);
}
}
public static int medianOfMedians(int[] arr, int begin, int end){
int num = end - begin + 1;
int offset = num % 5 == 0 ? 0 : 1;
int[] mArr = new int[sum / 5 + offset];
for(int i = 0; i < mArr.length; i++){
int beginI = begin + i * 5;
int endI = beginI + 4;
mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
}
return select(mArr, 0, mArr.length - 1; mArr.length / 2);
}
public static int[] partition(int[] arr, int begin, int end, int pivotValue){
int small = begin - 1;
int cur = begin;
int big = end + 1;
while(cur != big){
if(arr[cur] < pivotValue){
swap(arr, ++small, cur++);
}else if(arr[cur] > pivotValue){
swap(arr, cur, --big);
}else{
cur++;
}
}
}
题目2:给定一个正数1,裂开的方法有1种,给定一个正数2,裂开的方法 1和1 、 2
3,裂开的方法有三种(1、1、1)、(1、2)、(3)
不降序裂开
//pre裂开的前一个部分
//rest还剩多少值,需要去裂开,要求裂出来的第一部分,不要比pre小
//返回裂开的方法数
public static int process(int pre, int rest){
if(rest == 0){
return 1;//之前裂开的方案,构成了一种有效方法
}
if(pre > rest){
return 0;
}
int ways = 0;
for(int i = pre; i <= rest; i++){//i:rest第一个裂开的部分,值是多少
ways += process(i, rest - i);
}
return ways;
}
public static int ways2(int n){
if(n < 1){
return 0;
}
int[][]dp = new int[n + 1][n + 1];
for(int pre = 1; pre < dp.length; pre++){
dp[pre][0] = 1;
}
for(int pre = n; pre > 0; pre--){
for(int rest = pre; rest <= n; rest++){
for(int i = pre; i <= rest; i++){
dp[pre][rest] += dp[i][rest - i];
}
}
}
return dp[1][n];
}
public static int ways3(int n){
if(n < 1){
return 0;
}
int[][]dp = new int[n + 1][n + 1];
for(int pre = 1; pre < dp.length; pre++){
dp[pre][0] = 1;
}
for(int pre = 1; pre < dp.length; pre++){
dp[pre][pre] = 1;
}
for(int pre = n - 1; pre > 0; pre--){
for(int rest = pre + 1; rest <= n; rest++){
dp[pre][rest] += dp[pre + 1][rest] + dp[pre][rest - pre];
}
}
return dp[1][n];
}
题目3:给定一棵二叉树的头节点head,已知所有节点的值都不一样,返回其中最大的且符合搜索二叉树条件的最大拓扑结构的大小
拓扑结构:不是子树,只要能连起来的结构都算。
时间复杂度O(N)
题目4:给定一个长度为偶数的数组arr,长度记作2*N,前N个为左部分,后N个为右部分
arr可以表示为{L1,L2,……,Ln,R1,R2,……,Rn}
请调整为{R1,L1,R2,L2......Rn,Ln}的样子
(完美洗牌)
a b c d e f
0 1 2 3 4 5
L1 L2 L3 R1 R2 R3
[a d b e c f]
i小于等于N i大于N
i左半区 右半区
2*i 2(i-N) - 1
下标循环对,可能存在多个不相交的小环
N(偶数)
当N = 3^k - 1
存在的结论:不同环的触发点:1,3,9....3^(k-1)
N=普通偶数时
……a b c d e 甲乙 ……
左组内部逆序,乙组内部逆序,整体逆序
……甲乙abcde……
假设N = 14,左7,右7
L1L2L3L4L5L6L7R1R2R3R4R5R6R7
14最接近8
先让L1L2L3L4R1R2R3R4调整
L5L6L7R1R2R3R4逆序
调整完后变成L1L2L3L4R1R2R3R4L5L6L7R5R6R7
再逆序变成R1R2R3R4L1L2L3L4L5L6L7R5R6R7
剩下6,接近2,重复……
拓展:数组无序时候,
调整为[a b c d e f g h i ……]
使得关系为小于等于和大于等于交错
1)arr 排序(堆排)
N=偶数
L1L2……LiR1R2……Ri
从小到大
调整为R1L1R2L2……RiLi
接着L1R1R2L3……
N=奇数
L0L1L2……LiR1R2……Ri
调整为L0R1L1R2L2……RiLi接着如上