为了账号安全,请及时绑定邮箱和手机立即绑定

google面试官会告诉你这些。

标签:
Java 算法 面试

递归书写方法

  • 数学归纳法中的数学/自然语言<–>程序语言(证明递归函数正确执行)
  • 严格定义递归函数作用,包括参数,返回值,Side-effect
  • 一般,后特殊
  • 每次调用必须缩小问题规模
  • 每次问题规模缩小程度必须为1
  • 递归缺点
    • 调用堆栈Stack太深,开销大
    • 如果数据大,就会Stack Overflow!
  • 不要尝试递归改成非递归
    • 一般化的方法仍需要栈
    • 代码复杂
    • 不根本解决问题

Side-effect 函数副作用。理想的状态是函数运行完没有side-effect。 比较函数执行中会修改全局的一些属性,当执行完,也要将这些全局属性还原

链表

图片描述

package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import interview.common.Node;

public class LinkedListCreator {

  /**
   * Creates a linked list.
   *
   * @param data the data to create the list
   * @return head of the linked list. The returned linked list
   * ends with last node with getNext() == null.
   */
  public <T> Node<T> createLinkedList(List<T> data) {
    if (data.isEmpty()) {
      return null;
    }

    Node<T> firstNode = new Node<>(data.get(0));
    firstNode.setNext(
        createLinkedList(data.subList(1, data.size())));
    return firstNode;
  }

  public Node<Integer> createLargeLinkedList(int size) {
    Node<Integer> prev = null;
    Node<Integer> head = null;
    for (int i = 1; i <= size; i++) {
      Node<Integer> node = new Node<>(i);
      if (prev != null) {
        prev.setNext(node);
      } else {
        head = node;
      }
      prev = node;
    }
    return head;
  }

  public static void main(String[] args) {
    LinkedListCreator creator = new LinkedListCreator();

    Node.printLinkedList(
        creator.createLinkedList(new ArrayList<>()));
    Node.printLinkedList(
        creator.createLinkedList(Arrays.asList(1)));
    Node.printLinkedList(
        creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5)));
  }
}

链表反转

图片描述

package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;

import interview.common.Node;

public class LinkedListReverser {

  /**
   * Reverses a linked list.
   *
   * @param head the linked list to reverse
   * @return head of the reversed linked list
   */
  public <T> Node<T> reverseLinkedList(Node<T> head) {
    // size == 0 or size == 1
    if (head == null || head.getNext() == null) {
      return head;
    }
	// 第一步(对应上图)
    Node<T> newHead = reverseLinkedList(head.getNext());
    // 第二步
    head.getNext().setNext(head);
    // 第三步 over
    head.setNext(null);
    return newHead;
  }

  public static void main(String[] args) {
    LinkedListCreator creator = new LinkedListCreator();
    LinkedListReverser reverser = new LinkedListReverser();

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(new ArrayList<>())));

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(Arrays.asList(1))));

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5))));

    System.out.println("Testing large data. Expect exceptions.");
    reverser.reverseLinkedList(
        creator.createLargeLinkedList(1000000));
    System.out.println("done");
  }
}

combinations([1,2,3,4],2)

  • 缩小问题规模
  • 选1>combinations([2,3,4],1)
  • 不选1>combinations([2,3,4],2)
package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Combinations {

  /**
   * Generates all combinations and output them,
   * selecting n elements from data.
   */
  public void combinations(
      List<Integer> selected, List<Integer> data, int n) {
    if (n == 0) {
      // output all selected elements
      for (Integer i : selected) {
        System.out.print(i);
        System.out.print(" ");
      }
      System.out.println();
      return;
    }

    if (data.isEmpty()) {
      return;
    }

    // select element 0
    selected.add(data.get(0));
    combinations(selected, data.subList(1, data.size()), n - 1);

    // un-select element 0
    selected.remove(selected.size() - 1);
    combinations(selected, data.subList(1, data.size()), n);
  }

  public static void main(String[] args) {
    Combinations comb = new Combinations();

    System.out.println("Testing normal data.");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 2);
    System.out.println("==========");

    System.out.println("Testing empty source data.");
    comb.combinations(
        new ArrayList<>(), new ArrayList<>(), 2);
    System.out.println("==========");
    comb.combinations(
        new ArrayList<>(), new ArrayList<>(), 0);
    System.out.println("==========");

    System.out.println("Selecting 1 and 0 elements.");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 1);
    System.out.println("==========");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 0);
    System.out.println("==========");

    System.out.println("Testing large data");
    comb.combinations(
        new ArrayList<>(),
        Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 4);
  }
}

