導入
数学系の問題では、長方形の面積を求める、傾きを求めるなど、義務教育9年間の数学の簡単な知識が出題されます。また、Bash ゲーム、グレイ コーディング、その他の質問など、より高度な数学的知識ポイントもあり、そのような高度な数学的知識ポイントを伴う質問については、私の個人的な提案は、理解してさらに多くのことを行うことですが、巻き込まれないようにすることです。この種の問題は、このような知識ポイントを知っていればコードは非常に簡単ですが、やり方が分からないとできません。実際、このテストはもはやコードのアルゴリズムの問題ではなく、数学の問題になっており、面接官であれば、このような答えを暗記するような質問は思いつかないでしょうし、選考の意味もありません。もちろん、学ぶための十分なエネルギーがあれば、より多くのことを学び、より理解して、徹底的に武装することができます。
理論的根拠
数学は、記号言語を用いて量、構造、変化、空間などの概念を研究する学問であり、ある観点からは形式科学の一種に属します。数学は、抽象化と論理的推論を使用して、物体の形状と運動を数えたり、計算したり、測定したり観察したりすることから生まれます。数学者は、新しい予想を定式化し、選択した公理と定義から厳密に導出された定理を確立するために、これらの概念を拡張します。
基礎的な数学の知識と応用は、個人およびグループの生活に不可欠な部分です。数学の基本概念の完成は、古代エジプト、メソポタミア、古代インドの古代数学書に早くも見られ、古代ギリシャではより厳密な扱いが行われています。それ以来、数学の発展は 16 世紀のルネサンスまで少しずつ続き、新しい科学的発見と数学的革新の相互作用により、今日に至るまで数学の発展が加速されました。数学は多くの国や地域で教育の一部となっています。今日、数学は科学、工学、医学、経済学、金融
など のさまざまな分野で使用されています。これらの分野への数学の応用は、応用数学と呼ばれることが多く、時には新しい数学的発見を刺激し、まったく新しい分野の発展につながります。たとえば、物理学の実質的な発展において確立された特定の理論は、数学者に特定の問題を解決するよう促すものです。さまざまな角度から。数学者もまた、実用化を目的とせずに純粋数学、つまり数学の本質そのものを研究します。多くの研究は純粋な数学から始まりますが、その過程で多くの応用が見つかります。コンピュータの終わりは数学であり、数学の終わりは哲学であり、哲学の終わりは神学です。
問題解決の経験
- 数学とコンピューターは密接な関係にあり、数式の使用は時間計算量を改善するのに非常に役立ちます。
- 数学の分野では、高度な数学の知識が含まれる質問に対する答えを暗記することは、あまり複雑にする必要はなく、理解するだけで十分であり、面接の焦点にはなりません。
- コンピュータ言語で数式を表現する方法を学びます。
- 学習するエネルギーが十分にあれば、高度な数学の知識を含む問題を暗記することができます。
アルゴリズムのトピック
7. 整数反転
問題分析: より広い範囲で Long に直接変換し、反転後に出力を強制します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int reverse(int x) {
long ret = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
ret = ret * 10 + pop;
}
if (ret > Integer.MAX_VALUE || ret < Integer.MIN_VALUE) return 0;
return (int) ret;
}
}
9. 回文番号
トピック分析: 整数が否定された後、2 つの値が等しいかどうかを比較します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public boolean isPalindrome(int x) {
// 如果为负数,刚一定不是回文,直接返回false
if (x < 0) {
return false;
}
int reverseVal = 0;
int val = x;
// 对值进行反转
while (val != 0) {
int pop = val % 10;
val /= 10;
reverseVal = reverseVal * 10 + pop;
}
if (reverseVal == x) {
return true;
} else {
return false;
}
}
}
50. パウ(x, n)
トピック分析: 半分の計算 x^n = (x 2) (n/2) により、計算量が大幅に削減されます。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public double myPow(double x, int n) {
double res = 1.0D;
// i每次操作都除2,不用一直相乘
// i = 1 时,i /= 2 就等于0了
for (int i = n; i != 0; i /= 2) {
if (i % 2 != 0) {
res *= x;
}
x *= x;
}
// 根据n的正负性,返回正数,还是倒数
return n < 0 ? 1 / res : res;
}
}
60. 順列シーケンス
トピック分析: バックトラッキングと再帰はタイムアウトになりますが、これは主に数学的手法によって最適化されており、数値は 1 から始まる連続した自然数であり、ソート結果をプッシュすることができます。式は、index = k / (n-1)! このとき、index は残りの候補番号のインデックスです。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public String getPermutation(int n, int k) {
// 用StringBuilder,比直接用String快1ms,多击败50%选手
StringBuilder res = new StringBuilder();
// n为0,直接返回
if (n == 0) {
return res.toString();
}
// 声明候选数字组
LinkedList<Integer> nums = new LinkedList();
for (int i = 1; i <= n; i++) {
nums.add(i);
}
k = k - 1;
// 查找候选数字
while (n != 0) {
// 计算需要第几个侯选值
int index = k / helper(n - 1);
res.append(nums.remove(index));
// 计算k剩余值
k %= helper(n - 1);
n--;
}
return res.toString();
}
// 实现阶乘
private int helper(int n) {
int sum = 1;
for (int i = n; i > 0; i--) {
sum *= i;
}
return sum;
}
}
66. プラスワン
トピック分析: 順番に 1 つを再帰的に追加します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int[] plusOne(int[] digits) {
int[] res = helper(digits, digits.length - 1);
return res;
}
// 递归加一
public int[] helper(int[] digits, int index) {
// 如果进位到首位,直接新建数组返回
if (index == -1) {
int[] temp = new int[digits.length + 1];
temp[0] = 1;
return temp;
}
// 不等于9就加1,等于9就进一位
if (digits[index] != 9) {
digits[index] = digits[index] + 1;
return digits;
} else {
digits[index] = 0;
digits = helper(digits, index - 1);
return digits;
}
}
}
69. xの平方根
トピック分析: 数学の外套を着て、二分法を使用します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int mySqrt(int x) {
// 特殊值处理
if (x == 0 || x == 1) {
return x;
}
int left = 0;
int right = x;
int mid = 0;
// 二分法
while (left <= right) {
mid = left + (right - left) / 2;
if (mid > x / mid) {
right = mid - 1;
} else if (mid < x / mid) {
left = mid + 1;
} else {
return mid;
}
}
// 否则返回r
return right;
}
}
149. ほとんどの点は直線上にある
問題分析: 点を固定し、この点と直線を形成する他の点を見つけて、その傾きを数えます。最も回数が多い傾きが答えになります。
コードは以下のように表示されます。
/**
* 数学 + 哈希表
*/
class Solution {
public int maxPoints(int[][] points) {
int n = points.length;
if (n <= 2) {
return n;
}
int ret = 0;
for (int i = 0; i < n; i++) {
if (ret >= n - i || ret > n / 2) {
break;
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int j = i + 1; j < n; j++) {
int x = points[i][0] - points[j][0];
int y = points[i][1] - points[j][1];
if (x == 0) {
y = 1;
} else if (y == 0) {
x = 1;
} else {
if (y < 0) {
x = -x;
y = -y;
}
int gcdXY = gcd(Math.abs(x), Math.abs(y));
x /= gcdXY;
y /= gcdXY;
}
int key = y + x * 20001;
map.put(key, map.getOrDefault(key, 0) + 1);
}
int maxn = 0;
for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
int num = entry.getValue();
maxn = Math.max(maxn, num + 1);
}
ret = Math.max(ret, maxn);
}
return ret;
}
public int gcd(int a, int b) {
return b != 0 ? gcd(b, a % b) : a;
}
}
168. Excel テーブルの列名
トピック分析: 16 進数は 0 から始まるため、整数は 1 減算され、16 進数に従って処理されます。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public String convertToTitle(int columnNumber) {
StringBuffer sb = new StringBuffer();
while (columnNumber != 0) {
columnNumber--;
sb.append((char)(columnNumber % 26 + 'A'));
columnNumber /= 26;
}
return sb.reverse().toString();
}
}
171. Excelの列番号
トピック分析: 16 進数を 10 進数に変換します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int titleToNumber(String columnTitle) {
int number = 0;
int multiple = 1;
for (int i = columnTitle.length() - 1; i >= 0; i--) {
int k = columnTitle.charAt(i) - 'A' + 1;
number += k * multiple;
multiple *= 26;
}
return number;
}
}
172. 階乗後のゼロ
トピック分析: 3 つの結論が導き出されます。 1. 0 の数を決定するには、因子 2 と 5 の数を見つけるだけで済みます。2 つ目は、2 の数は 5 より大きいので、因数 5 の数を見つけるだけです。3. 5 つの数字の間隔ごとに 5 で割り切れる数があり、これらの 5 で割り切れる数の中に、5 つの数字の間隔ごとに 25 で割り切れる別の数があるため、結果が 0 になるまで再度割る必要があります。つまり、5 で割り切れ続ける数は存在しないということです。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int trailingZeroes(int n) {
int count = 0;
while(n >= 5) {
count += n / 5;
n /= 5;
}
return count;
}
}
204. 素数を数える
トピック分析: スクリーニング方法: x が素数の場合、x 2x、3x ... より大きい x の倍数は素数であってはいけないので、それらをマークします。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int countPrimes(int n) {
int[] isPrime = new int[n];
// 初始化
Arrays.fill(isPrime, 1);
int res = 0;
for (int i = 2; i < n; ++i) {
if (isPrime[i] == 1) {
res += 1;
if ((long) i * i < n) {
for (int j = i * i; j < n; j += i) {
// 合数标记为 0
isPrime[j] = 0;
}
}
}
}
return res;
}
}
223. 長方形領域
トピック分析: ジオメトリ: 総面積 = 単一エリア 1 + 単一エリア 2 - 重複エリア。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
int area1 = (ax2 - ax1) * (ay2 - ay1), area2 = (bx2 - bx1) * (by2 - by1);
int overlapWidth = Math.min(ax2, bx2) - Math.max(ax1, bx1), overlapHeight = Math.min(ay2, by2) - Math.max(ay1, by1);
int overlapArea = Math.max(overlapWidth, 0) * Math.max(overlapHeight, 0);
return area1 + area2 - overlapArea;
}
}
233. 1の数
トピック分析: 各桁の 11 の数を列挙します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int countDigitOne(int n) {
// mulk 表示 10^k
// 在下面的代码中,可以发现 k 并没有被直接使用到(都是使用 10^k)
// 但为了让代码看起来更加直观,这里保留了 k
long mulk = 1;
int ans = 0;
for (int k = 0; n >= mulk; ++k) {
ans += (n / (mulk * 10)) * mulk + Math.min(Math.max(n % (mulk * 10) - mulk + 1, 0), mulk);
mulk *= 10;
}
return ans;
}
}
241. 算術式の設計の優先順位
トピック分析: まず、式に対して前処理を実行し、すべてのオペランド (数値と演算子を含む) を ops 配列に入れ、-1、-2、-3 を使用して演算子 +、-、* をそれぞれ表します。なぜなら、式内の演算子 op の場合、その左側の部分で考えられる計算結果を左側のセットで表し、右側の部分で考えられる計算結果を右側のセットで表すからです。この場合、演算子が式の最後の演算である場合に考えられるすべての結果は、演算子の演算に対応する対応するセット left と set right 内の要素の組み合わせの数になります。次に、式内のすべての演算子を左右の区切り文字として列挙して、対応するセットを取得します。式の最終的な結果は、これらのセットの和集合になります。
コードは以下のように表示されます。
/**
* 记忆化搜索
*/
class Solution {
static final int ADDITION = -1;
static final int SUBTRACTION = -2;
static final int MULTIPLICATION = -3;
public List<Integer> diffWaysToCompute(String expression) {
List<Integer> ops = new ArrayList<Integer>();
for (int i = 0; i < expression.length();) {
if (!Character.isDigit(expression.charAt(i))) {
if (expression.charAt(i) == '+') {
ops.add(ADDITION);
} else if (expression.charAt(i) == '-') {
ops.add(SUBTRACTION);
} else {
ops.add(MULTIPLICATION);
}
i++;
} else {
int t = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
t = t * 10 + expression.charAt(i) - '0';
i++;
}
ops.add(t);
}
}
List<Integer>[][] dp = new List[ops.size()][ops.size()];
for (int i = 0; i < ops.size(); i++) {
for (int j = 0; j < ops.size(); j++) {
dp[i][j] = new ArrayList<Integer>();
}
}
return dfs(dp, 0, ops.size() - 1, ops);
}
public List<Integer> dfs(List<Integer>[][] dp, int l, int r, List<Integer> ops) {
if (dp[l][r].isEmpty()) {
if (l == r) {
dp[l][r].add(ops.get(l));
} else {
for (int i = l; i < r; i += 2) {
List<Integer> left = dfs(dp, l, i, ops);
List<Integer> right = dfs(dp, i + 2, r, ops);
for (int lv : left) {
for (int rv : right) {
if (ops.get(i + 1) == ADDITION) {
dp[l][r].add(lv + rv);
} else if (ops.get(i + 1) == SUBTRACTION) {
dp[l][r].add(lv - rv);
} else {
dp[l][r].add(lv * rv);
}
}
}
}
}
}
return dp[l][r];
}
}
258. ビットの追加
トピック分析: 通常の質問を見つけます。3 桁の数字 'abc' の値が s1 = 100 * a + 10 * b + 1 * c の場合、数字を 1 回足すと、s2 = a + b + c になります。 、減少する差は (s1 -s2) = 99 * a + 9 * b です。各サイクル後の減少は 9 の倍数であるため、最後の値が 1 桁の場合、合計の減少は 9*n になります。このことから、9 の余りを直接取得していることがわかります。最後の num 値がゼロでない場合は、直接 num を返し、ゼロの場合は、直接 9 を返します。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public int addDigits(int num) {
// 为零直接返回
if (num == 0) {
return 0;
}
// 对9取余,相当于减去 9*n
num %= 9;
if (num == 0) {
return 9;
}
return num;
}
}
263. 醜い数字
トピック分析: ループ内で 2、3、5 で除算し、余りが 1 になると、それは醜い数になります。
コードは以下のように表示されます。
/**
* 数学
*/
class Solution {
public boolean isUgly(int n) {
if (n < 1) {
return false;
}
while (n % 2 == 0) {
n /= 2;
}
while (n % 3 == 0) {
n /= 3;
}
while (n % 5 == 0) {
n /= 5;
}
return n == 1;
}
}
273. 整数の英語表現への変換
トピック分析: 非負の整数 num の最大値が決定されているため、最大 10 桁になります。整数を英語表現に変換するには、数値を 3 桁のグループに分割し、各グループの英語表現を連結して、整数 num の英語表現を取得します。
コードは以下のように表示されます。
/**
* 递归
*/
class Solution {
String[] singles = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};
String[] teens = {"Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
String[] tens = {"", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
String[] thousands = {"", "Thousand", "Million", "Billion"};
public String numberToWords(int num) {
if (num == 0) {
return "Zero";
}
StringBuffer sb = new StringBuffer();
for (int i = 3, unit = 1000000000; i >= 0; i--, unit /= 1000) {
int curNum = num / unit;
if (curNum != 0) {
num -= curNum * unit;
StringBuffer curr = new StringBuffer();
recursion(curr, curNum);
curr.append(thousands[i]).append(" ");
sb.append(curr);
}
}
return sb.toString().trim();
}
public void recursion(StringBuffer curr, int num) {
if (num == 0) {
return;
} else if (num < 10) {
curr.append(singles[num]).append(" ");
} else if (num < 20) {
curr.append(teens[num - 10]).append(" ");
} else if (num < 100) {
curr.append(tens[num / 10]).append(" ");
recursion(curr, num % 10);
} else {
curr.append(singles[num / 100]).append(" Hundred ");
recursion(curr, num % 100);
}
}
}
278. 最初の間違ったバージョン
トピック分析: 二分法検索。値が大きすぎる場合、(left + right)/2 は使用できないことに注意してください。オーバーフローを防ぐために、left + (right -left)/2 を使用する必要があります。
コードは以下のように表示されます。
/**
* 二分法查找
*/
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
while(left <= right){
int mid = left + (right -left)/2;
if (isBadVersion(mid)){
right = mid - 1;
}else{
left = mid + 1;
}
}
return left;
}
}
292. ニムゲーム
トピック分析: バッシュ ゲーム、n % (m+1) != 0 の場合、最初のプレイヤーが常に勝ちます。両方やる場合は、整数 4 の最初の手が必ず負けます。a が先に n を取り、b が 4-n を取る限り、b が必ず勝つことが保証されます。整数が 4 の場合、残りを取り除くと勝ちになります。したがって、最初に開始するには、数字が 4 の倍数であるかどうかを判断するだけで勝敗を判断できます。
コードは次のとおりです。
/**
* 巴什博奕
*/
class Solution {
public boolean canWinNim(int n) {
if(n%4 == 0) return false;
return true;
}
}
ホームページに戻る
Leetcode 500 以上の質問を磨くことについての感想