3.配列の重複番号
件名の説明:
長さ\(N- \)アレイの全ての数である\(0 \)に\(N-1 \)の範囲。一部のデジタル配列が重複しているが、反復的であり、各桁が数回繰り返されるかわからないどのように多くの番号がわかりません。重複した数字のいずれかの配列を見つけてください。例えば、入力の長さ\(7 \)アレイ(\ \ {2,3,1,0,2,5,3 \} \)、次いで、第1の繰り返し数の対応する出力(\ 2 \) 。
Input:
{2, 3, 1, 0, 2, 5}
Output:
2
アイデア:
最も直接的な戦略は、ハッシュテーブルを使用することで、時間の複雑さがある\(O(n)は\)が、それは時間効率がされて増加サイズ\(nはO()\)価格のハッシュテーブルの、することができますスペースの複雑さを作るためのより良い方法が還元され\(O(1)\) 。
配列内の要素のタイトル\(0 \)に(N-1 \)\範囲で、我々は、配列の各要素は、要素の走査終了を開始する\(J =番号[I] \) と添え字をある\(J \)場合、位置スイッチング素子\(番号[j]が\)すでにに等しい(J \)を\次に、デジタル交換しない、\(J \)を(デジタル繰り返される私はNE Jを\(\ \) )。
public class Solution {
/**
* numbers: 数组;length: 数组的长度;duplication[0]: 存放任意一个重复的数字
* 返回为 true 代表为数组中存在重复数字,false 代表没有
*/
public boolean duplicate(int[] numbers,int length,int[] duplication) {
for(int i = 0; i < length; i++) {
while(i != numbers[i]) {
int j = numbers[i];
if(numbers[j] == j) {
//要替换的位置下标 j 上的元素已经为 j,说明已经存在重复元素
duplication[0] = j;
return true;
}
//进行位置上的元素替换
numbers[i] = numbers[j];
numbers[j] = j;
}
}
return false;
}
}
4. 2次元配列を探します
件名の説明:
(同一の各一次元アレイの長さ)は、上から下に増加する順に、二次元アレイの各列を並べ替えるために、昇順ソートの順に左から右に、各行。完全な機能、例えば、二次元アレイと整数を入力し、配列の整数が含まれているか否かが判断されます。
Consider the following matrix:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
Given target = 5, return true.
Given target = 20, return false.
アイデア:
その下側には、特定の数よりも大きい場合、その左側、特定の数よりも小さい場合、二次元アレイの数。したがって、右上から見始めることができ(ターゲット\)を\素子が電流に等しい場合、インターバルと現在の要素の大小関係を絞り込むこと\(ターゲット\) 、返し(\ trueに)\、現在の要素がより小さい場合(\ターゲット\) 、左隣の位置、現在の要素がより大きい場合\(目標\)ダウンし、次の位置、;次の位置が配列の範囲外にある場合、返し\(falseに\) 。
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int m = array.length, n = array[0].length;
int i = 0, j = n - 1;
while(i < m && j >= 0) {
if(array[i][j] == target) {
return true;
}else if(array[i][j] < target) {
i++;
}else{
j--;
}
}
return false;
}
}
5.スペースを置き換えます
件名の説明:
、にそれぞれのスペースを置き換えるために、文字列関数を実装してください「20%。」例えば、場合、文字列は、文字列が我々%20Are%20Happy後に交換された後に私たちは、満足しているです。
アイデア:
この質問は、直接使用することができます\(文字列\)される(\)(置き換え)\または\(でReplaceAll()\)メソッドが、この質問の意図は文字の配列でこの機能を実現することです。
public class Solution {
public String replaceSpace(StringBuffer str) {
return str.toString().replace(" ", "%20");
}
}
文字列は、代替的に、文字列のサイズに対応するためにポインタ拡張\(P1 \)を文字列の元の終了時に、ポインタ\(P2 \)は、文字の拡大アレイの端を指します。ポインタ\(P1の\) 、前方$ P1 $遭遇非空白文字を移動するには、文字がためにコピーされます(のP2 \)\位置、\(のP2 \)を前方に1を移動するには、\(P1の\は)空間に遭遇文字、\(P2 \)は前方3を移動するために、\(P2 \) 3 "20%"のその後の配置;直接ポインタ両端を満たしています。
public class Solution {
public String replaceSpace(StringBuffer str) {
int p1 = str.length() - 1;
//增大数组空间
for(int i = 0; i <= p1; i++) {
if(str.charAt(i) == ' ') {
str.append(" ");
}
}
int p2 = str.length() - 1;
while(p1 < p2) {
if(str.charAt(p1) != ' ') {
str.setCharAt(p2--, str.charAt(p1--));
}else {
str.setCharAt(p2--, '0');
str.setCharAt(p2--, '2');
str.setCharAt(p2--, '%');
p1--;
}
}
return str.toString();
}
}
6.リストの最後からプリントヘッド
件名の説明:
ヘッドエンドからの復帰シーケンスのリストに従って、リストを入力します\(ArrayListの\) 。
アイデア:
考えました:
再帰的な方法は、リストを印刷する逆。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> ret = new ArrayList<>();
if(listNode == null) {
return ret;
}
dfs(listNode, ret);
return ret;
}
public void dfs(ListNode listNode, ArrayList<Integer> ret) {
if(listNode.next != null) {
dfs(listNode.next, ret);
}
ret.add(listNode.val);
}
}
2考えます:
リストを取得する逆の方法を使用して第1の補間方法。
第1の補間ノードヘッダ\は(ヘッド\)の値が格納されていない追加のノードであり、\(head.nextの\)は、リストの最初のノードです。新しいノードを挿入する必要がある場合\(ノード\)を、挿入ノードの位置\(ヘッド\)と\(head.nextは\)挿入、間にある(node.next head.next = \)\、\(ノードhead.next = \) 。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ListNode head = new ListNode(-1);
while(listNode != null) {
ListNode node = listNode.next;
listNode.next = head.next;
head.next = listNode;
listNode = node;
}
ArrayList<Integer> ret = new ArrayList<>();
while(head.next != null) {
ret.add(head.next.val);
head = head.next;
}
return ret;
}
}
3思考:
スタック構造を使用してください。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*/
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while(listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
ArrayList<Integer> ret = new ArrayList<>();
while(!stack.isEmpty()) {
ret.add(stack.pop());
}
return ret;
}
}
7.バイナリツリーを再構築
件名の説明:
そして、再構築したバイナリツリーの先行順走査でバイナリツリーの先行順走査で結果を入力してください。仮定結果先行順走査順序と重複する数字の入力は無料です。入る前に例えば配列先行順走査(\ \ {1,2,4,7,3,5,6,8 \} \)を、シーケンス先行順(\ \ {4,7,2,1,5,3、 8,6 \} \) 、バイナリツリーの再構成は、図及び二分木のルートノードを返します。
アイデア:
これは、二分木のルートノードおよび先行順走査シーケンスの先行順走査、左サブツリー及び右サブツリーの範囲の値から決定することができます。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int[] pre,int [] in) {
return buildSubTree(pre, in, 0, 0, pre.length);
}
//pStart 是子树在前序遍历数组的起始索引,iStart 是子树在中序遍历数组的起始索引,len 是子树节点数目(大小)
public TreeNode buildSubTree(int[] pre, int[] in, int pStart, int iStart, int len) {
if(len == 0) {
return null;
}
//子树根节点
int rootVal = pre[pStart];
TreeNode root = new TreeNode(rootVal);
//寻找中序遍历数组中子树的根节点
int index = iStart;
while(in[index] != rootVal) {
index++;
}
int lLen = index - iStart; //根节点左子树的大小
int rLen = len - lLen - 1; //根节点右子树的大小
//左右孩子节点
TreeNode lNode = buildSubTree(pre, in, pStart + 1, iStart, lLen);
TreeNode rNode = buildSubTree(pre, in, pStart + lLen + 1, iStart + lLen + 1, rLen);
root.left = lNode;
root.right = rNode;
return root;
}
}
8.下ノードバイナリツリー
件名の説明:
前記ノードの指定されたバイナリツリーは、先行順走査順序とリターンの次のノードを見つけます。ツリー内のノードは、左と右の子ノードだけでなく、親ノードが指すポインタが含まれている含まれていることに注意してください。以下、木から子へのポインタを表す点線で示す子ノードポインタから親ノードを指し、実線で親ノードをノード。
アイデア:
次のノードのINORDERトラバーサル順序は、次の2つの場合に議論であってもよいです。
- 如果一个节点的右子树不为空,那么该节点的下一个节点是其右子树的最左节点。例如:上图中的节点 \(b\) 的下一个节点为 \(h\), 节点 \(a\) 的下一个节点是 \(f\),节点 \(e\) 的下一个节点是 \(i\) 。
- 如果一个节点的右子树为空,我们沿着父节点的指针往上遍历,如果遍历的某个节点是其父节点的左孩子节点,则该节点的父节点便是我们要寻找的下一个节点。例如:上图中的节点 \(i\) 的下一个节点为 \(a\),节点 \(d\) 的下一个节点为 \(b\)。
/*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode){
if(pNode == null) {
return null;
}
//右子树不为空,寻找右子树最左端
if(pNode.right != null) {
TreeLinkNode node = pNode.right;
while(node.left != null) {
node = node.left;
}
return node;
}
//右子树为空,往父节点方向向上寻找,直到找到某个节点是其父节点的左孩子
if(pNode.right == null) {
while(pNode.next != null) {
TreeLinkNode parent = pNode.next;
if(parent.left == pNode) {
return parent;
}
pNode = parent;
}
}
return null;
}
}
9. 用两个栈实现队列
题目描述:
用两个栈来实现一个队列,完成队列的 \(Push\) 和 \(Pop\) 操作。 队列中的元素为 \(int\) 类型。
思路:
使用两个栈 \(in\) 和 \(out\),\(in\) 栈负责数据的入栈操作,\(out\) 栈负责数据的出栈操作。需要注意的是,\(in\) 栈的数据倾倒到 \(out\) 栈过程要一次性将 \(in\) 栈全部数据倒入。
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>(); //in 栈
Stack<Integer> stack2 = new Stack<Integer>(); //out 栈
public void push(int node) {
stack1.push(node); //入栈数据全部进入 in 栈
}
public int pop() {
if(stack2.isEmpty()) {
//out 栈为空,要将 in 栈全部数据一次性倾倒到 out 栈
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
参考
- https://cyc2018.github.io/CS-Notes/#/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%203~9
- 《剑指OFFER 名企面试官精讲典型编程题 第2版》