160.相交链表
逻辑:
1.空判断:任意一个链表为空,则返回null
2.两个指针去遍历:都会相遇:
相交:则在第一个相交节点
不相交:公倍数的地方,最后的空指针
3.循环:两个指针不相等时
分别,两个指针判断,到末尾则跳到另一个链表的头部
88.合并两个有序数组
定义,三个指针
i,j 分别指向两个数组有意义的末尾,cur 指向合并数组长度的末尾
比较i,j 的大小,谁大放在cur指针的位置,并往前移
while 是防止 j 的越界
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n -1;
int cur = nums1.length - 1;
while(j >= 0){
if( i >= 0 && nums1[i] > nums2[j]){
nums1[cur] = nums1[i];
i--;
cur--;
} else {
nums1[cur] = nums2[j];
j--;
cur--;
}
}
}
}
206.反转链表
原地逆置
递归思想 --- 5行代码
1.判空:返回传进来的head
2.递归:获取最后的头指针
3.让最后非空指针的下一个指向自己
4.让自己的下一个为空
5.返回头指针
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode cur = reverseList(head.next);
head.next.next = head;
head.next = null;
return cur;
}
}
25.K 个一组翻转链表 --- 自己的思路还不清晰
代码思路:
1.构造新头节点,其下一个指向head
2.pre,end指针指向新头节点,end到时候遍历k个位置,标注未翻转的链表的前一个,相当于未翻转的链表的新头节点
3.遍历每k组,即end指针不为空
3.1 将end遍历到k位置,即要反转的末尾
3.2 如果发现 end == null,说明此时翻转的链表的节点数小于 k ,保存原有顺序就行
直接跳出循环
3.3 next指针指向end的下一个,是未翻转的head
next 指针,是反转时要断掉连接,反转断链用的
3.4 end 指针的下一个置空
3.5 新头节点的下一个为 start 指针
3.6 pre的下一个要断开
3.7 反转链表 传进去start指针 返回pre.next
3.8 【翻转链表区域】里面的尾节点的 next 指针指向【待翻转链表区域】里面的第一个节点
3.9 pre = start;
3.10 end = start;
4.返回新头节点的下一个
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
while( end.next != null){
for(int i = 0 ; i < k && end != null ; i++){
end = end.next ;
}
if(end == null){
break;
}
ListNode next = end.next;
end.next = null ;
ListNode start = pre.next;
pre.next = null;
pre.next = reverse(start);
start.next = next ;
pre = start;
end = start;
}
return dummy.next;
}
ListNode reverse(ListNode head){
if(head == null || head.next == null) return head;
ListNode cur = reverse(head.next);
head.next.next = head;
head.next = null;
return cur;
}
}
141.环形链表 --- 快慢指针的应用
两个判空操作:
head == null || head.next == null ---- false
fast == null || fast.next == null ---- false
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null) return false;
ListNode p = head , q = head.next;
while(p != q){
if(q == null || q.next == null) return false;
p = p.next;
q = q.next.next;
}
return true;
}
}
142.环形链表 II
就是在相遇的位置,重新开始遍历
定义两个指针,一个头,一个相遇位置,逐个往下遍历
由于,数学的相等,一定会在环形的开始节点 相遇
如果都没相遇,则返回空
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode p = head, q = head;
while(q != null && q.next != null){
p = p.next;
q = q.next.next;
if(p == q){
ListNode b = q;
ListNode a = head;
while( a != b){
a = a.next;
b = b.next;
}
return a;
}
}
return null;
}
}
21.合并两个有序链表
代码逻辑:
构造新头节点,和遍历指针pre
l1,l2分别是传进来的两个链表的头节点
循环一直到某个链表为空
比较l1,l2指针的大小,小于等于的让pre指向它,l1(小的一方)往后遍历进行比较
哪个链表不为空,则接到pre后面
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1);
ListNode pre = dummy;
while(list1 != null && list2 != null){
if(list1.val <= list2.val){
pre.next = list1;
list1 = list1.next;
} else {
pre.next = list2;
list2 = list2.next;
}
pre = pre.next;
}
if(list1 != null){
pre.next = list1;
}
if(list2 != null){
pre.next = list2;
}
return dummy.next;
}
}
66.加一
代码逻辑:
从后往前, 遍历数组
如果是9 ,则该位置0,继续for循环
如果不是9,则该位置+1,并直接返回数据
在循环中如果每一位都是9,则没有返回,要进行数组的扩容操作
扩容操作
1.new 新数组 大小为原数组的位数+1
2.第一位置1,其他位,默认为0
3.返回新数组
class Solution {
public int[] plusOne(int[] digits) {
for(int i = digits.length -1 ;i >= 0 ;i--){
if(digits[i] == 9){
digits[i] = 0;
} else {
digits[i] = digits[i] + 1;
return digits;
}
}
int[] res = new int[digits.length + 1];
res[0] = 1;
return res;
}
}
栈和队列 --- 一般都是匹配问题
20.有效的括号
代码逻辑:
如果,其长度为奇数,则必定有一个不匹配
构造一个字符栈,存左括号
把字符串转换为数组的形式,方便获取字符串中的每个字符
string.toCharArray(); 方法要记住
遍历字符串数组
如果是左边的压入栈中
否则,进行判断
如果,栈为空,不匹配
如果,不对应。。。。。对应,则栈要弹出
最后,返回栈是否为空
class Solution {
public boolean isValid(String s) {
if(s.length() % 2 == 1) return false;
Stack<Character> stack = new Stack<Character>();
char charlist[] = s.toCharArray();
for(int i = 0 ; i < charlist.length ; i++){
if(charlist[i] == '(' || charlist[i] == '[' ||charlist[i] == '{'){
stack.push(charlist[i]);
} else {
if(stack.isEmpty()) return false;
char re = stack.peek();
if(re == '(' && charlist[i] == ')' ||
re == '[' && charlist[i] == ']' ||
re == '{' && charlist[i] == '}'){
stack.pop();
} else {
return false;
}
}
}
return stack.isEmpty();
}
}
155.最小栈 --- 写了好几遍记住了
class MinStack {
Stack<Integer> stack = null;
Stack<Integer> min = null;
public MinStack() {
stack = new Stack<Integer>();
min = new Stack<Integer>();
}
public void push(int val) {
stack.push(val);
if(min.isEmpty() || min.peek() > val){
min.push(val);
} else {
min.push(min.peek());
}
}
public void pop() {
stack.pop();
min.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return min.peek();
}
}
150.逆波兰表达式求值 --- 后缀表达式求值
代码逻辑 : 栈的匹配问题
注意:数字弹出后 要 区分 前后顺序 除法是需要先后顺序的
数字栈,遇到符合,弹出两个对应 操作即可
class Solution {
public int evalRPN(String[] tokens) {
int left = 0 , right = 0 ,tmp = 0;
Stack<Integer> stack = new Stack<Integer>();
for(String token : tokens){
if(!"+".equals(token) &&
!"-".equals(token) &&
!"*".equals(token) &&
!"/".equals(token)){
stack.push(Integer.valueOf(token));
} else {
right = Integer.valueOf(stack.pop());
left = Integer.valueOf(stack.pop());
if("+".equals(token)){
tmp = left + right;
stack.push(tmp);
tmp = 0;
} else if("-".equals(token)){
tmp = left - right;
stack.push(tmp);
tmp = 0;
} else if("*".equals(token)){
tmp = left * right;
stack.push(tmp);
tmp = 0;
} else if("/".equals(token)){
tmp = left / right;
stack.push(tmp);
tmp = 0;
}
}
}
return stack.pop();
}
}