leetcode 分类刷题----数据结构

这篇博客主要介绍了LeetCode中与数据结构相关的题目,包括数组、链表、栈和队列、哈希表和字符串等多个方面。作者通过详细讲解每个问题和思路,帮助读者理解和掌握各种数据结构的运用。例如,数组题目中涉及的最大连续1的个数、移动零等问题,链表题目如相交链表、链表反转等,以及栈和队列的题目,如有效的括号、用栈实现队列等。此外,还探讨了哈希表在解决存在重复元素、两数之和等问题上的应用,以及字符串相关的回文子串、最长回文串等挑战。
摘要由CSDN通过智能技术生成

LeetCode

数据结构

数组

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. 最长连续序列

问题

在这里插入图片描述

思路
  1. 数组排序 ,再循环数组,判断是否连续 (类似双指针),计算长度,更新最大值
//执行用时: 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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值