循环控制(循环不变式 loop invariant)

图片描述
图片描述

边界控制

  • 列表项
  • 初始值
  • 特殊值
  • null
  • 空字符串

数据结构

树的遍历

算法复杂度

  • 代表最坏情况用时
  • f(x)=O(g(x))asx-oo 当且仅当
  • If(x)1≤Mlg(x)l for all x >=xo 相当于高等数学中极限的定义

广义的“极限”是指“无限靠近而永远不能到达”的意思。数学中的“极限”指:某一个函数中的某一个变量,此变量在变大(或者变小)的永远变化的过程中,逐渐向某一个确定的数值A不断地逼近而“永远不能够重合到A”(“永远不能够等于A,但是取等于A‘已经足够取得高精度计算结果)的过程中,此变量的变化,被人为规定为“永远靠近而不停止”、其有一个“不断地极为靠近A点的趋势”。极限是一种“变化状态”的描述。此变量永远趋近的值A叫做“极限值”(当然也可以用其他符号表示)。

面向对象思想

商业代码复杂性,从用户角度思考问题
摒弃完全基于逻辑的思维

类与对象

  • 类的成员变量>对象状态
  • 类的成员函数>对象行为
  • 类的静态变量
  • 类的静态函数
  • 没有this引用,静态变量全局唯一—份
  • 普通函数可以引用静态变量、函数
  • 对象上引用静态变量、函数会产生编译器警告
  • 静态函数引用普通成员变量、函数会编译错误

图片描述
图片描述

类的特殊函数

  • 构造函数
  • equals
  • hashCode
  • toString
  • a.equals(b)说明a.hashCode)==b.hashCode) 必要条件

接口与实现

图片描述

  • 与类相比
  • 由编译器强制的一个模块间协作的合约(Contract)
  • 无成员变量
  • 成员函数只有申明不能有实现
  • 接口的申明
    • Java:interface BankEndPoint{……
    • C++:一个全部是纯虚函数的类
    • Python/大部分动态语言:依靠注释申明
      接口和抽象类有什么不同?
  • 从实现角度看
    • 抽象类可以有成员变量
    • 抽象类可以有部分实现
    • 抽象类不可以多重继承,接口可以
  • 接口强调合约
  • 强制协作双方无法犯错(编译器)
  • 但是抽象类提供公共的实现

图片描述

继承与封装

不可变对象 Immutable Objects

  • 可以引用传递,可以缓存
  • 线程安全
  • final 关键字
  • 类申明>类不可以被继承
  • 函数申明>函数不可以在派生类中重写
  • 变量申明>变量不可以指向其它对象
  • static final变量>用于定义常量,名称一般大写
  • final关键字无法保证不可变性
  • 从接口定义,类的实现上保证不可变性
  • Collections.unmodifiableXXX

泛型

c++虚函数表

图片描述

void dispatch_work(Employee* p){
p->doWork();
dispatch_ work(&manager);

图片描述


设计模式(解耦和)

设计模式当初的提出就是因为GOF(Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides)的博士论文(这才是真正的程序员)
图片描述

再谈Singleton(单例模式)

  • 确保全局至多只有一个对象
  • 用于:构造缓慢的对象,需要统一管理的资源
  • 缺点:很多全局状态,线程安全性
  • 双重锁 Double checked locking
    • 第一次check全局对象是不是null,不是null直接拿来用,如果是null,锁住,再check一次(防止其他人在这个空挡创建)
  • 作为Java类的静态变量(全局只有一份,不过程序员初始化就要创建这个静态变量,所以也就增加了初始化的时间(如果是比较缓慢的对象))
  • 使用框架提供的能力
    • 依赖注入

变继承关系为组合关系(状态模式State)

  • 描述is-a关系
  • 不要用继承关系来实现复用
    • 状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
  • 使用设计模式来实现复用
    图片描述
    图片描述

Decotator装饰器模式

interface Runnable{
void run);

如何实现LoggingRunnable,TransactionalRunnable.…
图片描述

图片描述

对象如何创建

使用new来创建的缺点

  • 编译时必须决定创建哪个类的对象
  • 参数意义不明确

Abstract Factory Pattern 抽象工厂

task=new Logging Task(new Coding Task);

task=taskFactory.createCodingTask); (好)

Builder Pattern 生成器

解决参数意义不明确的问题
不可变对象往往配合Builder使用

employee=new Employee(
oldEmployee.getName),15000);

