前言
提示:这里为每天自己的学习内容心情总结;
Learn By Doing,Now or Never,Writing is organized thinking.
刷题 and 面经。
先多,后少。
提示:以下是本篇文章正文内容
一、今天学习了什么?
- 贪心算法;
- 数组,代码随想录;
- 链表,代码随想录;
二、关于问题的答案
1、贪心
- 134. 加油站(⭐⭐⭐⭐⭐)
public int canCompleteCircuit(int[] gas, int[] cost) {
/**
* curGas:当前有的油量
* totalGas:全部绕完需要的油量
*/
int curGas = 0;
int totalGas = 0;
int index = 0;// 记录开始顺序绕环路的下标
for (int i = 0; i < gas.length; i++) {
curGas += gas[i] - cost[i];
totalGas += gas[i] - cost[i];
if (curGas < 0) {
// 如果当前剩余的油量为负数,那么就代表失败
curGas = 0;
index = (i + 1) % gas.length;
}
}
// 最后判断
if (totalGas < 0) {
return -1;
}
return index;
}
- 135. 分发糖果(⭐⭐⭐⭐⭐)- 毫无思路
public int candy(int[] ratings) {
/**
* - dp[i],代表第i个下标的孩子,需要的最少糖果数目
* - 贪心策略是,遍历两次
* - 从左朝右遍历时,只要右边的孩子评分比左边孩子大,就需要多给一个
* - 从右朝左遍历时,只要左边的孩子评分比右边孩子大,就需要多给一个
*/
int length = ratings.length;
int[] dp = new int[length];
dp[0] = 1;
for (int i = 1; i < length; i++) {
dp[i] = ratings[i] > ratings[i - 1] ? dp[i - 1] + 1 : 1;
}
for (int i = length - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
dp[i] = Math.max(dp[i], dp[i + 1] + 1);// 很重要
}
}
int sum = 0;
for (int i = 0; i < length; i++) {
sum += dp[i];
}
return sum;
}
public boolean lemonadeChange(int[] bills) {
/**
* - five:当前手上有五美元钞票的张数
* - ten:当前手上有10美元钞票的张数
*/
int five = 0;
int ten = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
five++;
} else if (bills[i] == 10) {
five--;
ten++;
} else {
if (ten > 0) {
ten--;
five--;
} else {
five -= 3;
}
}
if (five < 0 || ten < 0) {
return false;
}
}
return true;
}
public int[][] reconstructQueue(int[][] people) {
/**
* - 首先根据people[i][0]进行降序排序,两个相同时按照第二个元素大小进行升序排序
*/
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0] == 0 ? o1[1] - o2[1] : o2[0] - o1[0];
}
});
/**
* 从左到右遍历数组元素,将元素放入队列中,按照顺序
*/
LinkedList<int[]> list = new LinkedList<>();
for (int[] person : people) {
int index = person[1];
list.add(index, person);
}
return list.toArray(new int[list.size()][]);
}
2、数组
public int search(int[] nums, int target) {
/**
* 二分法,左闭右闭
*/
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (target == nums[mid]) {
return mid;
} else if (target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
public int removeElement(int[] nums, int val) {
/**
* 遍历拷贝
*/
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == val) {
continue;
}
nums[index] = nums[i];
index++;
}
return index;
}
public int[] sortedSquares(int[] nums) {
/**
* 指针
*/
int index = nums.length - 1;
int left = 0, right = index;
int[] ans = new int[nums.length];
while (index >= 0) {
if (left <= right && nums[left] < 0 && -nums[left] > nums[right]) {
ans[index] = nums[left] * nums[left];
left++;
} else {
ans[index] = nums[right] * nums[right];
right--;
}
index--;
}
return ans;
}
public int minSubArrayLen(int target, int[] nums) {
/**
* 滑动窗口?
* - sum:窗口内的元素和
*/
int sum = 0;
int left = -1, ans = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
while (sum >= target && left <= i) {
ans = Math.min(ans, i - left);
sum -= nums[++left];
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
- 59. 螺旋矩阵 II(⭐⭐⭐⭐⭐)
public int[][] generateMatrix(int n) {
/**
* 定义边界
* - loop:控制绕圈的次数
* - i,j:下标
* - begin:每次开始循环的下标(begin,begin)
* - count:填充的数字
*/
int[][] ans = new int[n][n];
int loop = 0;
int i = 0, j = 0;
int begin = 0;
int count = 1;
while (loop++ < n / 2) {
// 左 - 右(上)
for (j = begin; j < n - loop; j++) {
ans[begin][j] = count++;
}
// 上 - 下 (右)
for (i = begin; i < n - loop; i++) {
ans[i][j] = count++;
}
// 右 - 左(下)
for (; j >= loop; j--) {
ans[i][j] = count++;
}
// 下 - 上(左)
for (; i >= loop; i--) {
ans[i][j] = count++;
}
begin++;
}
// 奇数
if (n % 2 != 0) {
ans[begin][begin] = count;
}
return ans;
}
3、链表
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
/**
* pHead:虚拟头节点
* cur : 当前遍历到的节点
* pre : 前一个节点
*/
ListNode pHead = new ListNode(0);
pHead.next = head;
ListNode cur = head, pre = pHead;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return pHead.next;
}
- 707. 设计链表(⭐⭐⭐⭐⭐)
class MyLinkedList {
private Node dummy;// 头指针
private int size;// 链表长度
class Node {// 节点类
int val;
Node next;
public Node() {
}
public Node(int val) {
this.val = val;
}
}
public MyLinkedList() {
size = 0;
dummy = new Node(-1);// 虚拟头节点哈,初始化
}
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
Node cur = dummy.next;
while (index > 0 && cur != null) {
cur = cur.next;
index--;
}
return cur.val;
}
public void addAtHead(int val) {
Node head = dummy.next;
Node node = new Node(val);
dummy.next = node;
node.next = head;
size++;
}
public void addAtTail(int val) {
Node cur = dummy;
while (cur.next != null) {
cur = cur.next;
}
Node node = new Node(val);
cur.next = node;
size++;
}
public void addAtIndex(int index, int val) {
if (index < 0 || index > size) {
return;
}
Node pre = dummy;
Node cur = dummy.next;
while (cur != null && index > 0) {
pre = cur;
cur = cur.next;
index--;
}
Node node = new Node(val);
pre.next = node;
node.next = cur;
size++;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
Node pre = dummy;
Node cur = dummy.next;
while (cur != null && index > 0) {
pre = cur;
cur = cur.next;
index--;
}
pre.next = cur.next;
size--;
}
}
public ListNode reverseList(ListNode head) {
/**
* - 合理安排指针
* - pre 、 cure 、next:指针
*/
ListNode pre = null;
ListNode cur = head;
ListNode next = cur;
// 开始遍历
while (cur != null) {
next = cur.next;// 保存下一个指针
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
- 24. 两两交换链表中的节点(⭐⭐⭐⭐⭐)- 挺难的
public ListNode swapPairs(ListNode head) {
// base
if (head == null || head.next == null) {
return head;
}
/**
* 递归版本
*/
ListNode next = head.next;
ListNode node = swapPairs(next.next);
next.next = head;
head.next = node;
return next;
}
public ListNode swapPairs02(ListNode head) {
// base
if (head == null || head.next == null) {
return head;
}
/**
* 合理安排指针
* - tail:已经交换完的节点的尾节点
* -
*/
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode nextHead = null;
ListNode tail = dummy, cur = dummy;
ListNode first = null;
ListNode second = null;
while (cur.next != null && cur.next.next != null) {
nextHead = cur.next.next.next;// 保存下一个起始的节点
first = cur.next;
second = cur.next.next;
// 开始
second.next = first;
tail.next = second;
first.next = nextHead;
// 改变指针
cur = first;
tail = cur;
}
return dummy.next;
}
public ListNode removeNthFromEnd(ListNode head, int n) {
/**
* 先走一下
*/
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode slow = dummy, fast = dummy;
while (n > 0) {
fast = fast.next;
n--;
}
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummy.next;
}
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
/**
* - 快慢指针
*/
int sizeA = getLength(headA);
int sizeB = getLength(headB);
ListNode big = sizeA >= sizeB ? headA : headB;
ListNode small = big == headA ? headB : headA;
ListNode fast = big, slow = small;
int size = Math.abs(sizeA - sizeB);
while (size > 0) {
fast = fast.next;
size--;
}
while (fast != null) {
if (fast == slow) {
return fast;
}
fast = fast.next;
slow = slow.next;
}
return null;
}
// 获取head的链表长度
int getLength(ListNode head) {
int size = 0;
ListNode cur = head;
while (cur != null) {
size++;
cur = cur.next;
}
return size;
}
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
/**
* 快慢指针
*/
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
4、哈希表
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
/**
* 需要统计每个字母出现的次数
*/
int[] count = new int[26];
for (int i = 0; i < s.length(); i++) {
char cs = s.charAt(i);
count[cs - 'a']++;
char ct = t.charAt(i);
count[ct - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
return false;
}
}
return true;
}
public int[] intersection(int[] nums1, int[] nums2) {
/**
* set?
*/
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < nums1.length; i++) {
set.add(nums1[i]);
}
HashSet<Integer> ans = new HashSet<>();
for (int i = 0; i < nums2.length; i++) {
if (set.contains(nums2[i])) {
ans.add(nums2[i]);
}
}
int[] array = ans.stream().mapToInt(Integer::intValue).toArray();
return array;
}
public boolean isHappy(int n) {
/**
* set
*/
HashSet<Integer> set = new HashSet<>();
while (n != 1 && !set.contains(n)) {
set.add(n);
n = get(n);
}
return n == 1;
}
// 返回n的平方数
int get(int n) {
int res = 0;
while (n != 0) {
int right = n % 10;
res += right * right;
n /= 10;
}
return res;
}
public int[] twoSum(int[] nums, int target) {
/**
* map,key-元素,value-元素下标
*/
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int rest = target - nums[i];
if (map.containsKey(rest)) {
return new int[]{map.get(rest), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
- 454. 四数相加 II(⭐⭐⭐⭐⭐)- 挺难的
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
/**
*- map,key记录和,value记录次数
*/
HashMap<Integer, Integer> map = new HashMap<>();
for (int i1 : nums1) {
for (int i2 : nums2) {
map.put(i1 + i2, map.getOrDefault(i1 + i2, 0) + 1);
}
}
int res = 0;
for (int i3 : nums3) {
for (int i4 : nums4) {
res += map.getOrDefault(0 - i3 - i4, 0);
}
}
return res;
}
public boolean canConstruct(String ransomNote, String magazine) {
/**
* map,key-字符,value-出现的次数
*/
HashMap<Character, Integer> map = new HashMap<>();
for (char c : magazine.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
for (char c : ransomNote.toCharArray()) {
if (!map.containsKey(c)) {
return false;
}
Integer count = map.get(c);
count--;
if (count < 0) {
return false;
}
map.put(c, count);
}
return true;
}
5、字符串
public void reverseString(char[] s) {
/**
* - 原地修改就涉及到交换、采用指针
*/
int left = 0, right = s.length - 1;
while (left < right) {
swap(left, right, s);
left++;
right--;
}
}
void swap(int i, int j, char[] s) {
// 交换的方法
char c = s[i];
s[i] = s[j];
s[j] = c;
}
- 541. 反转字符串 II(⭐⭐⭐⭐⭐)- 挺难的
public String reverseStr(String s, int k) {
/**
*
*/
char[] chars = s.toCharArray();
int index = 0;// 字符串开始翻转的起始位置
while (index < chars.length) {
// 判断剩余字符的个数和k的关系
if (chars.length - index <= k) {
// 将剩余字符全部翻转
int left = index, right = chars.length - 1;
while (left < right) {
swap(left, right, chars);
left++;
right--;
}
} else {
// 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
int left = index, right = index + k - 1;
while (left < right) {
swap(left, right, chars);
left++;
right--;
}
}
index += 2 * k;
}
return String.valueOf(chars);
}
void swap(int i, int j, char[] chars) {
char c = chars[i];
chars[i] = chars[j];
chars[j] = c;
}
public String replaceSpace(String s) {
/**
* append?
*/
StringBuilder sb = new StringBuilder();
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == ' ') {
sb.append("%20");
} else {
sb.append(chars[i]);
}
}
return sb.toString();
}
public String reverseWords(String s) {
/**
* 拼接字符串
*/
String ans = "";
// 开始遍历
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == ' ') {
continue;
}
int left = i, right = left + 1;
while (right < chars.length && chars[right] != ' ') {
right++;
}
ans = s.substring(left, right).concat(" ").concat(ans);
i = right;
}
return ans.substring(0, ans.length() - 1);
}
public String reverseLeftWords(String s, int n) {
if (s == null) {
return s;
}
/**
*
*/
String substring = s.substring(0, n);
return s.substring(n, s.length()).concat(substring);
}
- 28. 找出字符串中第一个匹配项的下标(⭐⭐⭐⭐⭐)- 挺难的,很难
public int strStr(String haystack, String needle) {
/**
* next:前缀表数组
*/
int[] next = getPrefixArray(needle);
// 遍历haystack元素,开始匹配
int j = 0;// 模式串下标-needle
for (int i = 0; i < haystack.length(); i++) {
// 回退,需要连续回退
while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
j = next[j - 1];
}
if (haystack.charAt(i) == needle.charAt(j)) {
j++;
}
if (j == next.length) {
return i - next.length + 1;
}
}
return -1;
}
// 求字符串s的前缀表数组
int[] getPrefixArray(String s) {
int[] next = new int[s.length()];
int j = 0;
next[0] = 0;
for (int i = 1; i < s.length(); i++) {
while (j > 0 && s.charAt(j) != s.charAt(i))
j = next[j - 1];
if (s.charAt(j) == s.charAt(i)) {
j++;
}
next[i] = j;
}
return next;
}
- 459. 重复的子字符串(⭐⭐⭐⭐⭐)- 挺难的,很难
public boolean repeatedSubstringPattern(String s) {
/**
* 本体为 KMP 的变式,可以看作将两个字符串s拼接后是否能凑出一个s
*/
int[] next = getNext(s);
int len = s.length();
// 为什么?
/**
* 假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。
* 因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,(这里如果不懂,看上面的推理)
*
* 所以如果 nx % (n - m)x = 0,就可以判定有重复出现的子字符串。
*/
if (next[len - 1] > 0 && len % (len - next[len - 1]) == 0) {
return true;
}
return false;
}
int[] getNext(String s) {
int[] next = new int[s.length()];
int j = 0;// 维护下标
for (int i = 1; i < s.length(); i++) {
while (j > 0 && s.charAt(i) != s.charAt(j)) {
// 需要考虑连续回退
j = next[j - 1];
}
if (s.charAt(i) == s.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
总结
提示:这里对文章进行总结:
这几天在刷题