查找旋转数组
思路:1. 查找中间数 2. 如果a[l] < a[m] 说明左边有序 如果x < a[l] 说明目标数在右边,反之在左边
import java.util.*;
public class Finder {
public int findElement(int[] a, int n, int x) {
// write code here
int l = 0, r = n-1;
// 注意 要有等号
while (l <= r) {
int m = l + (r-l)/2;
if (a[m] == x) return m;
if (a[m] > x) {
//前面有序
if (a[l] < a[m] && x < a[l]) {
l = m+1;
}else {
r = m-1;
}
}else {
//后面有序
if (a[m] < a[r] && x > a[r]) {
r = m-1;;
}else {
l = m+1;
}
}
}
return -1;
}
}
合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals == null || intervals.length ==0 || intervals.length == 1) return intervals;
// 按照第一个元素从小到大排序
Arrays.sort(intervals, (a1,a2) -> a1[0] - a2[0]);
int[] tmp = intervals[0];
List<int[]> res = new ArrayList<>();
for (int i = 1; i < intervals.length; i++) {
// 如果第一个数组的第二个元素比第二个数组的第一个元素小,说明不需要合并
if (tmp[1] < intervals[i][0]) {
res.add(tmp);
tmp = intervals[i];
} else {
// 否则找两个数组的第二个元素最大值作为合并后的第二个元素
tmp[1] = Math.max(tmp[1], intervals[i][1]);
}
}
// 保存最后一个合并后的数组
res.add(tmp);
int[][] ans = new int[res.size()][];
for (int i = 0; i < res.size(); i++) {
ans[i] = res.get(i);
}
return ans;
}
}
最长无重复子数组
给定一个长度为n的数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。
子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
public static int lengthOfLongestSubstring(int[] ss) {
if (ss == null) return 0;
Map<Integer,Integer> map = new HashMap<>();
int len = ss.length;
int max = 0;
//双指针标记无重复子数组起止下标
for (int s = 0, e = 0; e < len; e++) {
//如果重复则起点修改为在重复数字后一位
if (map.containsKey(ss[e])) {
s = Math.max(map.get(e)+1,s);
}
//放入数字和下标
map.put(ss[e],e);
//比较最大长度
max = Math.max(max,(e-s+1));
}
return max;
}
思路:1. 2个有序链表合并 2. 用归并合并,时间复杂度是O(NlogN)
class Solution {
public ListNode mergeKLists(ListNode[] nodes) {
if (nodes == null || nodes.length == 0) return null;
return merge(lists, 0, nodes.length-1);
}
private ListNode merge(ListNode[] lists, int lo, int hi) {
// base case
if (lo == hi) {
return lists[lo];
}
int mid = lo + (hi - lo) / 2;
// 递归合并前后两部分
ListNode l1 = merge(lists, lo, mid);
ListNode l2 = merge(lists, mid + 1, hi);
return mergeTwoLists(l1, l2);
}
public ListNode mergeTwo (ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode res = new ListNode(-1);
ListNode cur = res;
while (l1 != null || l2 != null) {
if (l1 == null) {
cur.next = l2;
return res.next;
}
if (l2 == null) {
cur.next = l1;
return res.next;
}
if (l1.val < l2.val) {
cur.next = l1;
l1 = l1.next;
}else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
return res.next;
}
}
思路:1. 双指针,快指针先走k步,反转s到e。
2. 反转后的e是头指针,s的下一个是递归反转后的链表。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null) return null;
ListNode s = head, e = head;
int i = k;
while (i-- > 0) {
if (e == null) return head;
e = e.next;
}
ListNode newHead = reverse(s, e);
s.next = reverseKGroup(e, k);
return newHead;
}
public ListNode reverse(ListNode cur, ListNode e) {
ListNode pre = null;
ListNode next = null;
while (cur != e) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转
思路:1. 快慢指针 快指针先走到m的前一个
2. 保存连接点和反转后的尾巴节点
3. 反转m到n节点
4. 进行连接点和尾巴节点拼接
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
if (head == null) return null;
ListNode cur = head;
ListNode pre = null;
while (m > 1) {
pre = cur;
cur = cur.next;
m--;
n--;
}
ListNode con = pre, tail = cur;
while (n > 0) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
n--;
}
if (con == null) {
head = pre;
}else {
con.next = pre;
}
tail.next = cur;
return head;
}
}
思路:1. 快慢指针 快指针先走n步 2. 快慢指针一起走,快指针走到最后一个节点,慢指针刚好走到要删除节点的前一个节点 3. 删除节点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null || n == 0) return null;
ListNode pre = new ListNode(-1);
pre.next = head;
ListNode cur = pre;
ListNode e = pre;
//快指针
while (n-- > 0) {
e = e.next;
}
//让慢指针走到要删除节点的前一个节点
while(e.next != null) {
cur = cur.next;
e = e.next;
}
cur.next = cur.next.next;
return pre.next;
}
}
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
思路:
1. 快慢指针 快指针一次走2步 慢一次一步
2. 快慢指针相遇时,说明有环。将快指针重置到head
3.快慢指针每次都走1步,再次相遇时就是入环点
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) return null;
ListNode fast = head, slow = head;
while (true) {
//如果没有环 则结束
if (fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
//第一次相遇
if (fast == slow) break;
}
//相遇到将fast重置到head
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
//再次相遇即环形入口点
return slow;
}
}
两数相加
思路
1. 2个链表 哪个不为空 sum += node.val
2. 当前节点的值为 sum %10
3. 保存进位,sum / 10
4. 如果进位不为0,则还需要创建新节点。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode res = new ListNode(-1);
ListNode cur = res;
int sum = 0;
while (l1 != null || l2 != null || sum != 0) {
if (l1 != null) {
sum += l1.val;
l1 = l1.next;
}
if (l2 != null) {
sum += l2.val;
l2 = l2.next;
}
cur.next = new ListNode(sum % 10);
cur = cur.next;
sum /= 10;
}
return res.next;
}
}
相交链表
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode a = headA;
ListNode b = headB;
while (a != b) {
a = a== null ? headB : a.next;
b = b == null ? headA : b.next;
}
return a;
}
}
排序链表
思路:
1. 快慢指针找到中点
2. 将链表一分为2
3. 递归排序左边,排序右边,然后进行合并
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
}
private ListNode mergeSort(ListNode head) {
if (head == null || head.next == null)return head;
ListNode tmp = new ListNode(Integer.MIN_VALUE);
tmp.next = head;
ListNode fast = tmp, slow = tmp;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
ListNode head2 = slow.next;
slow.next = null;
head = mergeSort(head);
head2 = mergeSort(head2);
ListNode pre = tmp;
while (head != null && head2 != null) {
if (head.val < head2.val) {
pre.next = head;
pre = pre.next;
head = head.next;
}else {
pre.next = head2;
pre = pre.next;
head2 = head2.next;
}
}
if (head != null) {
pre.next = head;
}
if (head2 != null) {
pre.next = head2;
}
return tmp.next;
}
}
旋转链表
思路:
1. 先计算链表长度,如果len % k == 0 则不需要旋转。
2. 快慢指针,快指针先走k节点,慢指针开始走
3. 当快指针走到尾节点时,慢指针走到要旋转节点的前一个节点
4. 慢节点的下一个保存为头节点,慢节点的下一个置为null,尾节点的下一个连接到头节点。
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || k <= 0) return head;
int len = 0;
ListNode tmp = head;
while (tmp != null) {
len++;
tmp = tmp.next;
}
k = k % len;
if (k == 0) return head;
ListNode slow = head;
ListNode fast = head;
while (k-- > 0) {
fast = fast.next;
}
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
ListNode res = slow.next;
slow.next = null;
fast.next = head;
return res;
}
}
删除排序链表的重复元素
思路:
1. 创建虚拟 节点
2. 快慢指针,快指针的下一个和当前节点的值进行比较,如果相等则后移
3. 判断慢指针的下一个是否是快指针,如果是快指针,则慢指针后移
4. 如果不是则慢指针的下一个=快指针的下一个
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if ( head == null) return head;
ListNode dump = new ListNode (Integer.MIN_VALUE);
dump.next = head;
ListNode slow = dump;
ListNode fast = head;
while (fast != null) {
while (fast.next != null && fast.next.val == fast.val) fast = fast.next;
if (slow.next == fast) {
slow = slow.next;
}else {
slow.next = fast.next;
}
fast = fast.next;
}
return dump.next;
}
}
奇偶链表
class Solution {
public ListNode oddEvenList(ListNode head) {
if (head == null) return null;
ListNode odd = head, even = head.next, evenHead = head.next;
while (even != null && even.next != null) {
//奇数下一个是偶数的下一个
odd.next = even.next;
//奇数 = 奇数的下一个 也就是第一个偶数的下一个
odd = odd.next;
//偶数下一个 = 第一个偶数的下一个的下一个
even.next = odd.next;
//偶数= 偶数的下一个
even = even.next;
}
//奇数尾下一个是偶数的头节点
odd.next = evenHead;
return head;
}
}