数据结构
数组
2021.1.1 – 2021.1.8(后几道不算数组,待定)
int[ ] :排序:Arrays.sort(); //由小到大排序
。
由大到小排序:①将数组从后往前读。
②用Arrays.sort(c,Collections.reverseOrder());
ArrayList:Collections.sort(arr);//由小到大
Collections.sort(arr,Collections.reverseOrder());//由大到小
从排序方法可以看出ArrayList实现List接口,List 实现了Colection 接口
485. 最大连续1的个数
问题
思路
①for循环,遇到1计数,遇到0就更新。
public int findMaxConsecutiveOnes(int[] nums) {
int len = nums.length;
int maxi = 0;
int count=0;
for (int i = 0; i<len; i++){
if(nums[i]==1){
maxi++;
}
if(nums[i]==0 || i == len-1){
count = Math.max(maxi,count);
maxi = 0;
}
}
return Math.max(maxi,count);
}
②以0为分割点,更新两个0之间的距离
public int findMaxConsecutiveOnes(int[] nums) {
int len = nums.length;
int prev = -1;
int count=0;
if(nums == null || len == 0){
return 0;
}
for (int i = 0; i<len; i++){
if(nums[i]==0){
count = Math.max(i-prev-1,count);
prev = i;
}
}
//return count;
return Math.max(len-prev-1,count);
}
283. 移动零
问题
思路
①非0元素往前移
public void moveZeroes(int[] nums) {
int len = nums.length;
int index =0;
for (int i = 0; i < len;i++){
if(nums[i] != 0){
nums[index] = nums[i];
index++;
}
}
for (int i=index;i<len;i++){
nums[i] = 0;
}
}
②双指针 快排思想:左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
public void moveZeroes(int[] nums) {
int i=0,j=0;
int len = nums.length;
while(j<len){
if(nums[j]!=0){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
}
j++;
}
}
27. 移出元素
问题
思路
①双指针,一个从前找
要删除的元素,一个从后找非指定元素,两者都找到后将后元素赋值给前元素并计数,循环停止条件两个指针相撞
public int removeElement(int[] nums, int val) {
//双指针
if(nums.length==0 || nums==null){
return 0;
}
int l = 0, r = nums.length-1;
while(l<r){
if(nums[l]==val && nums[r] != val){
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
}
else if(nums[l]!=val){
l++;
}else if(nums[r] == val){
r--;
}
}
if(nums[l]==val){
return l;
}else{
return l+1;
}
}
非删除元素前移
public int removeElement(int[] nums, int val) {
int loc = 0;
for(int i = 0; i < nums.length;i++){
if(nums[i]!=val){
nums[loc++] = nums[i];
}
}
return loc;
}
566. 改变矩阵维度
问题
思路
1,双重循环遍历(复制到新数组时,直接思路是对原数组计数,每次到行尾时换行。简单优化是利用除数和余数)
public int[][] matrixReshape(int[][] nums, int r, int c) {
if(nums==null || nums.length==0){
return nums;
}
int row = nums.length;
int col = nums[0].length;
if(r*c != row*col){
return nums;
}
int p=0,q=0;//优化:int index = 0;
int[][] res= new int[r][c];
for(int i = 0; i<r; i++){
for(int j = 0; j<c;j++){
res[i][j] = nums[j/col][j%col]; //优化:res[i][j] = nums[index/col][index%col];
if(q==col-1){ //优化:index++;
p++; //优化:p,q有关都删除
q = 0;
}else{
q++;
}
}
}
return res;
}
3 使用队列(大可不必)
240. 有序矩阵查找
问题
思路
1.矩阵是排好序的,考虑二分法。对每一行进行二分.
public boolean searchMatrix(int[][] matrix, int target) {
int r = matrix.length;
int c = matrix[0].length;
int row = r-1,col = 0;
for(int i = 0;i<r;i++){
int left = 0,right = c-1;
if(matrix[i][0]>target){
return false;
}
while(left<=right){
int mid = left+(right-left)/2;
if(matrix[i][mid]==target){
return true;
}else if(matrix[i][mid]>target){
right = mid-1;
}else{
left = mid+1;
}
}
}
return false;
}
2.区域递归。看矩阵图,根据中心点,将矩阵分割成4个,和目标元素对比,舍弃掉不符合的那个子矩阵(代码编写较麻烦,还没仔细看)。
//对二分搜索变形
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) {
return false;
}
return searchMatrixHelper(matrix, 0, 0, matrix[0].length - 1, matrix.length - 1, matrix[0].length - 1, matrix.length - 1, target);
}
private boolean searchMatrixHelper(int[][] matrix, int x1, int y1, int x2, int y2, int xMax, int yMax, int target) {
//只需要判断左上角坐标即可
if (x1 > xMax || y1 > yMax) {
return false;
}
//x 轴代表的是列,y 轴代表的是行
if(x1 == x2 && y1 == y2){
return matrix[y1][x1] == target;
}
int m1 = (x1 + x2) >>> 1;
int m2 = (y1 + y2) >>> 1;
if (matrix[m2][m1] == target) {
return true;
}
if (matrix[m2][m1] < target) {
// 右上矩阵
return searchMatrixHelper(matrix, m1 + 1, y1, x2, m2, x2, y2, target) ||
// 左下矩阵
searchMatrixHelper(matrix, x1, m2 + 1, m1, y2, x2, y2, target) ||
// 右下矩阵
searchMatrixHelper(matrix, m1 + 1, m2 + 1, x2, y2, x2, y2, target);
} else {
// 右上矩阵
return searchMatrixHelper(matrix, m1 + 1, y1, x2, m2, x2, y2, target) ||
// 左下矩阵
searchMatrixHelper(matrix, x1, m2 + 1, m1, y2, x2, y2, target) ||
// 左上矩阵
searchMatrixHelper(matrix, x1, y1, m1, m2, x2, y2, target);
}
}
作者:windliang
链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-5-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
从左下角出发或从右上角出发,和目标数字比较走行或列
public boolean searchMatrix(int[][] matrix, int target) {
int r = matrix.length;
int c = matrix[0].length;
int row = r-1,col = 0;
while(row >= 0 && col < c){
if(matrix[row][col] == target){
return true;
}else if(matrix[row][col] > target){
row--;
}else{
col++;
}
}
return false;
}
378. 有序矩阵的 Kth Element *
问题
思路
1 暴力:将二维数组放到一维数组中,由小到大排序,输出第k-1个元素。(没有利用原矩阵的性质)
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int[] nm = new int[n*n];
int q = 0;
for(int i = 0; i< n;i++){
for(int j = 0;j<n;j++){
nm[q++] = matrix[i][j];
}
}
Arrays.sort(nm);
return nm[k-1];
}
3 二分查找,第k个可以转化成计算有k个不大于mid的数,不大于mid 的数分布在mid的左上方,如图(图来自leetcode官方题解)。
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int l = matrix[0][0];
int r = matrix[n-1][n-1];
int mid = l+(r-l)/2;
int count = 0;
while(l<r) {
mid = l+(r-l)/2;
//计算不大于mid的个数
int row = n-1;
int col = 0;
while(row>=0 && col <n) {
if(matrix[row][col]<=mid) {
count += row+1;//把当前列加进来
col++;
}else {
row--;
}
}
if(count >= k) {
r = mid;
}else {
l = mid+1;
}
count = 0;
}
return l;//返回l而不是mid
}
2 归并排序 问题即转化为从这 n 个有序数组中找第 k 大的数 (练到排序再补)
645. Set Mismatch
问题
思路
排序,丢失元素可以通过比较相邻元素差值是否大于1来获取,注意数组的结尾
public int[] findErrorNums(int[] nums) {
int n = nums.length;
int[] res = new int[2];
Arrays.sort(nums);
res[0] =-1;
res[1] = 1;
for(int i = 1;i<n;i++){
if(nums[i]==nums[i-1]) {
res[0] = nums[i];
}
else if(nums[i]>nums[i-1]+1) {
res[1] = nums[i-1]+1;
}
}
if(nums[n-1]!= n){
res[1] = n;
}
return res;
}
使用map/额外数组,新数组所以为nums的元素,值为该元素出现次数
public int[] findErrorNums(int[] nums) {
int[] s = new int[nums.length+1];
int rep = -1;
int los = -1;
for(int i = 0; i<nums.length; i++){
s[nums[i]]++;
}
for(int i = 1; i<=nums.length;i++){
if(s[i]==0){
los = i;
continue;
}else if(s[i]==2){
rep = i;
continue;
}
if(los!=-1 && rep!=-1){
break;
}
}
return new int[]{rep,los};
}
287. 找出数组中重复的数
问题
667. 数组相邻差值的个数
问题
697. 数组的度
问题
思路
用三个map,刚开始用了数组,试图一次循环完成,略绕,思路不清晰
public int findShortestSubArray(int[] nums) {
Map<Integer, Integer> count = new HashMap<>();
Map<Integer, Integer> left = new HashMap<>();
Map<Integer, Integer> right = new HashMap<>();
int maxi = 0, maxnum = 0, mini = 50003;
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
count.put(num, count.getOrDefault(num, 0) + 1);
right.put(num, i);
if (!left.containsKey(num)) {
left.put(num, i);
}
}
int res = nums.length;
maxi = Collections.max(count.values());
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
int cnt = count.get(num);
if(cnt != maxi) continue;
res = Math.min(res, right.get(num)-left.get(num)+1);
}
return res;
}
766. 对角元素相等的矩阵
问题
链表
2020.11.26 – 2020.12.31
LinkedList 。
160. 相交链表
问题
思路
①双指针:两条链表的长度a,b,相交后为c, a+b+c=b+a+c),若无交点,则c为0.循环A链表,走到尾就去走B链表,B则反之,若有交点两者会在交点相遇,若无在null相遇。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode l1 = headA, l2 = headB;
while(l1 != l2){
l1 = (l1 == null)? headB:l1.next;
l2 = (l2 == null)? headA:l2.next;
}
return l1;
}
}
暴力思路:新建节点,循环A链表,循环B链表,找交点
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode l1 = headA, l2 = headB;
while(l1 != null){
while(l2 != null){
if(l1 == l2){
return l1;
}
l2 = l2.next;
}
l2 = headB;
l1 = l1.next;
}
return null;
}
}
206. 链表反转
问题
思路
画图
①直接反转节点。设置3个节点,前,后,当前节点(head)。设置dummy节点指向head,从head开始保存下一个节点,将当前节点指向前节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public ListNode reverseList(ListNode head) {
ListNode dummy =null;
while(head != null){
ListNode next = head.next;
head.next = dummy;
dummy = head;
head = next;
}
return dummy;
}
② 设置新节点作为反转后新链表的头(l节点),从head开始,保存它的下一个节点,将当前节点加入到新链表的头(l.next)。
public ListNode reverseList(ListNode head) {
ListNode l = new ListNode(-1);
while(head!=null) {
ListNode next = head.next;
head.next = l.next;
l.next = head;
head = next;
}
return l.next;
}
③递归(所有迭代都能写成递归的形式,反之亦然)。每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。同时让当前结点的 next 指针指向 NULL ,从而实现从链表尾部开始的局部反转
public ListNode reverseList(ListNode head) {
if(head == null || head.next==null) {
return head;
}
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
21. 归并两个有序的链表
问题
思路
①双指针:设置新节点为合并后的头结点,设双指针分别指向两个链表,哪个节点的值小,将该节点加入新链表,并走这个指针。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null && l2 == null){
return null;
}
ListNode res = new ListNode();
ListNode h = res;
while(l1!=null && l2!=null){
if(l1.val <= l2.val){
res.next = l1;
l1 = l1.next;
}else{
res.next = l2;
l2 = l2.next;
}
res = res.next;
}
res.next = l1==null?l2:l1;
return h.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;
}
}
83. 从有序链表中删除重复节点
问题
思路
排序链表,重复元素肯定相邻。当前节点的值和下个节点的值对比并移动指针
① 迭代
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public ListNode deleteDuplicates(ListNode head) {
if(head==null){
return null;
}
ListNode res = head;
while(head!=null && head.next!=null){
if(head.val == head.next.val){
head.next = head.next.next;
}else{
head = head.next;
}
}
return res;
}
递归
public ListNode deleteDuplicates(ListNode head) {
if(head==null || head.next == null){
return head;
}
head.next = deleteDuplicates(head.next);
return head.val == head.next.val?head.next:head;
}
19. 删除链表的倒数第 n 个节点
问题
思路
直接思路:先遍历链表求长度,再遍历链表找到要删除的链表,改变指针删除该节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
int count = 0;
ListNode res = new ListNode(0,head);
int loc = 0;
ListNode a = res;
while(head != null){
head = head.next;
count ++;
}
for (int i = 0; i < count-n; i++) {
a = a.next;
}
a.next = a.next.next;
return res.next;
}
双指针:边遍历边找需要删除的节点。快慢指针,快慢指针的差距为n,当快指针到尾时,慢指针刚好到要删除的前一个节点。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head;
ListNode low = head;
while(n-->0){
fast = fast.next;
}
if(fast == null){
return head.next;
}
while(fast.next!=null){
low = low.next;
fast = fast.next;
}
low.next = low.next.next;
return head;
}
24. 交换链表中的相邻结点
问题
思路
两两节点交换涉及到三个指针,前,后,当前
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode i = head;
ListNode j = head.next;
ListNode dummy = new ListNode(-1,head);
ListNode pre = dummy;
while(i != null && i.next != null){
j = i.next;
i.next = j.next;
j.next = i;
pre.next = j;
pre = i;
i = i.next;
}
return dummy.next;
}
445. 链表求和
问题
思路
算术运算一般用栈。两个栈运算后,余数作为要插入新链表的节点,插入到头结点之后,涉及到头结点和第二个节点
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> s1 = new Stack<Integer>();
Stack<Integer> s2 = new Stack<Integer>();
ListNode head = new ListNode(-1);
while(l1!=null) {
s1.add(l1.val);
l1 = l1.next;
}
while(l2!=null) {
s2.add(l2.val);
l2 = l2.next;
}
int res = 0;
while(!s1.isEmpty() || !s2.isEmpty()||res!=0) {
int a = s1.isEmpty()?0:s1.pop();
int b = s2.isEmpty()?0:s2.pop();
int sum = res+a+b;
ListNode node = new ListNode(sum%10);
node.next = head.next;
head.next = node;
res = sum/10;
}
return head.next;
}
234. 回文链表
问题
思路
① 利用数据结构,头尾比较
public boolean isPalindrome(ListNode head) {
LinkedList<Integer> node = new LinkedList<Integer>();//放数组也可以
int i = 0;
while(head!=null) {
node.add(i++, head.val);
head = head.next;
}
int n = node.size();
System.out.println(n);
for (int j = 0; j < n/2; j++) {
if(!node.get(j).equals(node.get(n-j-1))) {
return false;
}
}
return true;
}
② 双指针,快慢指针,慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。
public static boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) {
return true;
}
ListNode slow = head, fast = head;
ListNode pre = null;
while(fast != null && fast.next != null) {
fast = fast.next.next;
ListNode temp = slow.next;
slow.next = pre;
pre = slow;
slow = temp;
}
if(fast != null) {
slow = slow.next;
}
while(pre != null && slow != null) {
if(pre.val != slow.val) {
return false;
}
pre = pre.next;
slow = slow.next;
}
return true;
}
递归:使用递归反向迭代节点,同时使用递归函数外的变量向前迭代
class Solution {
private ListNode frontPointer;
private boolean recursivelyCheck(ListNode currentNode) {
if (currentNode != null) {
if (!recursivelyCheck(currentNode.next)) {
return false;
}
if (currentNode.val != frontPointer.val) {
return false;
}
frontPointer = frontPointer.next;
}
return true;
}
public boolean isPalindrome(ListNode head) {
frontPointer = head;
return recursivelyCheck(head);
}
}
725. 分割链表
问题
思路
使用余数和除数,因为任意两部分的长度差距不超过1,所以给前余数个部分的长度应该是除数+1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public ListNode[] splitListToParts(ListNode root, int k) {
ListNode[] res = new ListNode[k];
ListNode head = root;
int len = 0;
while (root != null) {
root = root.next;
len++;
}
int i = 0;
int num = len / k;
int first = len % k;
for (int j = 0; head!=null &&j < k; j++) {
res[j] = head;
int cursize = num+(first-->0 ? 1:0);
for (int l = 0; l < cursize-1; l++) {
head = head.next;
}
ListNode newhead = head.next;
head.next = null;
head = newhead;
}
return res;
}
328. 链表元素按奇偶聚集
问题
思路
双指针:分成奇数和偶数链,各自走各自的,最后结合
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public ListNode oddEvenList(ListNode head) {
if(head==null || head.next==null || head.next.next == null) {
return head;
}
ListNode odd = head;//当前奇数节点
ListNode even = head.next;//偶数头结点
ListNode neweven = even;//偶数尾节点
while(odd != null&& even!=null && odd.next!=null && even.next!=null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = neweven;
return head;
}
直接思路:在原链表移动。把奇数节点移动到前面,每次移动保持原链表不断
public ListNode oddEvenList(ListNode head) {
if(head==null || head.next==null || head.next.next == null) {
return head;
}
ListNode cur = head;//当前奇数节点
ListNode tou = head.next;//偶数头结点
ListNode wei = head.next;//偶数尾节点
while(wei != null&& wei.next!=null) {
ListNode no = wei.next;
cur.next = no;
wei.next = wei.next.next;
no.next = tou;
cur = cur.next;
wei = wei.next;
}
return head;
}
203. 移除链表元素
问题
思路
遍历链表,需要保存当前节点(删除的节点)的前一个节点,找到后修改指针继续找直到链表结束
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public ListNode removeElements(ListNode head, int val) {
ListNode prev = new ListNode(0);
prev.next = head;
ListNode now = head;
ListNode pre = prev;
while(now!=null) {
if(now.val == val) {
pre.next = now.next;
}else {
pre = now;
}
now = now.next;
}
return prev.next;
}
栈和队列
2021.1.09 – 1.12
Stack Queue。
933. 最近的请求次数
问题
思路
用队列,加上当前时间的请求,去除小于t-3000的请求,返回队列长度
class RecentCounter {
Queue<Integer> q;
public RecentCounter() {
q = new LinkedList<Integer>();
}
public int ping(int t) {
q.add(t);
while(q.size() > 0 && q.peek()<(t-3000)) {
q.remove();
}
return q.size();
}
}
/**
* Your RecentCounter object will be instantiated and called as such:
* RecentCounter obj = new RecentCounter();
* int param_1 = obj.ping(t);
*/
20. 有效的括号
问题
思路
匹配问题用栈,左括号先压入栈,遇到右括号弹出,看是否匹配
public boolean isValid(String s) {
if (s.length() == 0) {
return true;
}
char[] ca = s.toCharArray();
Stack<Character> stack = new Stack<Character>();
int i;
for (i = 0; i < ca.length; i++) {
if (ca[i] == '(' || ca[i] == '[' || ca[i] == '{') {
stack.add(ca[i]);
} else {
if(stack.isEmpty()) {
return false;
}else {
char temp = stack.pop();
if (ca[i] == ')') {
if (temp != '(') {
return false;
}
} else if (ca[i] == ']') {
if (temp != '[') {
return false;
}
} else if (ca[i] == '}') {
if (temp != '{') {
return false;
}
}
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
循环看字符串中是否有完整括号,将其替换为空,循环结束看字符串是否为空,(代码简洁,更耗时)
class Solution {
public boolean isValid(String s) {
while(s.contains("()") || s.contains("[]") || s.contains("{}")){
if(s.contains("()")){
s=s.replace("()","");
}
if(s.contains("{}")){
s=s.replace("{}","");
}
if(s.contains("[]")){
s=s.replace("[]","");
}
}
return s.length()==0;
}
}
496. 下一个更大元素 I
问题
思路
1.将nums2放到栈中,利用栈后进先出,循环nums1,弹出栈顶元素判断与nums1[i]的关系,循环一次后要恢复栈。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Stack<Integer> stack = new Stack<>();
int[] maxi = new int[nums1.length];
for (int i = 0; i < nums2.length; i++) {
stack.push(nums2[i]);
}
for (int i = 0; i < nums1.length; i++) {
Stack<Integer> temp = new Stack<>();
boolean isTrue = false;
int max = -1;
while(!stack.isEmpty()&&!isTrue) {
int num = stack.pop();
if(num > nums1[i]) {
max = num;
}else if(num == nums1[i]) {
isTrue = true;
}
temp.push(num);
}
maxi[i] = max;
while(!temp.isEmpty()){
stack.push(temp.pop());
}
}
return maxi;
}
}
2.利用HashMap,放nums2的值和索引,循环nums1,找到当前值在nums2中的位置,往后对比找到最近的较大值。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
HashMap<Integer,Integer> map = new HashMap<>();
int[] maxi = new int[nums1.length];
for (int i = 0; i < nums2.length; i++) {
map.put(nums2[i],i);
}
for (int i = 0; i < nums1.length; i++) {
int k = map.get(nums1[i]);
int max = -1;
for(int q = k+1; q<nums2.length; q++){
if(nums2[q] > nums1[i]){
max = nums2[q];
break;
}
}
maxi[i] = max;
}
return maxi;
}
}
232. 用栈实现队列
问题
思路
两个栈,一个存数组用来入队,另一个对栈反转用来出队,当作队列。
class MyQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
/** Initialize your data structure here. */
public MyQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(stack2.isEmpty()){
while (!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
int peek = stack2.pop();
return peek;
}
/** Get the front element. */
public int peek() {
if(stack2.isEmpty()){
while (!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return stack2.isEmpty()&&stack1.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
225. 用队列实现栈
问题
用一个队列,入栈时先把现有队列反转(出队再入队),再加入当前元素
class MyStack {
private Queue<Integer> q;
/** Initialize your data structure here. */
public MyStack() {
q = new LinkedList<>()
}
/** Push element x onto stack. */
public void push(int x) {
q.add(x);
int count = q.size();
while (count-->1){
q.add(q.poll());
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return q.poll();
}
/** Get the top element. */
public int top() {
return q.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return q.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
155. 最小栈
问题
1.用两个栈,一个存数,一个存当前的最小值
class MinStack {
//用两个栈
private Stack<Integer> data = new Stack<>();
private Stack<Integer> mins = new Stack<>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
data.push(x);
if(mins.isEmpty()){
mins.push(x);
}else{
mins.push(Math.min(x,mins.peek()));
}
}
public void pop() {
data.pop();
mins.pop();
}
public int top() {
return data.peek();
}
public int getMin() {
return mins.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
2.用数组栈,存当前值和当前最小值
class MinStack {
//想到了用数组,没想到数组栈
private Stack<int[]> s;
/** initialize your data structure here. */
public MinStack() {
s = new Stack<>();
}
public void push(int x) {
if(s.isEmpty()){
s.push(new int[]{x,x});
}else{
s.push(new int[]{x,Math.min(x,s.peek()[1])});
}
}
public void pop() {
s.pop();
}
public int top() {
return s.peek()[0];
}
public int getMin() {
return s.peek()[1];
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
739. 每日温度
问题
思路
用栈,存下标,比较当前元素和栈顶元素,他们的索引差就是需要输出的值
class Solution {
public int[] dailyTemperatures(int[] T) {
//想到栈,没想到可以存下标
Stack<Integer> stack = new Stack<>();
int[] res = new int[T.length];
int q = 0;
for(int i = 0;i<T.length;i++){
while(!stack.isEmpty()&&T[stack.peek()]<T[i]){
int index = stack.pop();
res[index] = i-index;
}
stack.push(i);
}
return res;
}
}
503. 下一个更大元素 II
问题
思路
循环数组,所以要入栈两次,第一次返回的值存入结果
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
int[] next = new int[n];
Arrays.fill(next, -1);
Stack<Integer> pre = new Stack<>();
for (int i = 0; i < n * 2; i++) {
int num = nums[i % n];
while (!pre.isEmpty() && nums[pre.peek()] < num) {
next[pre.pop()] = num;
}
if (i < n){
pre.push(i);
}
}
return next;
}
哈希
2021.1.14 – 1.16
HasMap, HashSet。
217. 存在重复元素
问题
思路
1.HashMap
class Solution {
public boolean containsDuplicate(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
if(nums.length==0||nums==null) {
return false;
}
for (int i=0; i<nums.length;i++) {
if(map.containsKey(nums[i])) {
return true;
}
map.put(nums[i], i);
}
return false;
}
}
2.设为-1
3 HashSet去重
public boolean containsDuplicate(int[] nums) {
HashSet<Integer> set = new HashSet<>();
int len = nums.length;
if(nums.length==0||nums==null) {
return false;
}
for (int i=0; i<len;i++) {
set.add(nums[i]);
}
return !(set.size()==len);
}
389. 找不同
问题
思路
1.位运算,异或。拼接两个字符串,问题转换成求字符串中出现奇数次的字符
class Solution {
public char findTheDifference(String s, String t) {
if (s.length() == 0) {
return t.toCharArray()[0];
}
char res = t.charAt(t.length()-1);
for (int i = 0; i < s.length(); i++) {
res ^= s.charAt(i);
res ^= t.charAt(i);
}
return res;
}
}
2.循环两个数组,字符为下标,hts为HashTable
public char findTheDifference(String s, String t) {
if (s.length() == 0) {
return t.toCharArray()[0];
}
int[] hts = new int[26];
char res = 0;
for (int i = 0; i < t.length(); i++) {
hts[t.charAt(i)- 97] ++;
}
for (int i = 0; i < s.length(); i++) {
hts[s.charAt(i)-97] --;
}
for (int i = 0; i < hts.length; i++) {
if(hts[i] == 1) {
res = (char) (i+97);
}
}
return res;
}
496. 下一个更大元素 I
问题
思路
1.HashMap,记录数组中的元素和位置
//执行用时: 4 ms
//内存消耗: 38.9 MB
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
HashMap<Integer,Integer> map = new HashMap<>();
int[] maxi = new int[nums1.length];
for (int i = 0; i < nums2.length; i++) {
map.put(nums2[i],i);
}
for (int i = 0; i < nums1.length; i++) {
int k = map.get(nums1[i]);
int max = -1;
for(int q = k+1; q<nums2.length; q++){
if(nums2[q] > nums1[i]){
max = nums2[q];
break;
}
}
maxi[i] = max;
}
return maxi;
}
}
2.单调栈
//执行用时: 5 ms
//内存消耗: 38.4 MB
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
HashMap<Integer,Integer> map = new HashMap<>();
int[] res = new int[nums1.length];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i<nums2.length; i++){
while(stack.size()!=0 && (int) stack.peek()<nums2[i]){
int temp = stack.pop();
map.put(temp, nums2[i]);
}
stack.add(nums2[i]);
}
while(stack.size()!=0) {
int temp = stack.pop();
map.put(temp, -1);
}
for (int i = 0; i < nums1.length; i++) {
res[i] = map.get(nums1[i]);
}
return res;
}
}
1. 两数之和
问题
思路
1 暴力:双重循环一次数组,外循环为
num[i]
时,在内循环找是否存在target-num[i]
,返回对应下标。
2 用HashMap 做数组元素和下标的映射,这样可以利用containsKey
方法,省略内循环
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
return new int[0];
}
}
594. 最长和谐子序列
问题
思路
用HashMap存元素和对应次数,循环每一个x,找是否存在x+1,计算他们的长度,更新最大值 (
foreach
还不大手写,循环map时是集合,要用keySet)
class Solution {
public int findLHS(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int num:nums) {
map.put(num,map.getOrDefault(num,0)+1);
}
int maxi = 0;
for (int num:map.keySet()) {
if(map.containsKey(num+1)){
maxi = Math.max(maxi,map.get(num+1)+map.get(num));
}
}
return maxi;
}
}
2 哈希映射 + 单次扫描,扫描一次数组,当扫描到元素 x 时,首先将 x 加入哈希映射,随后获取哈希映射中 x - 1, x, x + 1 三者出现的次数(来自官方题解)
public class Solution {
public int findLHS(int[] nums) {
HashMap < Integer, Integer > map = new HashMap < > ();
int res = 0;
for (int num: nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
if (map.containsKey(num + 1))
res = Math.max(res, map.get(num) + map.get(num + 1));
if (map.containsKey(num - 1))
res = Math.max(res, map.get(num) + map.get(num - 1));
}
return res;
}
}
128. 最长连续序列
问题
思路
- 数组排序 ,再循环数组,判断是否连续 (类似双指针),计算长度,更新最大值
//执行用时: 2 ms O(nlog(n))
//内存消耗: 38.7 MB
class Solution {
public int longestConsecutive(int[] nums) {
if(nums.length == 0)
return 0;
Arrays.sort(nums);
int max=1,curr=1,last=nums[0];
for (int i = 1; i < nums.length; i++) {
if(nums[i]==last)
continue;
if(nums[i] == last+1)
curr++;
else {
max=Math.max(max, curr);
curr=1;
}
last = nums[i];
}
max = Math.max(max, curr);
return max;
}
}
2 HashMap 。遍历一遍数组,看是否存在
nums[i]+1
,这样查看一个数是否存在就优化成了O(1),但总的还是 O(n^2)(即外层需要枚举 O(n)O(n) 个数,内层需要暴力匹配 O(n)O(n) 次)
- 再优化涉及动归,之后再更
705. 存在重复元素
问题
思路
用数组,如果用常规的
int[]
,删除操作时时间复杂度和HashSet不符,注意到返回结果都是true/false,选用boolean类型的数组,这只是简单实现,
- 考虑实际使用和底层源码实现HashSet
数据结构,有两个关键的问题,即哈希函数和冲突处理,要使用链表或树来设计HashSet,以后更
class MyHashSet {
boolean[] hashset = null;
/** Initialize your data structure here. */
public MyHashSet() {
hashset = new boolean[1000001];
}
public void add(int key) {
hashset[key] = true;
}
public void remove(int key) {
hashset[key] = false;
}
/** Returns true if this set contains the specified element */
public boolean contains(int key) {
return hashset[key];
}
}
/**
* Your MyHashSet object will be instantiated and called as such:
* MyHashSet obj = new MyHashSet();
* obj.add(key);
* obj.remove(key);
* boolean param_3 = obj.contains(key);
*/
字符串
2021.1.16 – 1.18
HasMap, HashSet。
242. 有效的字母异位词
问题
思路
排序后的s和t应该相等
//执行用时: 5 ms
//内存消耗: 38.5 MB
public boolean isAnagram(String s, String t) {
char[] cs = s.toCharArray();
char[] ct = t.toCharArray();
Arrays.sort(cs);
Arrays.sort(ct);
return Arrays.equals(cs,ct);//比较数组相等的方法
}
s和t的元素种类和数量应该一样,可以用数组表示元素和数量
class Solution {
public boolean isAnagram(String s, String t) {
//用Map,输入数据太多时解答失败
//需要利用字母有26个的特点
//String使用charAt比使用toCharArray遍历,效率要高。
//避免在for循环中使用s.length()方法,可以显著提升效率。
//虽然底层都调用了C语言的Native方法,toCharArray多了复制数组的一个步骤,所以会慢,因为String的数据结构本来也是数组。
if(s.length()!=t.length()){
return false;
}
char[] res = new char[26];
int len = s.length();
for (int i = 0; i< len; i++){
res[s.charAt(i)-'a']++;
res[t.charAt(i)-'a']--;
}
for (int i = 0;i<res.length;i++){
if(res[i]!=0){
return false;
}
}
return true;
}
}
409. 最长回文串
问题
思路
回文串的特点,中心元素可以是奇数次,其他都必须是偶数次; 包含大小写字母,z(122)和A(65)差为58,
class Solution {
public int longestPalindrome(String s) {
int len = s.length();
int res = 0;
boolean flag = false;
int[] num = new int[58];
for(int i = 0;i<len;i++){
num[s.charAt(i)-'A']++;
}
for(int i = 0;i<num.length;i++){
if(num[i]!=0){
if(num[i]%2==1){
flag = true;
}
res += num[i]-num[i]%2;
}
}
return flag?res+1:res;
}
}
205. 同构字符串
问题
思路
根据题意,s和t元素应该是一一对应的,可以用数组或map来表示映射关系,如果数组中的元素还没有映射关系,为它建立映射关系,如果有,看他的映射关系是否和之前对应
class Solution {
public boolean isIsomorphic(String s, String t) {
int[] ns = new int[128];
int[] nt = new int[128];
int len = s.length();
for(int i = 0;i<len;i++){
if(ns[s.charAt(i)] == 0 && nt[t.charAt(i)]==0){
ns[s.charAt(i)] = t.charAt(i);
nt[t.charAt(i)] = s.charAt(i);
}else{
if(ns[s.charAt(i)] != t.charAt(i) && nt[t.charAt(i)]!=s.charAt(i))
return false;
}
}
return true;
}
}
647. 回文子串
问题
思路
双指针,当前元素为回文串中心,左右指针同时移动并判断,要分字符串长度是奇数(中心为一个元素)和偶数(中心为两个元素)
class Solution {
private int cnt = 0;
public int countSubstrings(String s) {
for (int i = 0; i < s.length(); i++) {
extendSubstrings(s, i, i); // 奇数长度
extendSubstrings(s, i, i + 1); // 偶数长度
}
return cnt;
}
private void extendSubstrings(String s, int start, int end) {
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
start--;
end++;
cnt++;
}
}
}
可以把奇数和偶数情况统一起来,这样需要循环2*n-1次(演示)
class Solution {
public int countSubstrings(String s) {
int ans = 0;
int len = s.length();
for (int center = 0; center < 2*len-1; center++) {
int left = center/2;
int right = left+center%2;
while (left>=0 && right<len && s.charAt(left)==s.charAt(right)){
ans++;
left--;
right++;
}
}
return ans;
}
}
9.回文数
问题
思路
双指针,一个在头,一个在尾。
class Solution {
public boolean isPalindrome(int x) {
if((x%10==0 && x!=0) ||(x<0)) {
return false;
}
String y = Integer.toString(x);
char[] re = y.toCharArray();
int len = re.length;
for (int i = 0; i <(len+1)/2; i++) {
if(re[i] != re[len-1-i]) {
return false;
}
}
return true;
}
}
反转一半的数组,和另一半比较。字符串的反转可以
StringBuilder(x).reverse()
///简单粗暴,看看就行
class Solution {
public boolean isPalindrome(int x) {
String reversedStr = (new StringBuilder(x + "")).reverse().toString();
return (x + "").equals(reversedStr);
}
}
进阶:反转一半的数字,因为反转整个数字可能会出现溢出,大于int.MAX
class Solution {
public boolean isPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
}
696. 计数二进制子串
问题
思路
计算每一段0和每一段1的次数放入数组,取数组中相邻元素的最小值相加
class Solution {
public int countBinarySubstrings(String s) {
int n = s.length();
int ans = 0;
int[] count= new int[n];
Arrays.fill(count,1);
int q = 0;
for(int i = 0;i<n-1;i++){
if(s.charAt(i)==s.charAt(i+1)){
count[q]++;
}else{
q++;
}
}
for(int i = 0;i<q;i++){
ans += Math.min(count[i],count[i+1]);
}
return ans;
}
//使用一次循环,用last记录相邻元素的上一个,降低内存消耗
public int countBinarySubstrings(String s) {
int n = s.length();
int ans = 0;
int count= 1,last = 0;
for(int i = 0;i<n-1;i++){
if(s.charAt(i)==s.charAt(i+1)){
count++;
}else{
ans += Math.min(last,count);
last = count;
count = 1;
}
}
return ans+Math.min(last,count);//最后一个元素在循环中没有加上次数
}
}
树
参考
[1]: https://www.bilibili.com/video/BV1sy4y1q79M
[2]:https://github.com/CyC2018/CSNotes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md