591. 标签验证器【困难题】【每日一题】
思路:
分类讨论,我分不明白,对答案的写法加了注释。
代码:
class Solution {
public boolean isValid(String code) {
int n = code.length();
char[] chars = code.toCharArray();
Deque<String> deque = new ArrayDeque<>();
int i = 0;
while (i < n){
//遇到 <
if (chars[i] == '<'){
//如果是在code结尾,直接返回false
if (i == n-1){
return false;
}
//不在code结尾的话,判断这是单纯的 < (左标签)还是 </ (右标签)还是 <! (cdata)
//如果是 </ 说明这是右标签
if (chars[i+1] == '/'){
//求出code字符串中从 i 位置往右遇到的第一个 > 的位置
int j = code.indexOf('>',i);
//如果 j < 0 说明没找到 那么说明右标签没有闭合,返回false
if (j < 0){
return false;
}
//取出标签名
String tagName = code.substring(i+2,j);
//判断队列 deque 中是否为空 或者 队列顶部需要闭合的左标签是否与当前这个右标签名字相同
if (deque.isEmpty() || !deque.peek().equals(tagName)){
//如果为空 说明没有待匹配的左标签 ; 如果 不相同 说明当前右标签与左标签不匹配,均不合法,返回false
return false;
}
//能走到这里说明右标签与左标签匹配成功,queue中删除待匹配的左标签
deque.pop();
//更新待判断的下标
i = j+1;
//如果deque就此空了 且 i 不在字符串结尾处 那么说明左右标签都已经闭合关闭,但仍有字符在标签之外,此时字符串依然不合法
if (deque.isEmpty() && i != n){
return false;
}
}else if (chars[i+1] == '!'){// cdata
//先判断deque是否为空,因为cdata只能出现在标签体内,如果已经出现了cdata,在它之前却没有左标签出现,那么一定是不合法的
if (deque.isEmpty()){
return false;
}
//cdata 的 左侧固定字符为 <![CDATA[ 如果从i往右数第9位就已经超出 code了,说明连cdata都不合法,更别提cdata右侧还得有 右标签
if (i + 9 > n){
return false;
}
//截取 code中 cdata体 的左侧固定字符长度
String cdata = code.substring(i+2,i+9);
//判断与标准左侧固定字符( [CDATA[ )是否匹配
if (!"[CDATA[".equals(cdata)){
//不匹配直接返回false
return false;
}
//求 ]]> 这个cdada的右侧固定字符串在code中 i位置往右第一次出现的位置下标
int j = code.indexOf("]]>",i);
//如果没有出现,说明cdata无法闭合,返回false
if (j < 0){
return false;
}
//更新待判断的下标
i = j+3;
}else {// 左标签
//求出当前 < 右侧第一个 > 的下标
int j = code.indexOf('>',i);
//如果不存在,说明不合法,返回false
if (j < 0){
return false;
}
//求出标签名
String tagName = code.substring(i+1,j);
//判断标签名长度是否合法,合法长度为 [1,9]
if (tagName.length() < 1 || tagName.length() > 9){
return false;
}
//判断标签名是否均为大写字母,合法应为全部大写
for (char c : tagName.toCharArray()) {
if (!Character.isUpperCase(c)){
return false;
}
}
//将标签名压入栈
deque.push(tagName);
//更新待判断的下标
i = j+1;
}
}else {//没有遇到 < 说明这就是普通文本,直接跳过即可
//不过要判断此时是否有标签尚未闭合,如果标签已全部闭合却遇到了普通文本,那么这是不合法的
if (deque.isEmpty()){
return false;
}
i++;
}
}
//当code遍历结束,如果是合法的,那么此时标签完全闭合,deque是空的
return deque.isEmpty();
}
}
剑指 Offer II 024. 反转链表【简单题】
思路:【迭代】
定义一个正向链表,指针
cur
初始指向head
,定义一个逆向链表,指针pre
初始指向null
。
正向遍历链表,同时将遍历到的节点cur
原来的指向断开,指向pre
节点,然后更新pre
为cur
节点。
最后返回pre
指向的链表。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while(cur != null){
//定义正向链表中 cur的下一个节点
ListNode next = cur.next;
//将cur的下一个节点指向 pre
cur.next = pre;
//将pre更新为当前cur节点
pre = cur;
//继续遍历正向链表,将cur更新为原本的下一个节点
cur = next;
}
return pre;
}
}
剑指 Offer II 025. 链表中的两数相加【中等题】
思路1【反转链表】
利用
024
题 反转链表,将传入链表l1
和l2
反转后对应位进行相加进位。
将所得的结果存入新的节点并放在ans
链表头部。
最后返回ans
链表。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//反转 l1 和 l2
ListNode r1 = reverseList(l1),r2 = reverseList(l2);
//进位标志位
int add = 0;
//表示 和 的链表,初始为 null
ListNode ans = null;
//当 r1不为空 或 r2不为空 或 仍有进位
while(r1 != null || r2 != null || add != 0){
int x1 = r1 == null ? 0 : r1.val;//求出r1的值
int x2 = r2 == null ? 0 : r2.val;//求出r2的值
int sum = x1 + x2 + add;//相加
add = sum / 10;//求进位
sum = sum % 10;//当前位的和
//建立表示当前位的 和节点
ListNode curNode = new ListNode(sum);
//将和节点指向ans
curNode.next = ans;
//ans更新为和节点
ans = curNode;
//遍历r1的下一位
if (r1 != null){
r1 = r1.next;
}
//遍历r2的下一位
if (r2 != null){
r2 = r2.next;
}
}
return ans;
}
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while(cur != null){
//定义正向链表中 cur的下一个节点
ListNode next = cur.next;
//将cur的下一个节点指向 pre
cur.next = pre;
//将pre更新为当前cur节点
pre = cur;
//继续遍历正向链表,将cur更新为原本的下一个节点
cur = next;
}
return pre;
}
}
思路2【栈】【进阶版不反转链表解答此题】【题解】
遍历两个链表,将其值压入栈,然后再依次取出栈顶元素相加求进位。
将所得的结果建立新的节点并放在ans
链表头部。
最后返回ans
链表。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Deque<Integer> stack1 = new ArrayDeque<Integer>();
Deque<Integer> stack2 = new ArrayDeque<Integer>();
while (l1 != null) {
stack1.push(l1.val);
l1 = l1.next;
}
while (l2 != null) {
stack2.push(l2.val);
l2 = l2.next;
}
int carry = 0;
ListNode ans = null;
while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
int a = stack1.isEmpty() ? 0 : stack1.pop();
int b = stack2.isEmpty() ? 0 : stack2.pop();
int cur = a + b + carry;
carry = cur / 10;
cur %= 10;
ListNode curnode = new ListNode(cur);
curnode.next = ans;
ans = curnode;
}
return ans;
}
}