employee=Employee.fromExisting(oldEmployee)
.withSalary(15000)
.build();



Beautiful Numbers

1,11,111…是beautiful的

  • 3>2进制>11
  • 13>3进制>111(选位数多的)
    • 133+1*3+1=13
    • 13%3=1,13/3=4
    • 4%3=1,4/3=1
    • 1%3=1,1/3=0
  • 13>12进制>11
  • N>r进制>111…1(k个) 求大数据集思路
    • N=r(k-1)+r(k-2)+.…+r+1
    • N=(1-r^k)/(1-r)
    • 假设N能转化成k个1组成的Beautiful Number
    • 那么这个Beautiful Number是几进制?r=?
    • 因为k最多是64个(最低两位),但是r最多就太大了
O(nlogn) 10*8 ~= 1s
package interview.google;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;

public class BeautifulNumber {

  public static void main(String[] args) {
    Scanner in = new Scanner(
        new BufferedReader(new InputStreamReader(System.in)));
    int cases = in.nextInt();
    for (int i = 1; i <= cases; ++i) {
      long n = in.nextLong();
      System.out.println("Case #" + i + ": "
          + beautiful(n));
    }
  }

  private static long beautiful(long n) {
    for (long radix = 2; radix < n; radix++) {
      if (isBeautiful(n, radix)) {
        return radix;
      }
    }

    throw new IllegalStateException("Should not reach here.");
  }

  private static boolean isBeautiful(long n, long radix) {
    while (n > 0) {
      if (n % radix != 1) {
        return false;
      }
      n /= radix;
    }
    return true;
  }
}

O(logn*logn*logn) 64*64*64
package interview.google;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;

public class BeautifulNumberLarge {

  public static void main(String[] args) {
    Scanner in = new Scanner(
        new BufferedReader(new InputStreamReader(System.in)));
    int cases = in.nextInt();
    for (int i = 1; i <= cases; ++i) {
      long n = in.nextLong();
      System.out.println("Case #" + i + ": "
          + beautiful(n));
    }
  }

  private static long beautiful(long n) {
    for (int bits = 64; bits >= 2; bits--) {
      long radix = getRadix(n, bits);
      if (radix != -1) {
        return radix;
      }
    }

    throw new IllegalStateException("Should not reach here.");
  }

  /**
   * Gets radix so that n is 111...1 (bits 1 in total) in that
   * radix.
   *
   * @return the radix. -1 if there's no such radix.
   */
  private static long getRadix(long n, int bits) {
    long minRadix = 2;
    long maxRadix = n;
    while (minRadix < maxRadix) {
       // 二分查找法
      long m = minRadix + (maxRadix - minRadix) / 2;
      long t = convert(m, bits);
      if (t == n) {
        return m;
      } else if (t < n) {
        minRadix = m + 1;
      } else {
        maxRadix = m;
      }
    }
    return -1;
  }

  /**
   * Returns the value of 111...1 (bits 1 in total) in radix.
   * 等比数列求和
   */
  private static long convert(long radix, int bits) {
    long component = 1;
    long sum = 0;
    for (int i = 0; i < bits; i++) {
      if (Long.MAX_VALUE - sum < component) {
        sum = Long.MAX_VALUE;
      } else {
        sum += component;
      }
	  // 防止内存溢出
      if (Long.MAX_VALUE / component < radix) {
        component = Long.MAX_VALUE;
      } else {
        component *= radix;
      }
    }
    return sum;
  }
}
点击查看更多内容
5人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
PHP开发工程师
手记
粉丝
1.6万
获赞与收藏
1807

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消