序文
週末に何もすることがなかったので、7 月にオンラインで文字列関連のアルゴリズムを説明するビデオを視聴し、多くのフィードバックを受け取りました。ビデオの説明と一緒にメモを取り、後で読んで復習できるようにしました。私もそれを皆さんと共有できることを嬉しく思います。アルゴリズムにおいて文字列がいかに重要であるかなど、路上での丁寧な言葉についてはあまり話しません。すぐに注意事項に進みましょう。
1. 文字列
- java: String は組み込み型であり、変更できません。(変更する必要がある場合は、StringBuffer、StringBuilder、char[] などを検討してください。)
2. 分類
文字列に関連する関連質問タイプには通常、次の側面が含まれます。
- 概念的な理解: 辞書の順序
- 簡単な操作:文字の挿入、削除、回転
- ルール判定(ローマ数字変換が正当な整数か浮動小数点数か)
- 数値演算(大数加算、2進加算)
- 並べ替える、交換する
- 文字数: アナグラム
- マッチング(正規表現、全文字列マッチング、KMP、ピリオド判定)
- 動的プログラミング (LCS、編集距離、最長回文部分文字列)
- 検索(単語の変換、順列、組み合わせ)
3. 質問例
1. 交換: 01 だけを含む文字列をソートし、任意の 2 つの数字の位置を交換します。交換は最低何回必要ですか?
アイデア: 両端から中央までスイープします。スイープ プロセス中に、左側に 1 が出現すると、右側に出現した 0 と位置が交換され、左側に添字があると終了します。具体的なコードは次のとおりです。
1 public static void main(String[] strs) {
2 int count = 0;
3 int[] arrays = new int[] {
0, 0, 1, 1, 1, 0, 1, 0, 0, 1};
4 int left = 0;
5 int right = arrays.length - 1;
6 while (true) {
7 while (arrays[left] == 0) {
8 left++;
9 }
10 while (arrays[right] == 1) {
11 right--;
12 }
13 if (left >= right) {
14 break;
15 } else {
16 int temp = arrays[left];
17 arrays[left] = arrays[right];
18 arrays[right] = temp;
19 count++;
20 }
21 }
22 Logger.println("交换次数:" + count);
23 for (int array : arrays) {
24 Logger.print(array + ", ");
25 }
26 }
わかりやすくするために、数回の交換と並べ替えを行った後の文字列出力は次のとおりです。
交换次数:3
0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
2. 文字列の置換とコピー: 文字列内のすべての a を削除し、すべての b をコピーします (文字配列は十分な大きさです)
アイデア: 詳細なアイデアについてはコードのコメントを参照してください。
1 public static void main(String[] strs) {
2 char[] input = new char[]{
'a', 'b', 'c', 'd', 'a', 'f', 'a', 'b', 'c', 'd', 'b', 'b', 'a', 'b'};
3 char[] chars = new char[50];
4 for (int j = 0; j < input.length; j++) {
5 chars[j] = input[j];
6 }
7 Logger.println("操作前:");
8 for (char c:chars
9 ) {
10 Logger.print(c + ", ");
11 }
12 int n = 0;
13 int countB = 0;
14 // 1、删除a,用n当做新下标,循环遍历数组,凡是不是a的元素都放到新下标的位置,由于新n增长慢,老下标i增长快,所以元素不会被覆盖。
15 // 并且在删除a时顺便记录b的数量,以便下一步复制b时可以提前确定数组最终的最大的下标。
16 for (int i = 0; chars[i] != '\u0000' && i < chars.length; i++) {
17 if (chars[i] != 'a') {
18 chars[n++] = chars[i];
19 }
20 if (chars[i] == 'b') {
21 countB++;
22 }
23 }
24
25 // 2、复制b,由于在第一步中就已经知道了字符串中b的个数,这里就能确定最终字符串的最大下标,从最打下表开始倒着复制原字符串,碰到b时复制即可。
26 int newMaxIndex = n + countB - 1;
27 for (int k = n - 1; k >= 0; k--) {
28 chars[newMaxIndex--] = chars[k];
29 if (chars[k] == 'b') {
30 chars[newMaxIndex--] = chars[k];
31 }
32 }
33
34 Logger.println("\n操作后:");
35 for (char c:chars
36 ) {
37 Logger.print(c + ", ");
38 }
39 }
3. アスタリスクを交換します。文字列に * と数字のみが含まれる場合は、すべての * を先頭に置きます。
例: 1 * 2 * 4 * 3 => * * * 1 2 4 3
- オプション 1: 逆方向に操作します。最大の添字から開始して前方に移動します。* 以外の要素が見つかった場合は、それらを「新しい」添字に追加します。走査が完了した後、j は * の数を表し、次に 0-j だけを表します値を * に代入します。(操作後、数値の相対位置は変わりません) コードは次のとおりです。
1 public static void main(String[] strs) {
2 char[] chars = new char[]{
'1', '*', '4', '3', '*', '5', '*'};
3 // 方案一(操作后,数字的相对位置不变)
4 // 倒着操作:从最大下标开始向前遍历,遇到非*号的元素则加入"新"下标中,遍历完毕后,j即代表*号的个数,然后将0-j赋值为*即可。
5 int j = chars.length - 1;
6 for (int i = j; i >= 0; i--) {
7 if (chars[i] != '*') {
8 chars[j--] = chars[i];
9 }
10 }
11 while (j >= 0) {
12 chars[j--] = '*';
13 }
14 for (char c:chars
15 ) {
16 Logger.print(c + ", ");
17 }
18 }
出力は次のとおりです。
*, *, *, 1, 4, 3, 5,
- オプション 2 (演算後、配列の相対位置が変更されます) ループの不変式 (ループの各ステップの後に条件が true になる) に従って、クイック ソート除算: 例: [0...i-1]この質問では *、[i...j-1 ] は数値、[j...n-1] は検出されませんでした。ループ中、i と j が増加しても、この条件は変わりません。コード以下のとおりであります:
1 public static void main(String[] strs) {
2 char[] chars = new char[]{
'1', '*', '4', '3', '*', '5', '*'};
3 // 方案二(操作后,数组的相对位置会变)
4 // 快排划分,根据循环不变式(每一步循环之后条件都成立):如本题[0..i-1]是*,[i..j-1]是数字,[j...n-1]未探测,循环时,随着i和j增加,维护此条件依然不变
5 for (int i = 0, j = 0; j < chars.length; ++j) {
6 if (chars[j] == '*') {
7 char temp = chars[i];
8 chars[i] = chars[j];
9 chars[j] = temp;
10 i++;
11 }
12 }
13 for (char c:chars
14 ) {
15 Logger.print(c + ", ");
16 }
17 }
出力は次のとおりです。
*, *, *, 3, 1, 5, 4,
4. 単語の反転
例:私は学生です =》 学生です 私は学生です
アイデア:
1. まず、文字列全体を反転します。次のようになります。 I am a students => tneduts a ma I
2. スペースで各単語を決定し、各単語を反転します
コードは以下のように表示されます。
1 public static void main(String[] strs) {
2 String input = "I am a student";
3 char[] chars = input.toCharArray();
4 int i = 0;
5 int j = chars.length - 1;
6 while (i < j) {
7 swap(chars, i++, j--);
8 }
9 int front = 1;
10 int tail = 0;
11 while (front < chars.length) {
12 if (chars[front] == ' ') {
13 int frontTemp = front - 1;
14 while (tail < frontTemp) {
15 swap(chars, tail++, frontTemp--);
16 }
17 tail = front + 1;
18 }
19 front++;
20 }
21 for (char c:chars
22 ) {
23 Logger.print(c);
24 }
25 }
26
27 public static void swap(char[] chars, int index1, int index2) {
28 char temp = chars[index1];
29 chars[index1] = chars[index2];
30 chars[index2] = temp;
31 }
出力は次のとおりです。
student a am I
5. 部分文字列アナグラム: 2 つの文字列 a と b が与えられた場合、b が a の部分文字列アナグラムであるかどうかを尋ねます。
例: a= こんにちは。b=lel、lle、ello はすべて true、b=elo は false
アイデア:
-
- 1. まず、2 つの文字列がアナグラムであるかどうかを理解する必要があります。
-
- 2 つの文字列を同じルールに従って並べ替えます。並べ替え後に値が等しい場合、それらはアナグラムです。
- 2 つの文字列に出現する文字を数えます。2 つの文字列に同じ文字が同じ回数出現する場合、それはアナグラムです。
-
- 2. 次に、親文字列の最初の要素から走査します。要素を走査するたびに、現在の要素から部分文字列の長さまでの位置が間隔として使用され、部分文字列と比較されて、それがアナグラムであるかどうかが確認されます。
最後の質問は、これまでの質問で使用されたテクニックのいくつかを組み合わせたもので、非常に興味深いものです。コードはここには掲載しませんが、みんなの実践能力も刺激されます。興味のあるお子様は、書いてみるとよいでしょう。
最後に、面接の質問に対する回答の分析を紹介します。
コンテンツ量が多くスペースが限られているため、情報は PDF ドキュメントにまとめられています。2023 年の最も包括的な Android 中級および上級面接の質問への回答を含む完全なドキュメントが必要な場合は、QR コードをスキャンして入手できます。 !!!
目次
第 1 章 Java の側面
- Javaの基本
- Java コレクション
- Javaマルチスレッド
- Java仮想マシン
第2章 アンドロイド
- Android の 4 つの主要コンポーネントに関連する
- Android の非同期タスクとメッセージ メカニズム
- Android UI描画関連
- Androidのパフォーマンスチューニング関連
- Android の IPC
- AndroidシステムSDK関連
- サードパーティのフレームワーク分析
- 総合技術
- データ構造
- デザインパターン
- コンピュータネットワーク
- Kotlin のアスペクト
第 3 章 オーディオおよびビデオ開発に関する頻繁な面接の質問
- 巨大なオリジナル ビデオを非常に小さなビデオにエンコードできるのはなぜですか?どのようなテクノロジーが関係しているのでしょうか?
- ライブブロードキャストの即時開始を最適化するにはどうすればよいですか?
- 画像処理におけるヒストグラムの最も重要な役割は何ですか?
- デジタル画像のフィルタリングにはどのような方法がありますか?
- 画像からどのような特徴を抽出できるでしょうか?
- 画像再構成の品質を測定する基準は何ですか? 計算方法は?
第 4 章 フラッターの高頻度面接の質問
- ダーツ部分
- フラッター部
第 5 章 アルゴリズムの高頻度面接質問
- 素数を効率的に見つける方法
- 二分探索アルゴリズムの使い方
- 雨水問題を効率的に解決する方法
- ソートされた配列から重複要素を削除する方法
- べき乗剰余演算を効率的に実行する方法
- 最長の回文部分文字列を見つける方法
第 6 章 Andrio フレームワーク
- システム立ち上げプロセスの面接質問の分析
- バインダーの面接質問の分析
- ハンドラーの面接質問の分析
- AMS面接の質問の分析