中等难度题
17.1编写一个函数,不使用临时变量,直接交换两个数
加减法
public void change(int a, int b){
a = a - b;
b = b + a;
a = b - a;
}
位操作
public void change(int a, int b){
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
17.2检查一个人是否完成了井字游戏
思路: 现在红色3 * 3 的棋盘,存在两种设计思路,
1。直接对每一个空格进行编码 3的0次方v0, 3的1次方v1 ,3的2次方v2。。。一直到3的8次方v8,其中 vi为0, 表示没有这个数, vi为1,表示红方在,vi为2,表示蓝方在, 对结果进行缓存
2.检查行,检查列,检查对角线,检查逆对角线
17.3设计一个算法,计算出n阶乘有多少个尾随0
//这个尤其要注意, 25是包含两个5的
public int countFactZeros(int num){
if (num < 5){
return 0;
}
int count = 0;
for (int i = 5 ; num / i > 0; i *= 5){
res += num / i;
}
return count;
}
17.4编写一个方法,找出两个数中最大的那一个。不得使用if或其他运算比较符。
思路:利用位运算,对大的数乘以1,小的数乘以0
public int sign(int num){
return !((num >> 31) & 0x01);
}
public int flip(int num){
return 1 ^ num;
}
public int getBigger(int a, int b){
int c = a - b;
int sa = sign(a);//这个函数四个很关键的函数,大于等于0 返回1,小于0 返回0,flip反转也对
int sb = sign(b);
int sc = sign(c);// a - b 的符号
int use_sign_of_a = sa ^ sb;
int use_sign_of_c = flip (sa ^ sb);
int k = use_sign_of_a * a + use_sign_of_c * c;
int p = flip(k);
return a * k + b * p;
}
17.5珠玑妙算游戏,R,Y,G,B 。实际组合为RGBY,猜测GGRR返回,一次猜中,一次伪猜中。
public static void getResult(String real, String guess){
char[] ra = real.toCharArray();
char[] ga = guess.toCharArray();
int real1 = 0;
int sreal = 0;
int index = 0;
int [] temp = new int[4];
for (char c : ga){
if (c == ra[index++]){
real1++;
} else{
int code = getCode(c);// 这个地方加上去的是solution的解
temp[code]++;
}
}
for (int i = 0; i < ga.length; i++){
index = getCode(ga[i]);
if (temp[index] > 0 && ra[i] != ga[i]){
temp[index]--;
sreal++;
}
}
System.out.println(real1);
System.out.println(sreal);
}
public static int getCode(char c){
switch (c){
case 'B':
return 0;
case 'G':
return 1;
case 'R':
return 2;
case 'Y':
return 3;
default :
return -1;
}
}
17.6给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意 n - m越小越好。
思路:左增长序列索引,右增长数列索引,中间夹杂的数列的最小值与左边对比,最大值与右边对比
17.7给定一个整数,打印该整数的英文描述(例如“One Thousand, Two Hundred Thirty Four”)
思路: 熟悉正常的读法,然后构造读百位的方法,然后填充三位数的单位
注意 String[] 数组要使用 static关键字进行修饰
public static String[] digits = {"One", "Two" , "Three" , "Four" , "Five", "Six", "Seven", "Eight", "Nine"};
public static String[] teens = {"Eleven", "Twelve", "Thirten", "Fourteen","Fifteen","Sixteen", "Seventeen", "Eighteen", "Nineteen"};
public static String[] tens = {"Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
public static String[] bigs = {"", "Thousands", "Million", "Billion"};
public static String numToString100(int num){
StringBuffer sb = new StringBuffer();
if (num > 100){
sb.append(digits[num / 100 - 1] + " Hundred ");
num %= 100;
}
if (num >= 11 && num <=19){
sb.append(teens[num - 11] + " ");
return sb.toString();
} else if (num == 10 || num >= 20){
sb.append(tens[num / 10 - 1] + " ");
num %= 10;
}
if (num > 0 && num <= 9){
sb.append(digits[num - 1] + " ");
}
return sb.toString();
}
public static String numToString(int num){
if (num == 0){
return "Zero";
}
if (num < 0){
return "Negative " + numToString(-1 * num);
}
int count = 0;
String res = "";
while (num > 0){
if (num % 1000 != 0){
res = numToString100(num % 1000) + bigs[count] + " " + res;
}
count++;
num /= 1000;
}
return res;
}
17.8给定一个数组(有正数有负数),找出总和最大的连续数列,并返回总和。
思路:这个题有点DP的意思,需要缓存每一个加到现在的数,用模式匹配的方法,与leetcode的买股票类似
public static int getMaxSum(int[] nums){
int sum = 0;
int maxsum = 0;
for (int i = 0; i < nums.length; i++){
sum += nums[i];
if (sum > maxsum){
maxsum = sum;
} else if (sum < 0){
sum = 0;
}
}
return maxsum;
}
17.10 XML非常冗长,你可将每个标签对应为预先定义好的整数值,encode
17.11给定rand5(),实现一个方法rand7()。也即,给定一个产生0到4(含)随机数方法,编写一个产生0到6(含)随机数的方法。
rand5随机产生0-4 的数,rand7随机产生0 -6的数,提供两种思路,但是次数都不定
思路1: 5 * rand5() + rand5(); 随机产生0 -24 的数,只选取其中的0 -20,这个地方要注意了,之前考虑的出错了。
思路2: 2 * rand5() 获得 0 - 9 之间的偶数, 舍弃rand5()等于4的时候,对2求余得到 0.5的概率,加到第一个数上去。
17.12设计一个算法,找出数组中两数之和为指定值的所有整数对。
思路:1.O(n)时间复杂度,将数组中的数都放到map中, 然后半边迭代,如果map中包含num - x,输出
2.如果是有序数组,两个指针,一个在开始,一个在结束,left < right 的时候进行处理,和与目标值相等:left++,right–;否则如果小 left ++ ,如果大right–,重复元素不好解决。
17.13有个简单的类似结点的数据结构BinNode,包含两个指向其他结点的指针,将查找二叉树转化为双链表的形式。
这个题目可以看出来,递归真是大法宝,归并排序的方式类似,解决好当前点的处理模式
// 因为二叉查找树的中序遍历就是增序的访问,这个是非递归的形式
public BinNode changeTreeToList(BinNode root){
if (null == root){
return null;
}
BinNode head= new BinNode();
BinNode temp = root;
BinNode temp2 = head;
Stack<BinNode> stack = new Stack<BinNode>();
while(true){
if (temp != null){
stack.push(temp);
temp = temp.node1;//left, before
} else if (stack.size() > 0){
temp = stack.pop();
BinNode node = new BinNode(temp.val);
temp2.node2 = node;
node.node1 = temp2 ;
temp2 = node;
temp = temp.node2;//right , after
} else {
break;
}
}
head = head.node2;
head.node1 = null;
return head;
}
// 递归的形式
public NodePair convert(BinNode root){
if (root == null){
return null;
}
NodePair left = convert(root.node1);// left
NodePair right = convert(root.node2);// right
if (left != null){
contact(left.tail, root);
}
if (right != null){
contact(root, right.head);
}
return new NodePair(left == null ? root : left.head, right == null ? root : right.tail);
}
public void contact(BinNode no1, BinNode no2){
no1.node2 = no2;
no2.node1 = no1;
}
public class NodePair{
BinNode head;
BinNode tail;
public NodePair(BinNode he, BinNode tai){
head = he;
tail = tai;
}
}
17.14给你字符串,给你字典,解析成正确的句子。
这本书上给的动态规划的策略是对递归加传入参数的缓存。
在返回值里面 如果包含多个返回值,新建一个包裹类。
在DP里面,如果缓存的是对象而不是基本的数据类型,很可能需要复制该对象。
高难度题
18.1编写函数实现两个数相加。不得使用加号或其他运算符。
这边涉及到了加法的原理
public int add(int a, int b){
if (b == 0){
return a;
}
int sum = a ^ b;
int carry = a & b << 1;
return add(sum, carry);
}
// 这里给出leetcode的最优解,一步只加减,不进位,一步考虑进位,如果两者相与为0,则可直接进行加减数
public int add (int a, int b) {
int x = 0, y = 0;
while (a & b != 0) {
x = a ^ b;// add
y = a & b << 1;//carry
a = x;
b = y;
}
return a | b;
}
18.2编写一个方法,洗一副牌。要求做到完美洗牌。给定一个完美的随机数发生器。
//递归方法
int rand(int lower, int higher) {
return lower + (int)(Math.random() * (higher - lower + 1));
}
public int[] shuffleArray(int[] cards, int i){
if (i == 0){
return cards;
}
//
shuffleArray(cards, i - 1);
int k = rand(0, i);
int temp = cards[i];
cards[i] = cards[k];
cards[k] = temp;
return cards;
}
//迭代也是从小到大交换即可。
18.3编写一个方法,从大小为n的数组中随机选出m个整数。要求每个元素被选中的概率相同。
思路:与上题目类似,只不过是先进行了m的填充。,这个概率,不要忘记初始的问题
18.4数出0到n(含)中数字2出现了几次。
思路: 1.暴力求解, 先如此,后叠加
2. 找数字中出现的规则
public static int count2sDigit(int num, int d){
int powerof10 = (int)Math.pow(10, d);
int nextpowerof10 = powerof10 * 10;
int right = num % powerof10;
int roundDown = num - num % nextpowerof10;
int roundUp = roundDown + nextpowerof10;
int digit = (num / powerof10) % 10;
if (digit < 2){
return roundDown / 10;
} else if (digit == 2){
return roundDown / 10 + right + 1;
} else {
return roundUp / 10;
}
}
public static int count2sInRange(int number){
if (number < 0){
return count2sInRange(-number);
}
String num = String.valueOf(number);
int count = 0;
for (int i = 0; i < num.length(); i++){
count += count2sDigit(number , i);
}
return count;
}
18.6 给定10亿个数字,找出最小的100万个数字。假定计算机内存足以容纳全部10亿个数字。
思路: 1.TreeSet
2.排序 时间复杂度 O(nlog(n));
3.大顶堆
4.选择排序法(快排),可在预期的O(n)时间内找到第i个最小的元素。
18.7给定一组单词,找出其中最长单词,且该单词由这组单词中的其他单词组合而成
public class LengthComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
if (o1.length() >= o2.length()){
return -1;
} else {
return 1;
}
}// 按照长度从大到小进行排列
}
// 递归实现字符串能否被构建
public static boolean canBuild(String str, boolean flag, Map<String, Boolean> map){
if (map.containsKey(str) && !flag){
return map.get(str);
}
for (int i = 1; i < str.length(); i++){
String left = str.substring(0, i);
String right = str.substring(i);
if (map.containsKey(left) && map.get(left) == true && canBuild(right, false, map)){
return true;
}
}
map.put(str, false);
return false;
}
public static String getLong (String[] strs){
if (null == strs || strs.length < 1 ){
return null;
}
Map<String, Boolean> map = new HashMap<String, Boolean>();
Arrays.sort(strs, new LengthComparator());
for (String s : strs){
map.put(s, true);
System.out.println(s);
}
for (String s : strs){
if (canBuild(s, true, map)){
return s;
}
}
return "";
}
18.8给定一个字符串s和一个包含短字符串的数组T,设计一个方法,根据T中的每一个较短的字符串,对s进行搜索。
思路: 新的数据结构,后缀树, 敲黑板。考试必考重点题型,重点。
//后缀树的构建和搜索,短字符串如果出现,返回的是index
//每个字符后面跟的子树的index都是第一个子节点的index,如果第一代儿子的有两个或者两个以上的子树,indexes就会存在多个
public class SuffixTree {
SuffixTreeNode root = new SuffixTreeNode();
public SuffixTree(String s){
for (int i = 0; i < s.length(); i++){
String suffix = s.substring(i);
root.insertString(suffix, i);
}
}
public ArrayList<Integer> search(String s){
return root.search(s);
}
}
public class SuffixTreeNode {
HashMap<Character, SuffixTreeNode> children = new HashMap<Character, SuffixTreeNode>();
char value;
ArrayList<Integer> indexes = new ArrayList<Integer>();
public SuffixTreeNode(){}
public void insertString(String s, int index){
indexes.add(index);
if (s != null && s.length() > 0){
value = s.charAt(0);
SuffixTreeNode child = null;
if (children.containsKey(value)){
child = children.get(value);
} else {
child = new SuffixTreeNode();
children.put(value, child);
}
String remainder = s.substring(1);
child.insertString(remainder, index);
}
}
public ArrayList<Integer> search(String s){
if (s == null || s.length() == 0){
return indexes;
} else {
char first = s.charAt(0);
if (children.containsKey(first)){
String remainder = s.substring(1);
return children.get(first).search(remainder);
}
}
return null;
}
}
18.9随机生成一些树并传入,每当收到新的数字时,找出并标记中位数。
思路:利用两个优先级堆(pariority):一个大顶堆,存放小于中位数的值,以及一个小顶堆,存放大于中位数的值。
这两个堆实现了比较完备的平衡
//tips x > y 返回-1 ,生成的队列是从大到小排列的。 默认队列是小的数在队列前面,也就是小堆顶
public class MyPriorityQueue {
private PriorityQueue<Integer> minHeap = null;
private PriorityQueue<Integer> maxHeap = null;
public MyPriorityQueue(){
Comparator<Integer> maxHeapComparator = new maxHeapComparator();
minHeap = new PriorityQueue<Integer>();
maxHeap = new PriorityQueue<Integer>(11, maxHeapComparator);
}
public void addNewNumber(int num){
if (minHeap.size() == maxHeap.size()){// max num > = min num,条件得以保证
if (minHeap.peek() != null && num > minHeap.peek()){
maxHeap.add(minHeap.poll());
minHeap.add(num);
} else {
maxHeap.offer(num);// max size > min size,初始两者的size是保持相等的
}
} else {
if (num < maxHeap.peek()){
minHeap.add(maxHeap.poll());
maxHeap.add(num);
} else {
minHeap.add(num);//此时 持平
}
}
}
public double getMeidan(){
if (maxHeap.isEmpty()){
return 0;
}
if (maxHeap.size() == minHeap.size()){
return (double)(maxHeap.peek() + minHeap.peek()) / 2.0;
} else {
return maxHeap.peek();
}
}
18.10给定两个字典里的单词,长度相等。编写一个方法,将一个单词变化成另一个单词。一次只改动一个单词。在变换的过程中,每一步得到的新单词都必须是字典里存在的。
思路:这里面用到了图的知识,广度优先搜索。深度搜索和广度搜索,也就是深搜与广搜。
这个地方假设字符串的长度n,看一下单词的数量,构造图的时候,获取临近节点的位置,看下字典中单词的数量,多的话用 24的n次方,少的话,用遍历字典,广度优先得到的是最短路径。
这边采用遍历字典的方式,
边构建图,边记录回溯路径
单向图
没有bugfree的原因是把t写成了w
用了HashMap缓存了路径
public class Graph {
public Set<String> getAdjacent(String s, Set<String> dict){
Set<String> set = new HashSet<String>();
for (String single : dict){
if (single.length() == s.length()){
int count = 0;
for (int i = 0; i < s.length(); i++){
if (s.charAt(i) != single.charAt(i)){
count++;
}
}
if (count == 1){
set.add(single);
}
}
}
return set;
}
public LinkedList<String> transForm(String start, String end, Set<String> dictionary){
if (start == null || end == null){
return null;
}
Queue<String> qu = new LinkedList<String>();
Set<String> visited = new HashSet<String>();
Map<String, String> trackpath = new TreeMap<String, String>();
qu.add(start);
visited.add(start);
while(!qu.isEmpty()){
String w = qu.poll();
Set<String> temp = getAdjacent(w, dictionary);
for (String t : temp){
if (t.equals(end)){
LinkedList<String> path = new LinkedList<String>();
path.add(t);
while(w != null){
path.add(w);
t = w;
w = trackpath.get(t);
}
return path;
}
if (!visited.contains(t)){
qu.add(t);
visited.add(t);
trackpath.put(t, w);
}
}
}
return null;
}
}
18.11给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出四条边皆为黑色像素的最大子方阵。
思路:正常按照从大到小的边的长度进行迭代,O(n的四次方)。
预处理的话,就可以吧时间复杂度降为O(n的三次方)。新建一个包装类,预处理的方式为每一个点记录下,右边第几个为0或者边界,下面第几个为0或者边界。
18.12给定一个正整数和负整数组成的N*N矩阵,编写代码找出总和最大的子矩阵
思路:1.蛮力法:O(n的6次方)
因为不一定是方阵,所以要迭代O(n的4次方)个子矩阵,计算每个子矩阵的用时O(n的2次方),
2.动态规划:O(n的4次方),也不算动态规划
先进行预处理,每一个都计算其左上角矩阵值得大小
3.还有一个优化算法,时间复杂度为O(n的3次方)
此题参考一位数组,找出和最大的连续序列。
//这个特别巧妙的是利用了上一次迭代的过程
public void clearArray(int[] array){
for (int i = 0; i < array.length; i++){
array[i] = 0;
}
}
public int maxSubArray(int[] arr, int n){
int max = Integer.MIN_VALUE;
int sum = 0;
for (int i = 0; i < n; i++){
sum += arr[i];
max = Math.max(max, sum);
sum = Math.max(sum, 0);
}
return max;
}
public int maxSubMatrix(int[][] matrix){
int row = matrix.length;
int col = matrix[0].length;
int[] partilSum = new int[col];
int max = Integer.MIN_VALUE;
int x = 0;
int y = 0;
for (int rowb = 0; rowb < row; rowb++){
clearArray(partilSum);
for (int rowe = rowb; rowe < row; rowe++){
for (int co = 0; co < col; co++){
partilSum[co] += matrix[rowe][co];
}
for (int a : partilSum){
}
int runmax = maxSubArray(partilSum, col);
if (runmax > max){
max = runmax;
x = rowb;
y = rowe;
}
}
}
return max;
}