数组和链表

查找旋转数组

思路: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;
  }

合并K个排序链表

思路: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;
    }
}

K 个一组翻转链表

思路: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;

    }
}

删除链表的倒数第N个节点

思路: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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值