初学的算法小问题,给点提示,呵呵 有2个已有序的LinkedList<Integer>,size分别为m,n。现在要找出在2个LinkedList<Integer>合并后第K大的,要求复杂度在O(lg m + lgn)。。给点提示吧,,呵呵 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 比如: LinkedList<Integer> aa = new LinkedList<Integer>(); for (int i = 0; i < 10; i++) { aa.add(i); } LinkedList<Integer> bb = new LinkedList<Integer>(); for (int i = 6; i < 16; i++) { bb.add(i); }现在要找出在aa和bb里面的第K个大的数,比如K=8的话,那么要找出来的数是6呵呵 用数组是可以实现O(lgm+lgn)的但用LinkedList就不可能了 LinkedList本身就是用链接表做的 直接在一个有序的链接表中找第k大的数就已经是O(n)了 import java.util.Scanner;public class Main { public static int fd(int[] arrA, int al, int ar, int[] arrB, int bl, int br, int k) { int am = (ar - al) / 2 + al; int bm = (br - bl) / 2 + bl; if( am+bm < k ) { if( arrA[am+1] < arrB[bm+1] ) { if( ar-am == 1 ) am = ar; return fd(arrA, am, ar, arrB, bl, br, k); } if( br-bm == 1 ) bm = br; return fd(arrA, al, ar, arrB, bm, br, k); } else if( am+bm == k ) { if( arrA[am] == arrB[bm] ) return arrA[am]; if( arrA[am] < arrB[bm] ) { if( arrA[am] >= arrB[bm-1] ) return arrA[am]; if( arrA[am+1] < arrB[bm] ) return fd(arrA, am, ar, arrB, bl, bm, k); else return arrB[bm]; } // arrA[am] > arrB[bm] if( arrA[am-1] <= arrB[bm] ) return arrB[bm]; if( arrA[am] > arrB[bm+1] ) return fd(arrA, al, am, arrB, bm, br, k); else return arrA[am]; } else { // am+bm > k if( arrA[am] < arrB[bm] ) return fd(arrA, al, ar, arrB, bl, bm, k); return fd(arrA, al, am, arrB, bl, br, k); } } public static int findKMax(int[] arrA, int[] arrB, int k) { // 处理零界情况 减少复杂度 if( arrA[arrA.length-1] < arrB[0] ) { if( k < arrA.length ) return arrA[k]; else return arrB[k-arrA.length]; } if( arrB[arrB.length-1] < arrA[0] ) { if( k < arrB.length ) return arrB[k]; else return arrA[k-arrB.length]; } if( k<arrA.length && arrA[k]<arrB[0] ) return arrA[k]; if( k<arrB.length && arrB[k]<arrA[0] ) return arrB[k]; if( k>arrA.length && arrB[k-arrA.length]>=arrA[arrA.length-1] ) return arrB[k-arrA.length]; if( k>arrB.length && arrA[k-arrB.length]>=arrB[arrB.length-1] ) return arrA[k-arrB.length]; return fd(arrA, 0, arrA.length-1, arrB, 0, arrB.length-1, k); } public static void main(String[] args) throws Exception { int[] arrA = new int[10]; int[] arrB = new int[10]; for(int i = 0; i < 10; i++) { arrA[i] = i; arrB[i] = i + 3; } // Scanner scanner = new Scanner(System.in);// while(true) {// int d = scanner.nextInt();// System.out.printf("d = %d, result = %d\n", d, findKMax(arrA, arrB, d));// } for(int i = 0; i < 20; i++) System.out.printf("i = %d, result = %d\n", i, findKMax(arrA, arrB, i)); }} fd函数中a表示数组ab表示数组bl表示左r表示右m表示中am对应的就是数组a的中间位子这样看我的代码也就没那么乱了^_^ 启发:在一个有序序列中查找某个值,使用二分查找需要O(lgn);如果在数组A中找某个值,那个值的位置假设为wa,同样在数组B中找的位置是wb,如果可以办到wa+wb=k,那么算法的时间复杂度就为O(lgm+lgn)了可行性:二分查找在实现上是去中间值进行比较的,这个有点像是指针,底了上升,高了下降;因此在wa+wb=k的等式中wa位置的值相对太高了就可以将wa调底(减去1/2),wb同样的道理,这样协同的调整是可以得到满足等式wa+wb=k的wa和wb的;所以是可行的设计:分析情况1.数组A中所有的数都比数组B中的数小,那么第k大的数就在A的第k位置上或者在B的第k-A.length位置上2.k小于数组A的长度,同时数组A中前k个数比数组B中的任意数都要小,那么第k大的数就在A的第k位置上3.k大于数组A的长度,同时数组B中前k-A.length个数都小于等于A中最大的数,那么第k大的数就在A的最后一个位置上4.k在数组A和数组B交叉的位置上,这是真正需要设计查找的地方查找算法:感觉用自然语言描述起来比较麻烦,就直接给代码注释一下吧// 原先的有点错误 这个已经更正过了// 以下的算法并没有考虑序列中有重复的情况public static int fd(int[] arrA, int al, int ar, int[] arrB, int bl, int br, int k) { int am = (ar - al) / 2 + al; int bm = (br - bl) / 2 + bl; // am,bm,k都是从0开始计数的 所以比较时am+bm+2,k+1 if( am+bm+2 < k+1 ) { // am+bm+2 < k+1 可以得出k的在arrA[am]之后 或者在arrB[bm]之后 if( arrA[am+1] < arrB[bm+1] ) { // 如果直接比较arrA[am]和arrB[bm],根本不知道后面的 // 情况,arrA[am]<arrB[bm]时,有可能arrA[am+1]>arrB[bm] if( ar-am == 1 ) am = ar; // ar前面作为中间位置,但实际B中有比arrA[ar]大的数,所以要回到那位置 return fd(arrA, am, ar, arrB, bl, br, k); } if( br-bm == 1 ) bm = br; return fd(arrA, al, ar, arrB, bm, br, k); } else if( am+bm+2 == k+1 ) { if( arrA[am] == arrB[bm] ) return arrA[am]; if( arrA[am] < arrB[bm] ) { if( bm==0 || arrA[am]>=arrB[bm-1] ) return arrB[bm]; if( arrA[am+1] < arrB[bm] ) return fd(arrA, am, ar, arrB, bl, bm, k); else return arrB[bm]; } // arrA[am] > arrB[bm] if( am==0 || arrA[am-1]<=arrB[bm] ) return arrA[am]; if( arrA[am] > arrB[bm+1] ) return fd(arrA, al, am, arrB, bm, br, k); else return arrA[am]; } else { // am+bm+2 > k+1 if( arrA[am] < arrB[bm] ) return fd(arrA, al, ar, arrB, bl, bm, k); return fd(arrA, al, am, arrB, bl, br, k); }} 希望大家谈谈,在类的内部,变量定义的先后顺序决定了初始化的先后顺序是怎么回事? netbeans相对路径 输出 byte 数组时,怎么有了 "-" ,是什么意思呢 工厂方法模式请教 Windows 2000 server上安装java 7 JDBC连接oracle数据库问题? 小妹遇到一个怪问题,请帮忙看一下,怪哉怪哉,在线等待好消息。。。。。 请教一个简单的问题 在接口类的实现中可否不只实现接口规定的方法? 关于命令参数的个数?? int类型溢出问题 java小程序 那位帮忙看看 不知道错在那里
LinkedList<Integer> aa = new LinkedList<Integer>();
for (int i = 0; i < 10; i++) {
aa.add(i);
} LinkedList<Integer> bb = new LinkedList<Integer>();
for (int i = 6; i < 16; i++) {
bb.add(i);
}现在要找出在aa和bb里面的第K个大的数,比如K=8的话,那么要找出来的数是6
呵呵
但用LinkedList就不可能了 LinkedList本身就是用链接表做的
直接在一个有序的链接表中找第k大的数就已经是O(n)了
public static int fd(int[] arrA, int al, int ar, int[] arrB, int bl, int br, int k) {
int am = (ar - al) / 2 + al;
int bm = (br - bl) / 2 + bl;
if( am+bm < k ) {
if( arrA[am+1] < arrB[bm+1] ) {
if( ar-am == 1 ) am = ar;
return fd(arrA, am, ar, arrB, bl, br, k);
}
if( br-bm == 1 ) bm = br;
return fd(arrA, al, ar, arrB, bm, br, k);
} else if( am+bm == k ) {
if( arrA[am] == arrB[bm] ) return arrA[am];
if( arrA[am] < arrB[bm] ) {
if( arrA[am] >= arrB[bm-1] ) return arrA[am];
if( arrA[am+1] < arrB[bm] ) return fd(arrA, am, ar, arrB, bl, bm, k);
else return arrB[bm];
}
// arrA[am] > arrB[bm]
if( arrA[am-1] <= arrB[bm] ) return arrB[bm];
if( arrA[am] > arrB[bm+1] ) return fd(arrA, al, am, arrB, bm, br, k);
else return arrA[am];
} else { // am+bm > k
if( arrA[am] < arrB[bm] ) return fd(arrA, al, ar, arrB, bl, bm, k);
return fd(arrA, al, am, arrB, bl, br, k);
}
}
public static int findKMax(int[] arrA, int[] arrB, int k) {
// 处理零界情况 减少复杂度
if( arrA[arrA.length-1] < arrB[0] ) {
if( k < arrA.length ) return arrA[k];
else return arrB[k-arrA.length];
}
if( arrB[arrB.length-1] < arrA[0] ) {
if( k < arrB.length ) return arrB[k];
else return arrA[k-arrB.length];
}
if( k<arrA.length && arrA[k]<arrB[0] ) return arrA[k];
if( k<arrB.length && arrB[k]<arrA[0] ) return arrB[k];
if( k>arrA.length && arrB[k-arrA.length]>=arrA[arrA.length-1] ) return arrB[k-arrA.length];
if( k>arrB.length && arrA[k-arrB.length]>=arrB[arrB.length-1] ) return arrA[k-arrB.length];
return fd(arrA, 0, arrA.length-1, arrB, 0, arrB.length-1, k);
}
public static void main(String[] args) throws Exception {
int[] arrA = new int[10];
int[] arrB = new int[10];
for(int i = 0; i < 10; i++) {
arrA[i] = i;
arrB[i] = i + 3;
}
// Scanner scanner = new Scanner(System.in);
// while(true) {
// int d = scanner.nextInt();
// System.out.printf("d = %d, result = %d\n", d, findKMax(arrA, arrB, d));
// }
for(int i = 0; i < 20; i++)
System.out.printf("i = %d, result = %d\n", i, findKMax(arrA, arrB, i));
}
}
a表示数组a
b表示数组b
l表示左
r表示右
m表示中am对应的就是数组a的中间位子
这样看我的代码也就没那么乱了^_^
分析情况
1.数组A中所有的数都比数组B中的数小,那么第k大的数就在A的第k位置上或者在B的第k-A.length位置上
2.k小于数组A的长度,同时数组A中前k个数比数组B中的任意数都要小,那么第k大的数就在A的第k位置上
3.k大于数组A的长度,同时数组B中前k-A.length个数都小于等于A中最大的数,那么第k大的数就在A的最后一个位置上
4.k在数组A和数组B交叉的位置上,这是真正需要设计查找的地方查找算法:
感觉用自然语言描述起来比较麻烦,就直接给代码注释一下吧
// 原先的有点错误 这个已经更正过了
// 以下的算法并没有考虑序列中有重复的情况
public static int fd(int[] arrA, int al, int ar, int[] arrB, int bl, int br, int k) {
int am = (ar - al) / 2 + al;
int bm = (br - bl) / 2 + bl;
// am,bm,k都是从0开始计数的 所以比较时am+bm+2,k+1
if( am+bm+2 < k+1 ) { // am+bm+2 < k+1 可以得出k的在arrA[am]之后 或者在arrB[bm]之后
if( arrA[am+1] < arrB[bm+1] ) { // 如果直接比较arrA[am]和arrB[bm],根本不知道后面的
// 情况,arrA[am]<arrB[bm]时,有可能arrA[am+1]>arrB[bm]
if( ar-am == 1 ) am = ar; // ar前面作为中间位置,但实际B中有比arrA[ar]大的数,所以要回到那位置
return fd(arrA, am, ar, arrB, bl, br, k);
}
if( br-bm == 1 ) bm = br;
return fd(arrA, al, ar, arrB, bm, br, k);
} else if( am+bm+2 == k+1 ) {
if( arrA[am] == arrB[bm] ) return arrA[am];
if( arrA[am] < arrB[bm] ) {
if( bm==0 || arrA[am]>=arrB[bm-1] ) return arrB[bm];
if( arrA[am+1] < arrB[bm] ) return fd(arrA, am, ar, arrB, bl, bm, k);
else return arrB[bm];
}
// arrA[am] > arrB[bm]
if( am==0 || arrA[am-1]<=arrB[bm] ) return arrA[am];
if( arrA[am] > arrB[bm+1] ) return fd(arrA, al, am, arrB, bm, br, k);
else return arrA[am];
} else { // am+bm+2 > k+1
if( arrA[am] < arrB[bm] ) return fd(arrA, al, ar, arrB, bl, bm, k);
return fd(arrA, al, am, arrB, bl, br, k);
}
}