徒手挖地球八周目
NO.19 删除链表的倒数第N个节点 中等
思路一:两次遍历 第一次遍历得到链表的长度L,第二次遍历删除第(L-N+1)个元素。
public ListNode removeNthFromEnd(ListNode head, int n) {
int len=0;
// 依然是借助哑节点
ListNode dummy=new ListNode(0),q=head;
dummy.next=head;
// 第一次遍历获取链表长度
while (q!=null){
len++;
q=q.next;
}
// 第二次遍历找到待删除节点的前一个节点
q=dummy;
len-=n;
while (len>0){
len--;
q=q.next;
}
// 删除目标节点
q.next=q.next.next;
return dummy.next;
}
操作执行了2L-n步,时间复杂度为O(L)。
思路二:双指针一次遍历 1. 用两个指针p、q分别指向链表的开头(哑节点)。2. 先让q指针逐步移动到距离p指针n+1的位置上,也就是上p指针和q指针间隔n个节点。3. 让p指针和q指针同时向后移动,直至q指针为null。4. 此时p指针指向的节点的下一个节点就是待删除节点,p.next=p.next.next删除即可。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode p=dummy;
ListNode q=dummy;
// 首先让q指针移动到和p指针间隔n个元素的位置
for (int i=1;i<=n+1;i++){
q=q.next;
}
// 此时让p和q保持间距的情况下,同时向后移动,直到q为null
while (q!=null){
q=q.next;
p=p.next;
}
// 删除目标节点
p.next=p.next.next;
return dummy.next;
}
操作执行了L+n+1步,时间复杂度为O(L)。
NO.20 有效括号 简单
思路一:栈 学校的数据结构课就是那这个作为例子来引入栈结构的。1. 遍历表达式中每个字符,如果是’(‘或’[]‘或’{‘就放入栈中。2. 如果是’)‘或’]‘或’}'就弹出栈顶字符top,如果此时栈为空或者将此时被遍历字符和top不匹配,则说明表达式无效。3. 遍历完所有字符,检查栈是否为空,如果不为空则表达式无效,反之有效。
public boolean isValid(String s) {
if (s==null||s.equals(""))return true;
// 用hashmap存储括号对
HashMap<Character,Character> map=new HashMap<>();
map.put(')','(');
map.put(']','[');
map.put('}','{');
// 用栈来保存遍历到的'(' '[' '{'
Stack<Character> stack=new Stack<>();
for (int i=0;i<s.length();i++){
char c = s.charAt(i);
// 如果map中没有c这个key,则说明c是(或[或{,就存入栈中(题目说只有六种字符)
if (!map.containsKey(c)){
stack.push(c);
}else {//如果存在c这个key则说明,c是)或]或},就需要去和栈顶字符进行匹配
// 如果栈为空,则无法匹配
if (stack.size()==0)return false;
// 取出栈顶元素
Character top = stack.pop();
// 如果map中c的value和栈顶元素top不相等,则无法匹配
if (map.get(c)!=top)return false;
}
}
// 遍历完所有字符之后,检查栈是否为空,如果为空则匹配,反之无法匹配
return stack.isEmpty();
}
时间复杂度:O(n)
NO.21 合并两个有序链表 简单
思路一:迭代法 这个题目也是学校老师讲述数据结构课程时说的。1. 创建一个新的头结点dummy,用prehead指针指向新创建的dummy头结点,用p指针指向l1链表的头结点,q指针指向l2链表的头结点。2. 比较p指向的节点的值和q指向的节点的值,如果p指向的节点值小,就让prehead的next指向p所指向的节点,然后prehead和p向后移动,反之就让prehead的next指向q所指向的节点,然后prehead和q向后移动。3. 直到p或者q指针有一个为null为止,最后检查p或者q是否有不为null的指针,如果有就让prehead指向非空的p或者q。返回dummy.next即可。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1==null)return l2;
if (l2==null)return l1;
ListNode dummy=new ListNode(0);
ListNode prehead=dummy,p=l1,q=l2;
while (p!=null&&q!=null){
if (p.val<q.val){
prehead.next=p;
prehead=prehead.next;
p=p.next;
}else {
prehead.next=q;
prehead=prehead.next;
q=q.next;
}
}
// 最后检查p或者q是否有不为null的指针
if (p!=null)prehead.next=p;
if (q!=null)prehead.next=q;
return dummy.next;
}
思路二:递归法 其实递归法不能算是第二个思路,只能说是思路一的另一种实现。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}
if(l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
递归好难理解。。。还是太菜还是太菜还是太菜。。。