- 2019年5月22日に記録
20.有効な括弧
①タイトルの説明
- '('、 ')'、 '{'、 '}'、 '['、 ']'のみを含む文字列を指定して、文字列が有効かどうかを判断します。
- 有効な文字列は満たす
必要があります。左括弧は同じタイプの右括弧で閉じる必要があります。
左括弧は正しい順序で閉じる必要があります。
空の文字列は有効な文字列と見なすことができることに注意してください。 - 例1:
入力: "()"
出力:true
- 例2:
入力: "()[] {}"
出力:true
- 例3:
入力: "(]"
出力:false
- 例4:
入力: "([)]"
出力:false
- 例5:
入力: "{[]}"
出力:true
②スタックを使う(スタック)
- ブラケットのペアリングの問題、典型的なメソッドの使用
stack
。ブラケットが押し込まれたときと同様stack
に、スタックの現在の先頭がペアの括弧でスキャンされるempty
とき、ブラケットはstack
スタックをポップします。stack
たとえば、直接押し込まれていない場合({[
。 - 時間の複雑さ:
O(n)
特定の文字列とスタックでのO(1)
プッシュおよびポップ操作を1回だけトラバースしたため。 - スペースの複雑さ:
O(n)
開いているすべての括弧をスタックにプッシュすると、最悪の場合、すべての括弧がスタックにプッシュされます。例えば((((((((((
。 - コードは以下のように表示されます:
public boolean isValid(String s) {
int len = s.length();
if (len == 0) {
return true;
}
if (len % 2 != 0) {
return false;
}
Stack<Character> stack = new Stack<>();
for (int i = 0; i < len; i++) {
if (stack.isEmpty()) {
stack.push(s.charAt(i));
} else {
if (stack.peek() == '(' && s.charAt(i) == ')') {
stack.pop();
} else if (stack.peek() == '[' && s.charAt(i) == ']') {
stack.pop();
} else if (stack.peek() == '{' && s.charAt(i) == '}') {
stack.pop();
} else {
stack.push(s.charAt(i));
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
21. 2つの順序付けられたリンクリストをマージする
①タイトルの説明
- 2つの順序付けられたリンクリストを新しい順序付けされたリンクリストに結合して戻ります。新しいリンクリストは、指定された2つのリンクリストのすべてのノードを接合することによって構成されます。
- 例:
入力:1-> 2-> 4、1-> 3-> 4
出力:1-> 1-> 2-> 3-> 4-> 4
②新しくリンクされたリストの頭の助けを借りて
l1
合計l2
のvalを比較することによりl1
、新しいリンクリストを追加するか、新しいリンクリストを追加するかl2
、およびl1
またはl2
ポインターを更新するかが決定されます。- 特別な場合に注意してください:
①l1
またはl2
null、別のリンクリストに直接戻る、
②マージが完了した後、常に最後ではなくリンクリストがあります。新しいリンクリストに追加するだけです。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//可以提前判断,也可以不用判断
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
ListNode result = new ListNode(0);
ListNode cur = result;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
cur.next = l1;
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
if (l1 != null) {
cur.next = l1;
} else {
cur.next = l2;
}
return result.next;
}
22.ブラケットの生成
①タイトルの説明
- nが括弧の生成の対数を表すとすると、可能なすべての有効な括弧の組み合わせを生成できる関数を記述してください。
- たとえば、n = 3の場合、生成される結果は次のとおりです。
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
②バックトラッキング方式
public List<String> generateParenthesis(int n) {
List<String> result = new ArrayList<>();
if (n == 0) {
return result;
}
helper(result, "", 0, 0, n);
return result;
}
public void helper(List<String> result, String s, int open, int close, int n) {
if (s.length() == n * 2) {
result.add(s);
return;
}
if (open < n) {
helper(result, s + "(", open + 1, close, n);
}
if (open > close) {
helper(result, s + ")", open, close + 1, n);
}
}
③暴力法
public List<String> generateParenthesis(int n) {
List<String> result = new ArrayList<>();
if (n == 0) {
return result;
}
helper(result, "", n);
return result;
}
public void helper(List<String> result, String s, int n) {
if (s.length() == n * 2) {
if (isTrue(s)) {
result.add(s);
}
return;
}
helper(result, s + "(", n);
helper(result, s + ")", n);
}
public boolean isTrue(String s) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
count++;
} else {
count--;
}
if (count < 0) {
return false;
}
}
return count == 0;
}
23. Kでソートされたリンクリストをマージする
①タイトルの説明
- kのソートされたリンクリストを結合し、マージされたソートされたリンクリストを返します。アルゴリズムの複雑さを分析して説明してください。
- 例:
入力:[
1->4->5
、1->3->4
、2->6
]
出力:1->1->2->3->4->4->5->6
②自分の愚かな方法
- 最初に、すべてのソートされたリンクリストのノード値を取得し、再ソート後にそれらを新しいリンクリストに格納します。
- 使用する
List
として巧みにCollections.sort()
素早く並べ替えを実現。 - コードは以下のように表示されます:
public ListNode mergeKLists(ListNode[] lists) {
ListNode result = new ListNode(0);
ListNode cur = reuslt;
if (lists.length == 1) {
return lists[0];
}
List<Integer> arr = new ArrayList<>();
for (int i = 0; i < lists.length; i++) {
ListNode p = lists[i];
while (p != null) {
arr.add(p.val);
p = p.next;
}
}
Collections.sort(arr);
for (int i = 0; i < arr.size(); i++) {
ListNode node = new ListNode(arr.get(i));
cur.next = node;
cur = cur.next;
}
cur.next = null;
return result.next;
}
③カラムごとの比較(自分のバカな方法よりも効果が悪いようです)
- すべてのリンクリストがnullになるまで、すべてのリンクリストを調整し、現在の列の最小値を1つずつ調べます。
- 概略図は次のとおりです。
- コードは以下のように表示されます:
public ListNode mergeKLists(ListNode[] lists) {
ListNode result = new ListNode(0);
ListNode cur = reuslt;
if (lists.length == 1) {
return lists[0];
}
while (true) {
int min = Integer.MAX_VALUE;
int min_index = 0;
boolean flag = true;
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
if (lists[i].val < min) {
min = lists[i].val;
min_index = i;
}
flag = false;
}
}
if (flag) {
break;
}
cur.next = lists[min_index];
cur = cur.next;
lists[min_index] = lists[min_index].next;
}
cur.next = null;
return result.next;
}
④優先キューを使用する(優先キューの定義をマスターする)
- コンパレータをカスタマイズすることで、目的の
ListNode
プライオリティキューを実現します。全体の考え方は一列一列的比较
同じですが、比較プロセスが優先キューに渡されて完了します。
Queue<ListNode> q = new PriorityQueue<ListNode>(cmp);
- リンクリストの特殊性に注意してください。1つのノードがキューに格納されているようですが、実際にはリンクリスト全体が格納されています。
- プライオリティキューを初期化する際は、必ず
lists[i]
かどうかを判断してくださいnull
!
Comparator<ListNode> cmp = new Comparator<ListNode>() {
// 自定义比较器
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
};
public ListNode mergeKLists(ListNode[] lists) {
ListNode result = new ListNode(0);
ListNode cur = result;
if (lists.length == 1) {
return lists[0];
}
//建立队列,每个位置存储一个链表
Queue<ListNode> q = new PriorityQueue<ListNode>(cmp);
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
q.add(lists[i]);
}
}
while (!q.isEmpty()) {
cur.next = q.poll(); // cur.next指向满足条件的待合并节点
cur = cur.next; // 更新cur指针,指向最新节点,这时整个链表与弹出的链表是一个整体
if (cur.next != null) {
// 如果cur.next不为空,将剩余的链表重新加入优先队列
q.add(cur.next);
}
}
cur.next = null;
return result.next;
}