leetcode

leetcode

-2022.4.1 -BDY

猛猪猪语录:五百道成神


文章目录


一、二分查找(搜索插入位置)

class Solution {
    public int searchInsert(int[] nums, int target) {
 int n = nums.length;

        // 定义target在左闭右闭的区间,[low, high]
        int low = 0;
        int high = n - 1;

        while (low <= high) { // 当low==high,区间[low, high]依然有效
            int mid = low + (high - low) / 2; // 防止溢出
            if (nums[mid] > target) {
                high = mid - 1; // target 在左区间,所以[low, mid - 1]
            } else if (nums[mid] < target) {
                low = mid + 1; // target 在右区间,所以[mid + 1, high]
            } else {
                // 1. 目标值等于数组中某一个元素  return mid;
                return mid;
            }
        }
        // 2.目标值在数组所有元素之前 3.目标值插入数组中 4.目标值在数组所有元素之后 return right + 1;
        return high + 1;
    }
}

主要注意为什么是low<=high
为什么两个:high = mid - 1; low = mid + 1;
为什么是 return high + 1;

总结:上面是左闭右闭
如果是左闭右开+》
high = mid ; low = mid + 1;
在经典二分法中两种都适用,但是有其他条件时需要额外考虑

二、链表(两数相加)

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
        ListNode cur = pre;
        int carry = 0;
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);

            cur = cur.next;
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry == 1) {
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

这里
cur.next = new ListNode(sum);
cur = cur.next;
是链表指向下一个节点的方法重点

三、二分查找+滑动边界(34. 在排序数组中查找元素的第一个和最后一个位置)

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int a[] = new int[2];
        int l=0;
        int r=nums.length-1;
        int m=0;
        int n=0;
        while(l<=r){
            m=l+(r-l)/2;
          if(nums[l]>target||nums[r]<target){
            a[0]=-1;
            a[1]=-1;
            return a;
          } else if(nums[m]==target){
              
              while(target==nums[m-n]){
                    n++;
                  if(m<n){
                      break;
                  }
              }
               if(m<n){
                   n--;
                    a[0]= m-n;
                  }else{
                    a[0]=m-n+1;
                  }
                n=0;

              while(target==nums[m+n]){
                   n++;
                   if((m+n)>=nums.length){
                      break;
                  }
              }
              if((m+n)>=nums.length){
                  n--;
                     a[1]=m+n;
                  }else{
                     a[1]=m+n-1;
                  }
              return a;

          }else if (nums[m]>target){
              r=m-1;
          }
          else if(nums[m]<target){
              l=m+1;
          }

        }
        a[0]=-1;
        a[1]=-1;
        return a;
    }
}

重点在寻找到一个值后向左右滑动判断,并且考虑边界条件

四、二分查找

class Solution {
    public int search(int[] nums, int target) {
            int l=0;
            int r=nums.length-1;
            int m=0;
            while(l<=r){//使得总能保证l==r的情况
                m=l+(r-l)/2;
                if(target>nums[r]||target<nums[l]){
                    return -1;
                }
                if(nums[m]==target){
                    return m;
                }else if(nums[m]<target){
                    l=m+1;
                }else if(nums[m]>target){
                    r=m;
                }
            }
            return -1;
    }
}

五、二分查找(69. x 的平方根 )

class Solution {
    public int mySqrt(int x) {
          int l=0;
        int r=x;
        int m=0;
        long a,b;
        while(l<=x){
            m=l+(r-l)/2;
            a=(long)m*m;
            b=(long)(m+1)*(m+1);
            if(a<=x&&b>x){
                return m;
            }
            else if(a>x){
                r=m-1;
            }else if(a<x&&b<=x){
                l=m+1;
            }
        }
        return m;


    }
}

注意int的范围,使用long避免越界
暴力解法会time out ,使用二分法

六、二分查找(367. 有效的完全平方数)

class Solution {
    public boolean isPerfectSquare(int num) {
        int l=0;
        int r=num;
        int m=0;
        long a;
        while(l<=r){
            m=l+(r-l)/2;
            a=(long)m*m;
            if(a==num){
                return true;
            }
            else if(a>num){
                r=m-1;
            }else if(a<num){
                l=m+1;
            }
        }
        return false;

    }
}

主要思想还是是利用二分法解决超时问题

七、移除元素(27. 移除元素)

暴力解法

class Solution {
    public int removeElement(int[] nums, int val) {
         int n=nums.length;
        int j=nums.length;
        if(nums == null || nums.length == 0){
            return 0;
        }
        while(n>=1){
            if(nums[n-1]!=val){
                break;
            }
            j--;
            n--;

        }
        for(int i=0;i<n;i++){
            if(nums[i]==val){
                for(int k=i;k<n-1;k++){
                    nums[k]=nums[k+1];
                    nums[k+1]=-1;

                }
                i--;
                j--;
            }
        }
        return j;
    }
}

主要学习关于临界值的判断
直接元素前移

快慢指针法

class Solution {
    public int removeDuplicates(int[] nums) {
        int n=nums.length;
        int j=nums.length;
        if(nums == null || nums.length == 0){
            return 0;
        }
        for(int i=0;i<n-1;i++){
            if(nums[i]==-10001){
                break;
            }
            if(nums[i]==nums[i+1]){
                for(int k=i;k<n-1;k++){
                    nums[k]=nums[k+1];
                    nums[k+1]=-10001;

                }
                i--;
                j--;
            }
        }
        return j;
    }
}

顾名思义

八、移除元素(26. 删除有序数组中的重复项)

暴力解法

class Solution {
    public int removeDuplicates(int[] nums) {
        int n=nums.length;
        int j=nums.length;
        if(nums == null || nums.length == 0){
            return 0;
        }
        for(int i=0;i<n-1;i++){
            if(nums[i]==-10001){
                break;
            }
            if(nums[i]==nums[i+1]){
                for(int k=i;k<n-1;k++){
                    nums[k]=nums[k+1];
                    nums[k+1]=-10001;

                }
                i--;
                j--;
            }
        }
        return j;
    }
}

2。通过快慢指针,找到指定元素再删除指定元素

class Solution {
    public int removeDuplicates(int[] nums) {
        //找到指定元素
       int f=0;
            int s=0;
            int j=nums.length;
            for(;f<nums.length;f++){
                if(nums[f]!=nums[s]){
                    s=f;
                }
                if(nums[f]==nums[s]&&f!=s){
                    nums[f]=-10001;
                }
            }
            //干掉-10001
        Map<Integer, Integer> map = new HashMap<>();
            int q=0;
            int w=0;
            for(int i=0;i<nums.length;i++){
                if(nums[i]==-10001){
                map.put(q,i);
                q++;
                 j--;
                }
                if(nums[i]!=-10001){
                    Integer integer = map.get(w);
                    if(integer!=null){
                        nums[integer] = nums[i];
                        nums[i]=-10001;
                        map.put(q, i);
                        q++;
                        w++;
                        
                    }
                }
            }
            return j;
    }
}

3.大佬的

class Solution {
    public int removeDuplicates(int[] nums) {
       int ans = 0;
        for (int i = 1; i < nums.length; i++) 
        if (nums[i] != nums[i - 1]) 
        nums[++ans] = nums[i];
        return ans + 1;

    }
}

真正的利用双指针,太炫酷了
我真你吗是沙口我日了,就算用后值替换也只会把不重复的值替换到前排

九、移除元素(283. 移动零)

class Solution {
    public void moveZeroes(int[] nums) {
        //循环把需要的元素放在前,再改变后面的元素
            int s=0;
        int j=nums.length-1;
        for(int f=0;f<nums.length;f++){
            if(nums[f]!=0){
                nums[s]=nums[f];
                s++;

            }
            if(nums[f]==0){
                j--;
            }
        }

        for(int i=nums.length-1;i>j;i--){
            nums[i]=0;
        }

    }
}

十、有序数组的平方(977. 有序数组的平方)

暴力解法

class Solution {
    public int[] sortedSquares(int[] nums) {
        int right = nums.length - 1;
        int left = 0;
        int[] result = new int[nums.length];
        int index = result.length - 1;
        while (left <= right) {
            if (nums[left] * nums[left] > nums[right] * nums[right]) {
                result[index--] = nums[left] * nums[left];
                ++left;
            } else {
                result[index--] = nums[right] * nums[right];
                --right;
            }
        }
        return result;
    }
}

双指针法

class Solution {
    public int[] sortedSquares(int[] nums) {
        int l = 0;
        int r = nums.length - 1;
        int[] res = new int[nums.length];
        int j = nums.length - 1;
        while(l <= r){
            if(nums[l] * nums[l] > nums[r] * nums[r]){
                res[j--] = nums[l] * nums[l++];
            }else{
                res[j--] = nums[r] * nums[r--];
            }
        }
        return res;
    }
}

十一、长度最小子数组(209. 长度最小的子数组)

滑动窗口(双指针)

这道题好像不能更改数组顺序
自己sb了,这里别人说的时候最小子数组,肯定不能改了三

class Solution {

    // 滑动窗口
    public int minSubArrayLen(int s, int[] nums) {
        int left = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        for (int right = 0; right < nums.length; right++) {
            sum += nums[right];
            while (sum >= s) {
                result = Math.min(result, right - left + 1);
                sum -= nums[left++];
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

十二、59.螺旋矩阵II

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] res = new int[n][n];

        // 循环次数
        int loop = n / 2;

        // 定义每次循环起始位置
        int startX = 0;
        int startY = 0;

        // 定义偏移量
        int offset = 1;

        // 定义填充数字
        int count = 1;

        // 定义中间位置
        int mid = n / 2;
        while (loop > 0) {
            int i = startX;
            int j = startY;

            // 模拟上侧从左到右
            for (; j<startY + n -offset; ++j) {
                res[startX][j] = count++;
            }

            // 模拟右侧从上到下
            for (; i<startX + n -offset; ++i) {
                res[i][j] = count++;
            }

            // 模拟下侧从右到左
            for (; j > startY; j--) {
                res[i][j] = count++;
            }

            // 模拟左侧从下到上
            for (; i > startX; i--) {
                res[i][j] = count++;
            }

            loop--;

            startX += 1;
            startY += 1;

            offset += 2;
        }

        if (n % 2 == 1) {
            res[mid][mid] = count;
        }

        return res;
    }
}

十三、关于数组的总结

1.二分法
2.双指针法
3.滑动窗口(也是双指针)
4.模拟行为
循环不变量原则

十四、移除链表元素(203. 移除链表元素)

1.将元素放到新链表
最后要点:删除最后一个链表位置

class Solution {
  public ListNode removeElements(ListNode head, int val) {
        ListNode a=new ListNode();
        ListNode b=a;
          ListNode c=a;
          int i=0;
          if(head==null){
              return head;
          }
        while(head!=null){
            if(head.val!=val){
            a.next = new ListNode();
            a.val=head.val;
            c=a;
            a=a.next;
            i++;
            }
            head=head.next;
        }
        if(i==0){
            return null;
        }

       c.next=null;
        return b;
    }
}

2.直接删掉元素返回当前链表
要新增前节点,用于指向( ListNode b=head;)
先清除从头出现的需要删除的元素( a=a.next;)
注意特殊条件的判断

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode a=head;
        ListNode b=head;
        while(head!=null){
            if(a.val==val){
                a=a.next;
                if(b.next!=null){
                     b=b.next;
                }           
            }

            else if(head.val==val){
                b.next=head.next;             
            }
            if(b.next!=null){
                if(b.next.val!=val){
                    b=head;
                }                 
            }
            head=head.next;
        }
        return a;
    }
}

3.官方版本(是否使用虚拟前节点)
也是使用了一前一后两个节点,但是跟自己写的还是有区别
我是设置一个节点在每次更新节点时指向这个节点,这样会出现问题(在连续出现两个需要删除的元素时),解决办法:再出现这种情况时,不更新b这个节点的地址,(还是自己的复杂点)

/**
 * 添加虚节点方式
 * 时间复杂度 O(n)
 * 空间复杂度 O(1)
 * @param head
 * @param val
 * @return
 */
public ListNode removeElements(ListNode head, int val) {
    if (head == null) {
        return head;
    }
    // 因为删除可能涉及到头节点,所以设置dummy节点,统一操作
    ListNode dummy = new ListNode(-1, head);
    ListNode pre = dummy;
    ListNode cur = head;
    while (cur != null) {
        if (cur.val == val) {
            pre.next = cur.next;
        } else {
            pre = cur;
        }
        cur = cur.next;
    }
    return dummy.next;
}
/**
 * 不添加虚拟节点方式
 * 时间复杂度 O(n)
 * 空间复杂度 O(1)
 * @param head
 * @param val
 * @return
 */
public ListNode removeElements(ListNode head, int val) {
    while (head != null && head.val == val) {
        head = head.next;
    }
    // 已经为null,提前退出
    if (head == null) {
        return head;
    }
    // 已确定当前head.val != val
    ListNode pre = head;
    ListNode cur = head.next;
    while (cur != null) {
        if (cur.val == val) {
            pre.next = cur.next;
        } else {
            pre = cur;
        }
        cur = cur.next;
    }
    return head;
}

十五、707. 设计链表

当人在脑子不清醒的时候是看不懂代码的,
1.其实按照我的思路是能写出来的,但是总是会因为出现点问题导致思路偏航,写出来的其实可以再看看能不能化简

class Listnode {
    int val;
    Listnode next;

    public Listnode() {
    }

    public Listnode(int val) {
        this.val = val;
    }
}

class MyLinkedList {

    Listnode listnode;
    int index1 ;

    public MyLinkedList() {
        index1 = 0;
        listnode = new Listnode(0);
    }

    public int get(int index) {
        Listnode l2 = listnode;
        if (index < index1&&index>=0) {
            for (int i = 0; i <= index; i++) {
                l2 = l2.next;
            }
            return l2.val;
        } else {
            return -1;
        }
    }

    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    public void addAtTail(int val) {
        addAtIndex(index1, val);
    }

    public void addAtIndex(int index, int val) {

        if(index<0){
            index=0;
        }
        if (index > index1) {
            return;
        }
        index1++;
        Listnode l2 = listnode;
            for (int i = 0; i < index; i++) {
                l2 = l2.next;
            }
            Listnode list= new Listnode(val);
            list.next=l2.next;
            l2.next = list;


    }

    public void deleteAtIndex(int index) {
        Listnode l2 = listnode;
        if(index < 0 || index >= index1) {
            return;
        }
        index1--;
        for (int i = 0; i < index ; i++) {
            l2=l2.next;
        }
        l2.next=l2.next.next;

    }
}

十六、206. 反转链表

1.取出元素反向填充

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        int []a = new int[5000];
        int i=0;
        if(head==null){
            return head;
        }
        while(head!=null){
           a[i] = head.val;
           i++;
            head=head.next;
        }
        i--;
        ListNode list = new ListNode(0);
        ListNode b = list;
        for(;i>=0;i--){
            list.val=a[i];          
            if(i!=0){  
                list.next= new ListNode();
                list=list.next;
            }           
        }
        return b;
    }
}

2.双链表直接反向

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
     ListNode a=head;
        ListNode b = new ListNode();
        ListNode c = head;
        if(head==null){
            return head;
        }
        
        if(head.next!=null){
            head=head.next;
            a.next=null;
            b= head;
        }else {
            return head;
        }
        while(head!=null){
            c=head;
            head=head.next;
            b.next=a;
            a=c;
            if(head!=null)
            b=head;

        }
        return b;
    }
}

十七、24. 两两交换链表中的节点

注意特殊值 空和一
注意特殊的位置第一次换位子的时候
注意单数链表情况
(这里用虚拟头结点指向就不用分是不是第一次)

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode a;
        ListNode b;
        ListNode a2=head;
        int i = 0;
        if(head==null||head.next==null){
            return head;
        }
        ListNode c = head.next;
        while (head != null) {
            //完成交换
            if (i == 0) {
                a = head;
                b = head.next;
                a.next = b.next;
                b.next = a;
                i++;
            }else{
                a = head;
                b = head.next;
                if(b==null){
                    return c;
                }
                a.next = b.next;
                b.next = a;
                a2.next=b;
            }
            a2=head;
            head=head.next;
            //进入下一个循环
        }
        return c;
    }
}

十八、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; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        int i=0;
        ListNode d = new ListNode(0,head);
        ListNode a=d;
        ListNode c=d;
        ListNode b=head;
        while(head!=null){
            head=head.next;
            i++;
        }
        if(n<=0||n>i){
            return b;
        }
        if(i==1){
            return null;
        }
        for(int j=n;j<=i;j++){
            a=d;
            d=d.next;
        }
        a.next=d.next;
        c=c.next;
        return c;
    }
}

十九、面试题 02.07. 链表相交

1.暴力解法(双循环)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        ListNode b;
        while(headA!=null){
            b= headB;
            while(b!=null){
                if(headA==b){
                  return headA;
                }
                b=b.next;
            }
            headA=headA.next;
        }
        return null;
    }
}

2.双指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        while(curA != null){
            lenA++;
            curA = curA.next;
        }
        while(curB != null){
            lenB++;
            curB = curB.next;
        }

        curA = headA;
        curB = headB;
        if(lenB > lenA){
            int tmplen = lenA;
            lenA = lenB;
            lenB = tmplen;

            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }

        int gap = lenA - lenB;
        while(gap-- > 0){
            curA = curA.next;
        }
        
        while(curA != null){
            if(curA == curB){
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }

        return null;

    }
}

二十、142. 环形链表 II

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

二十一、链表总结

1.使用虚拟头结点(真的会方便很多)
2.双指针

二十二、242. 有效的字母异位词(hash)

1.以数组为hash表,顺序将两个字符串映射到hash,通过+1,-1,使得最后都为0,代表相等

class Solution {
    public boolean isAnagram(String s, String t) {
int[] record = new int[26];
        for (char c : s.toCharArray()) {
            record[c - 'a'] += 1;
        }
        for (char c : t.toCharArray()) {
            record[c - 'a'] -= 1;
        }
        for (int i : record) {
            if (i != 0) {
                return false;
            }
        }
        return true;
    }
}

2.将两个字符串变为字符数组,再排序,如何比较

public boolean isAnagram1(String s, String t) {
        char[] sChars = s.toCharArray();
        char[] tChars = t.toCharArray();
        Arrays.sort(sChars);
        Arrays.sort(tChars);
        return Arrays.equals(sChars, tChars);
    }

3.逻辑与上种类似

public boolean isAnagram(String s, String t) {
        return Arrays.equals(s.chars().sorted().toArray(), t.chars().sorted().toArray());
    }

二十三、349. 两个数组的交集(hash)

1.使用数组当hash,再拿一个数组存储

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        //这里使用数组当hash是因为限制了数组的长度
        //使用数组会空间浪费
        int []a = new int[1000];
        
        int []b= new int[1000];
        int q=0;
        for(int i=0;i<nums1.length;i++){
            a[nums1[i]]++;
        }
        for(int j=0;j<nums2.length;j++){
            if(a[nums2[j]]!=0){
                a[nums2[j]]=0;
                b[q]=nums2[j];
                 q++;
            }
        }
        int []c = new int[q];
        for(int k=0;k<q;k++){
            c[k]=b[k];
        }
        return c;
    }
}

2.使用set(hashset)

import java.util.HashSet;
import java.util.Set;

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
            return new int[0];
        }
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> resSet = new HashSet<>();
        //遍历数组1
        for (int i : nums1) {
            set1.add(i);
        }
        //遍历数组2的过程中判断哈希表中是否存在该元素
        for (int i : nums2) {
            if (set1.contains(i)) {
                resSet.add(i);
            }
        }
        int[] resArr = new int[resSet.size()];
        int index = 0;
        //将结果几何转为数组
        for (int i : resSet) {
            resArr[index++] = i;
        }
        return resArr;
    }
}

二十四、202. 快乐数(hash)

1.使用set的contains方法来判断是否有值,减少时间复杂度
2.关于循环取值的问题:取余后除10

public class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set2 = new HashSet<>();
        if (n == 1) {
            return true;
        }

        while (true) {
            int m = 0;
            set2.add(n);
            while (n > 0) {
                int temp = n % 10;
                m += temp * temp;
                n = n / 10;
            }

            if (m == 1) {
                return true;
            } else {
                //这里使用set的contains方法会减少时间复杂度
                boolean contains = set2.contains(m);
                if(contains==true){
                    return false;
                }
            }
                n = m;
            }
        }
    }

二十五、1. 两数之和

//先排序再向后遍历=》优化:先放进list再排序跟暴力解法类似
//暴力解法
//二分法双循环
//map使用方法containsKey
在map中放入Key=target-num:value=下标
因为两数相加,所以这两个数是一个映射

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //先排序再向后遍历=》优化:先放进list再排序
        //暴力解法
        //二分法
        //map使用方法containsKey
         int[] res = new int[2];
    if(nums == null || nums.length == 0){
        return res;
    }
    Map<Integer, Integer> map = new HashMap<>();
    for(int i = 0; i < nums.length; i++){
        int temp = target - nums[i];
        if(map.containsKey(temp)){
            res[1] = i;
            res[0] = map.get(temp);
        }
        map.put(nums[i], i);
    }
    return res;
        
    }
}

二十六、454. 四数相加 II

统计两个数组中的元素之和,同时统计出现的次数,放入map
key=和
value=出现次数
和上面一道题类似,出现两个数,这两个数有映射关系,通过containsKey和0 - temp查找

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>();
        int temp;
        int res = 0;
        //统计两个数组中的元素之和,同时统计出现的次数,放入map
        for (int i : nums1) {
            for (int j : nums2) {
                temp = i + j;
                if (map.containsKey(temp)) {
                    map.put(temp, map.get(temp) + 1);
                } else {
                    map.put(temp, 1);
                }
            }
        }
        //统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                temp = i + j;
                if (map.containsKey(0 - temp)) {
                    res += map.get(0 - temp);
                }
            }
        }
        return res;
    }
}

二十七、383. 赎金信

因为是比较两个字符串,所以可以用数组来存储单个字符出现次数

class Solution {
   public boolean canConstruct(String ransomNote, String magazine) {
       
        char[] chars = ransomNote.toCharArray();
        char[] chars1 = magazine.toCharArray();
        int []a = new int[26];
        for (int i = 0; i < ransomNote.length(); i++) {
            a[chars[i]-'a']++;
        }
        for(int j=0;j<magazine.length();j++){
            a[chars1[j]-'a']--;
        }
        for (int i : a) {
            if(i>0){
                return false;
            }
        }
        return true;
    }
}

二十八、15. 三数之和

自己写的hash,会超时

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        if (nums == null || nums.length < 3) {
            return lists;
        }

        List<int[]> list2 = new ArrayList<>();
        Map<Integer, List<int[]>> map = new HashMap<>();
        for (int i = 0; i < nums.length - 1; i++) {
            for (int k = i + 1; k < nums.length; k++) {
                int[] a = new int[3];
                a[0] = i;
                a[1] = k;
                if (map.containsKey(nums[i] + nums[k])) {
                    map.get(nums[i] + nums[k]).add(a);
                } else {
                    list2.add(a);
                    map.put(nums[i] + nums[k], list2);
                }
            }
        }
//        找出三元组
        List<List<Integer>> list1 = new ArrayList<>();

        for (int j = 0; j < nums.length; j++) {
            if (map.containsKey(0 - nums[j])) {

                    List<int[]> list3 = map.get(0 - nums[j]);
                    for (int[] ints : list3) {
                        if(ints[0]!=j&&ints[1]!=j){
                            if(nums[ints[0]]+nums[ints[1]]==-nums[j]){
                                List<Integer> list = new ArrayList<>();
                                list.add(nums[ints[0]]);
                                list.add(nums[ints[1]]);
                                list.add(nums[j]);
                                //还是要排序才能让两个list相同
                                System.out.println(list);
                                Collections.sort(list);
                                lists.add(list);
                            }

                        }

                    }
            }
        }
//        去除重复三元组
        //方法是不存在则加入
        for (List<Integer> list : lists) {
            if (!list1.contains(list)) {
                list1.add(list);
            }
        }
        return list1;
    }
}

2.双指针法
在这里插入图片描述

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);

        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) {
                return result;
            }

            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            int left = i + 1;
            int right = nums.length - 1;
            while (right > left) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    
                    right--; 
                    left++;
                }
            }
        }
        return result;
    }
}

二十九、18. 四数之和

还是双指针法

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
       
        for (int i = 0; i < nums.length; i++) {

            if (i > 0 && nums[i - 1] == nums[i]) {
                continue;
            }
            
            for (int j = i + 1; j < nums.length; j++) {

                if (j > i + 1 && nums[j - 1] == nums[j]) {
                    continue;
                }

                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
}

三十、Hash总结

1.数组,set,list,map
2.四个的区别和方法要记牢
3.判断什么时候用hash,一般要有键值对映射的时候

hash:散列表(Hash table,也叫哈希表),是根据关键码值(Key
value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

三十一、344. 反转字符串(字符串)

1.直接调换
2.利用空字符串来接收
3.StringBuffer自带的方法

class Solution {
    public void reverseString(char[] s) {
            char q;
            for(int i=0;i<s.length/2;i++){
               q=s[i];
               s[i]=s[s.length-i-1];
               s[s.length-i-1]=q;
            }
            
    }
}

三十二、541. 反转字符串 II

简单逻辑,找到反转,用函数比较方便
StringBuffer可变字符串,线程安全

class Solution {
    public String reverseStr(String s, int k) {
int  v = s.length();
        String a = "";
        for(int j = 0; j< v; j+=k*2){
            if(v-j<k){
                 a += new StringBuffer(s.substring(j, v)).reverse().toString();
            }
            else{
                a += new StringBuffer(s.substring(j, j + k)).reverse().toString();
               if(j+k*2<v){
                   a += s.substring(j + k, j + 2 * k);
               }else{
                   a += s.substring(j + k, v);
               }

            }

        }
        return a;
    }
}

三十三、剑指 Offer 05. 替换空格.

简单逻辑

class Solution {
    public String replaceSpace(String s) {
         String a = "";
        char[] chars = s.toCharArray();
        for (char aChar : chars) {
            if(aChar==' '){
                a += "%20";
            }else{
                a += aChar;
            }
        }
        return a;
    }
}

2.双指针法
使用StringBuilder,变成可变字符串,线程不安全

public String replaceSpace(String s) {
    if(s == null || s.length() == 0){
        return s;
    }
    //扩充空间,空格数量2倍
    StringBuilder str = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
        if(s.charAt(i) == ' '){
            str.append("  ");
        }
    }
    //若是没有空格直接返回
    if(str.length() == 0){
        return s;
    }
    //有空格情况 定义两个指针
    int left = s.length() - 1;//左指针:指向原始字符串最后一个位置
    s += str.toString();
    int right = s.length()-1;//右指针:指向扩展字符串的最后一个位置
    char[] chars = s.toCharArray();
    while(left>=0){
        if(chars[left] == ' '){
            chars[right--] = '0';
            chars[right--] = '2';
            chars[right] = '%';
        }else{
            chars[right] = chars[left];
        }
        left--;
        right--;
    }
    return new String(chars);
}

三十四、151. 颠倒字符串中的单词

1.简单思路,还是需要一个空字符串去接收
我这里是使用String的split
还可以使用char[] (这是代码随想录里最快的解法)
先找到空格再将空格间的单词收集

class Solution {
    public String reverseWords(String s) {
String[] s1 = s.split(" ");
        String a = "";
        for(int i=s1.length-1;i>=0;i--){
           a += s1[i];
                if(i!=0){
                    if(!s1[i-1].equals("")){
                        a+=" ";
                    }
                }
    }
        return a;
    }
}

2.利用StringBuilder
//清除多余字符串(首部和中间的)
//反转
//将单词反转(跟上面一样,找空格然后反转)

class Solution {
    public String reverseWords(String s) {
 //清除多余字符串

        StringBuilder s1 = new StringBuilder(s);
         while(true){
            char c = s1.charAt(0);
            char c1 = s1.charAt(s1.length() - 1);
            if (c == ' ') s1.deleteCharAt(0);
            if (c1 == ' ') s1.deleteCharAt(s1.length() - 1);
            if(c1!=' '&&c!=' '){
                break;
            }
        }
         int q1=1;
        while(q1<s1.length()){
            if(s1.charAt(q1)==' '&&s1.charAt(q1-1)==' '){
                s1.deleteCharAt(q1-1);
                 q1--;
            }
            q1++;
        }
        //反转
        StringBuilder reverse = s1.reverse();
        //将单词反转
        int i = 0;
        int j = 0;
        int i2 = 0;
        while (i < s1.length()) {
            if (s1.charAt(i) == ' ') {
                 i2 = i - 1;
                while (i2 > j) {
                    char c2 = s1.charAt(i2);
                    s1.setCharAt(i2, s1.charAt(j));
                    s1.setCharAt(j, c2);
                    i2--;
                    j++;
                }
                j = i + 1;
            }
            i++;
        }

        while (i -1> j) {
            char c2 = s1.charAt(j);
            s1.setCharAt(j, s1.charAt(i-1));
            s1.setCharAt(i-1, c2);
            i--;
            j++;
        }
        return s1.toString();
}
}

三十五、剑指 Offer 58 - II. 左旋转字符串

1.使用java函数库,很简单

class Solution {
    public String reverseLeftWords(String s, int n) {
                String a = s.substring(n, s.length())+ s.substring(0, n);
        return a;
    }
}

2.使用字符数组

class Solution {
    public String reverseLeftWords(String s, int n) {
       char[] chars = s.toCharArray();
        char[] c = new char[s.length()];
        int i=0;

        for (char aChar : chars) {
            if(i>=n){
                c[i-n]=aChar;
            }else {
                c[s.length() - n + i] = aChar;

            }
            i++;
        }

        return new String(c);
    }
}

3.在原有字符串上操作,翻转再翻转

class Solution {
    public String reverseLeftWords(String s, int n) {
        int len=s.length();
        StringBuilder sb=new StringBuilder(s);
        reverseString(sb,0,n-1);
        reverseString(sb,n,len-1);
        return sb.reverse().toString();
    }
     public void reverseString(StringBuilder sb, int start, int end) {
        while (start < end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
            }
        }
}

三十六、28. 实现 strStr()**KMP算法

1.我能想到的思路,先找到首位然后开始循环对比
循环内嵌套循环,最暴力解法

class Solution {
    /**
     * 基于窗口滑动的算法
     * <p>
     * 时间复杂度:O(m*n)
     * 空间复杂度:O(1)
     * 注:n为haystack的长度,m为needle的长度
     */
    public int strStr(String haystack, String needle) {
        int m = needle.length();
        // 当 needle 是空字符串时我们应当返回 0
        if (m == 0) {
            return 0;
        }
        int n = haystack.length();
        if (n < m) {
            return -1;
        }
        int i = 0;
        int j = 0;
        while (i < n - m + 1) {
            // 找到首字母相等
            while (i < n && haystack.charAt(i) != needle.charAt(j)) {
                i++;
            }
            if (i == n) {// 没有首字母相等的
                return -1;
            }
            // 遍历后续字符,判断是否相等
            i++;
            j++;
            while (i < n && j < m && haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            }
            if (j == m) {// 找到
                return i - j;
            } else {// 未找到
                i -= j - 1;
                j = 0;
            }
        }
        return -1;
    }
}

2.经典KMP算法实现(解决字符串匹配问题)

class Solution {
    public void getNext(int[] next, String s){
        int j = -1;
        next[0] = j;
        for (int i = 1; i<s.length(); i++){
            while(j>=0 && s.charAt(i) != s.charAt(j+1)){
                j=next[j];
            }

            if(s.charAt(i)==s.charAt(j+1)){
                j++;
            }
            next[i] = j;
        }
    }
    public int strStr(String haystack, String needle) {
        if(needle.length()==0){
            return 0;
        }

        int[] next = new int[needle.length()];
        getNext(next, needle);
        int j = -1;
        for(int i = 0; i<haystack.length();i++){
            while(j>=0 && haystack.charAt(i) != needle.charAt(j+1)){
                j = next[j];
            }
            if(haystack.charAt(i)==needle.charAt(j+1)){
                j++;
            }
            if(j==needle.length()-1){
                return (i-needle.length()+1);
            }
        }

        return -1;
    }
}

三十七、459. 重复的子字符串(KMP)

1.自己的写法,算是考虑每种情况然后暴力求解

class Solution {
     public boolean repeatedSubstringPattern(String s) {
        if(s.length()==1){
            return false;
        }
        if(s.length()==2){
            if (s.substring(0, 1).equals(s.substring(1, 2))) {
                return true;
            } else {
                return false;
            }
        }
        if(s.length()==3){
            if(s.substring(0, 1).equals(s.substring(1,2))&&s.substring(0, 1).equals(s.substring(2,3))){
                return true;
            }else{
                return false;
            }
        }
        for (int i = 1; i <= s.length()/2; i++) {
            int q=1;
           int j=i;
            while (s.substring(0, i).equals(s.substring(j,j+i))) {
                q++;
                j=i+j;
                if (j+i > s.length()) {
                    break;
                }
            }
            if (q * i == s.length()) {
                return true;
            }

        }

        return false;
    }
}

2.双指针法

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        char[] chars = s.toCharArray();
        if (chars.length == 1) {
            return false;
        }
        for (int i = 1; i < chars.length; i++) {
           int k = i;
            int j=0;
            int l = 0;
            while (chars[j++] == chars[k++]) {
                l++;
                if (k == s.length()) {
                    if (j == i) {
                        break;
                    } else {
                        return false;
                    }
                }

                if (j == i) {
                    j = 0;
                }

            }
            if (l + i == chars.length) {
                return true;
            }
        }
        return false;
    }
}

3.KMP经典next数组(最长相同前后缀)

class Solution {
    public boolean repeatedSubstringPattern(String s) {
            //使用KMP
        char[] chars = s.toCharArray();
        int[] next = new int[s.length()];
        next[0] = 0;
        int j = 0;
        for (int k = 1; k < chars.length; k++) {
            while (j > 0 && chars[j] != chars[k]) {
                j=next[--j];
            }
            if (chars[j] == chars[k]) {
                j++;
            }
            next[k]=j;
        }
        int len = s.length();
       if(next[len-1]>0&&len % (len - (next[len - 1])) == 0 ){
            return true;
        }
        return false;
    }
}

三十八、字符串总结

1.关于字符串的几种基本的类
String char StringBuffer StringBuilder
2.每个类的方法使用
3.KMP算法
4.少用库函数
5.双指针法
6.反转

三十九、双指针总结

1.面试题 02.07. 链表相交
请添加图片描述
对其后,让两个指针都指向短链表的开头的点

2.142. 环形链表 II
请添加图片描述
slow每次加一,fast每次加二,两个相遇说明有环
两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口

3.15. 三数之和
在这里插入图片描述
要先排序然后分快慢指针
先慢指针不动,快指针向后移条件是是否fast之前的数相加大于0
然后慢指针后移

4.18. 四数之和
在这里插入图片描述跟上面同理

总结:
一般使用双指针提高效率
在这里插入图片描述

四十、斐波那契数列

1.递归(自己写的用for实现递归,但是有问题)

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

问题:如何操作特大数

低精度到高精度不会精度丢失,高精度到低精度会精度丢失
在这里插入图片描述

class Solution {
    public int fib(int n) {
        int n0 = 0;
        int n1 = 1;
        if(n < 2){
            return n;
        }
        int res = 0;
        for(int i = 2;i <= n; i++){
            res = (n0 + n1) % 1000000007;
            n0 = n1;
            n1 = res;
        }
        return res;
    }
}

2.后面再说,有点东西

四十一、232. 用栈实现队列

没什么算法
第一次使用java自带的Stack

class MyQueue {
    Stack<Integer> stack1;
    Stack<Integer> stack2;
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    public void push(int x) {
        stack1.push(x);
    }

    public int pop() {
        if (stack2 .empty()) {
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
            
        }
        return  stack2.pop().intValue();
    }

    public int peek() {
        if (stack2 .empty()) {
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek().intValue();
    }

    public boolean empty() {
        return stack1.empty() && stack2.empty();
    }
}


/**
 * 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. 用队列实现栈

第一次使用java自带的Queue(实现是LinkedList)

class MyStack {

    Queue<Integer> queue1; // 和栈中保持一样元素的队列
    Queue<Integer> queue2; // 辅助队列

    /** Initialize your data structure here. */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue2.offer(x); // 先放在辅助队列中
        while (!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Queue<Integer> queueTemp;
        queueTemp = queue1;
        queue1 = queue2;
        queue2 = queueTemp; // 最后交换queue1和queue2,将元素都放到queue1中
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue1.poll(); // 因为queue1中的元素和栈中的保持一致,所以这个和下面两个的操作只看queue1即可
    }
    
    /** Get the top element. */
    public int top() {
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty();
    }
}

四十三、20. 有效的括号

思路:先用map放入规则,再用栈来映射规则

class Solution {
    public boolean isValid(String s) {
         char[] chars = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('[', ']');
        map.put('(', ')');
        map.put('{', '}');
        for (char aChar : chars) {
             if (!stack.empty() && map.get(stack.peek())!=null&&map.get(stack.peek()) == aChar) {
               
                stack.pop();
            } else {
                stack.push(aChar);
            }
        }
        if (stack.empty())
            return true;
        return false;
    }
}

四十四、1047. 删除字符串中的所有相邻重复项

自己用递归,G,超时
还是得用栈

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> stack = new Stack<>();
        char[] chars = s.toCharArray();
        for (char aChar : chars) {
            if (!stack.empty() && stack.peek() == aChar) {
                stack.pop();
            }else{
                stack.push(aChar);
            }
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (Character character : stack) {
            stringBuilder.append(character);
        }
        return stringBuilder.toString();
    }
}

四十五、150. 逆波兰表达式求值

1.自己使用栈来操作

class Solution {
    public int evalRPN(String[] tokens) {
           List<String> list = new ArrayList<>();
        list.add("+");
        list.add("-");
        list.add("*");
        list.add("/");
        Stack<String> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if(list.contains(tokens[i])){
                int i1 = Integer.parseInt(stack.pop());
                int i2 = Integer.parseInt(stack.pop());
                if(tokens[i].charAt(0)=='+'){
                   Integer c = (i1 + i2);
                    stack.push(c.toString());
                }
                if(tokens[i].charAt(0)=='-'){
                    Integer c = (i2 - i1);
                    stack.push(c.toString());
                }
                if(tokens[i].charAt(0)=='*'){
                    Integer c = (i1 * i2);
                    stack.push(c.toString());
                }
                if(tokens[i].charAt(0)=='/'){
                    Integer c = (i2 / i1);
                    stack.push(c.toString());
                }
            }else {
                stack.push(tokens[i]);
            }
        }
        return Integer.parseInt(stack.peek());
    }
}

四十六、滑动窗口最大值

在这里插入图片描述
使用双端队列
想想什么是双端队列
自己的思路是对的,但是在最后删除最大数之前的数的时候会出现问题,因为自己的list没有这样的方法

class Solution {
     public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length < 2) return nums;
        // 双端队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList();
        // 结果数组
        int[] result = new int[nums.length-k+1];
        for(int i = 0;i < nums.length;i++){
            // 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            // 添加当前值对应的数组下标
            queue.addLast(i);
            // 判断当前队列中队首的值是否有效
            if(queue.peek() <= i-k){
                queue.poll();   
            } 
            // 当窗口长度为k时 保存当前窗口中最大值
            if(i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }

}

2.自己写的单调队列但是也是删除在最大值之前的数

class Solution {
     public int[] maxSlidingWindow(int[] nums, int k) {
        int[] a = new int[nums.length - k + 1];
        //必须是双端队列
        List<Integer> list = new LinkedList<>();
        int q=0;
        for (int i = 0; i < nums.length; i++) {
            while(!list.isEmpty() && nums[list.get(q-1)] <= nums[i]){
                list.remove(q-1);
                q--;
            }
            list.add(i);
            q++;
            if(list.get(0) <= i-k){
                list.remove(0);
                q--;
            }
            if(i+1 >= k){
                a[i+1-k] = nums[list.get(0)];
            }
        }
        return a;
    }

}

四十七、前 K 个高频元素

逻辑简单,map存放,堆排序
重要的是如何把map 的key取出来(使用entryset)
然后取出来了,怎样放在list里面(创建一个类)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        int[] a = new int[k];
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                int integer = map.get(nums[i]);
                map.put(nums[i],integer+1);
            } else {
                map.put(nums[i], 1);
            }
        }
        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        List<S> list = new LinkedList<>();
        for (Map.Entry<Integer, Integer> entry : entries) {
            Integer key = entry.getKey();
            Integer value = entry.getValue();
            S s = new S(key, value);
            list.add(s);
        }
        Collections.sort(list,((o1, o2) -> {
           return o2.num - o1.num;
        }));

        for (int i = 0; i < k; i++) {
            a[i] = list.get(i).key;
        }
        return a;
    }
}
class S {
   public int key;
    public int num;

    public S(int key, int num) {
        this.key = key;
        this.num = num;
    }


}

四十八、栈和队列(堆)总结

在这里插入图片描述

四十九、二叉树的中序遍历

1.递归遍历
(无论前中后序的遍历递归的逻辑都一样)

 public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        dfs(res,root);//递归
        return res;
    }

    void dfs(List<Integer> res, TreeNode root) {
        if(root==null) {
            return;
        }
        //按照 左-打印-右的方式遍历
        dfs(res,root.left);
        res.add(root.val);
        dfs(res,root.right);
    }

2.迭代遍历(相当于自己写出了栈逻辑)

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while(stack.size()>0 || root!=null) {
            //不断往左子树方向走,每走一次就将当前节点保存到栈中
            //这是模拟递归的调用
            if(root!=null) {
                stack.add(root);
                root = root.left;
                //当前节点为空,说明左边走到头了,从栈中弹出节点并保存
                //然后转向右边节点,继续上面整个过程
            } else {
                TreeNode tmp = stack.pop();
                res.add(tmp.val);
                root = tmp.right;
            }
        }
        return res;
    }
}

五十、 二叉树的前序遍历

1.递归

class Solution {
    void dfs(List<Integer> res, TreeNode root) {
        if(root!=null) {
            //按照 左-打印-右的方式遍历 
        res.add(root.val);
        dfs(res,root.left);
        dfs(res,root.right);
        }   
    }
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new LinkedList<>();
        dfs(res,root);//递归
        return res;
    }
}

2.迭代

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        while (stack.size() != 0 || root != null) {
            if (root != null) {
                stack.add(root);
                list.add(root.val);
                root = root.left;
            }else{
                TreeNode pop = stack.pop();
                root = pop.right;
            }
        }
        return list;
    }
}

五十一、二叉树的后序遍历

1.递归

class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        postorder(root,list);
        return list;
    }
    void postorder(TreeNode root,List<Integer> list){
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);
    }
}

2.迭代(右左中然后倒转)

class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        while (root != null || stack.size() > 0) {
            if (root != null) {
                stack.add(root);
                list.add(root.val);
                root = root.right;
            } else {
                TreeNode pop = stack.pop();
                root = pop.left;
            }
        }
        Collections.reverse(list);
        return list;
    }

}

五十二、226. 翻转二叉树

1.递归
class Solution {
public TreeNode invertTree(TreeNode root) {
invert(root);
return root;
}
void invert(TreeNode root2){
if (root2 == null) {
return;
}
TreeNode treeNode = root2.left;
root2.left=root2.right;
root2.right=treeNode;
invert(root2.left);
invert(root2.right);
}
}
2.迭代

class Solution {
    public TreeNode invertTree(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        TreeNode root2 =root;
     while (queue != null&&root!=null) {
            if (root.left != null) {
                queue.add(root.left);
            }
            if (root.right != null) {
                queue.add(root.right);
            }
            TreeNode treeNode = root.left;
            root.left = root.right;
            root.right = treeNode;
             queue.poll();
            root= queue.peek();
        }
        return root2;
    }
    
}

五十三、101. 对称二叉树

1.递归

class Solution {
	public boolean isSymmetric(TreeNode root) {
		if(root==null) {
			return true;
		}
		//调用递归函数,比较左节点,右节点
		return dfs(root.left,root.right);
	}
	
	boolean dfs(TreeNode left, TreeNode right) {
		//递归的终止条件是两个节点都为空
		//或者两个节点中有一个为空
		//或者两个节点的值不相等
		if(left==null && right==null) {
			return true;
		}
		if(left==null || right==null) {
			return false;
		}
		if(left.val!=right.val) {
			return false;
		}
		//再递归的比较 左节点的左孩子 和 右节点的右孩子
		//以及比较  左节点的右孩子 和 右节点的左孩子
		return dfs(left.left,right.right) && dfs(left.right,right.left);
	}
}


2.迭代

class Solution {
	public boolean isSymmetric(TreeNode root) {
		if(root==null || (root.left==null && root.right==null)) {
			return true;
		}
		//用队列保存节点
		LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
		//将根节点的左右孩子放到队列中
		queue.add(root.left);
		queue.add(root.right);
		while(queue.size()>0) {
			//从队列中取出两个节点,再比较这两个节点
			TreeNode left = queue.removeFirst();
			TreeNode right = queue.removeFirst();
			//如果两个节点都为空就继续循环,两者有一个为空就返回false
			if(left==null && right==null) {
				continue;
			}
			if(left==null || right==null) {
				return false;
			}
			if(left.val!=right.val) {
				return false;
			}
			//将左节点的左孩子, 右节点的右孩子放入队列
			queue.add(left.left);
			queue.add(right.right);
			//将左节点的右孩子,右节点的左孩子放入队列
			queue.add(left.right);
			queue.add(right.left);
		}
		
		return true;
	}
}

五十四、层序遍历

1.迭代

class Solution {
  public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new LinkedList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        if(root==null) {
            return new LinkedList<>();
        }
        while (queue.peek() != null) {
            int size = queue.size();
            List<Integer> treeNodes = new LinkedList<>();
            for (int i = 0; i < size; i++) {
                if (root.left != null) {
                    queue.add(root.left);
                }
                if (root.right != null) {
                    queue.add(root.right);
                }
                treeNodes.add(queue.poll().val);
                root = queue.peek();
            }
            list.add(treeNodes);
        }

        return list;
    }
}

2.递归

五十五、104. 二叉树的最大深度

1.递归

向左右遍历找到最大深度

public class Solution1 {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return loop(root);
    }

    public int loop(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int left = 1 + loop(node.left);
        int right = 1 + loop(node.right);
        return Math.max(left, right);
    }
}

2.DFS(深度优先搜索)
使用前序遍历

3.BFS(广度优先)
使用层序遍历
这个逻辑上面最简单

class Solution {
    public int maxDepth(TreeNode root) {
          if(root==null){
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int j=0;
        while (queue.peek() != null) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                if (root.left != null) {
                    queue.add(root.left);
                }
                if (root.right != null) {
                    queue.add(root.right);
                }
                queue.poll();
                root = queue.peek(); 
            }
            j++;
        }

        return j;
    }
}

五十六、二叉树的最小深度

1.递归

class Solution {
    public int minDepth(TreeNode root) {
        int i=0;
        return   min(root,i);
    }
    int min(TreeNode root,int i){
        i++;
        if(root.left==null&&root.right==null){
            return i;
        }
        if (root.left != null) {
            int min = min(root.left, i);
            if (min < i) {
                i = min;
            }
        }
        if (root.right != null) {
            int min = min(root.right, i);
            if (min < i) {
                i = min;
            }
        }
        return i;
    }
}

2.迭代

五十七、222. 完全二叉树的节点个数

1.最简单解法
前序遍历

class Solution {
    public int countNodes(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        int i = 0;
        while (root != null || stack.size() > 0) {
            if (root != null) {       
                stack.add(root);
                root = root.left;
                i++;
            } else {
                root = stack.pop().right;
            } 
        }
        return i;
    }
}

2.递归(普遍解法)

class Solution {
    // 通用递归解法
    public int countNodes(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

3.递归(针对完全二叉树)

class Solution {
    public int countNodes(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int i=0;
        int leftDepth = getDepth(root.left);
        int rightDepth = getDepth(root.right);
        if (leftDepth == rightDepth) {
            // 左子树是满二叉树
            i = (int)Math.pow(2, leftDepth);
            int i1 = countNodes(root.right);
            i += i1;
        } else {
            // 右子树是满二叉树
            i = (int)Math.pow(2, rightDepth);
            int i1 = countNodes(root.left);
            i += i1;
        }
        return i;
    }

    private int getDepth(TreeNode root) {
        int depth = 0;
        while (root != null) {
            root = root.left;
            depth++;
        }
        return depth;
    }
}

五十八、110. 平衡二叉树

1.自顶向下(递归)
//主要在于判断最大深度
//因为每次都取得了左右子树最大深度,所以得判断每一个树都是平衡的

class Solution {
    public boolean isBalanced(TreeNode root) {
        //暴力法,依次至上而下递归,分别判断平衡情况

        if (root == null){
            return true;
        }

        //left表示root的左子树的最大深度
        int left = depth(root.left);

        //right表示root的右子树的最大深度
        int right = depth(root.right);

        //得判断每一个树都是平衡的
        if (!isBalanced(root.left) || !isBalanced(root.right)){
            return false;
        }
        //返回两树深度的绝对值
        return Math.abs(left - right) > 1 ? false : true;
    }
    int depth(TreeNode root){
        if (root == null) {
            return 0;
        }
        return Math.max( depth(root.left),depth(root.right))+1;
    }
}

五十九、257. 二叉树的所有路径

1.迭代

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        // 初始化
        List<String> res = new ArrayList<>();
        if (root == null)
            return res;

        Stack<Object> stack = new Stack<>();
        stack.push(root);
        stack.push(root.val + "");

        while (!stack.isEmpty()) {
            // 节点和路径出栈
            String path = (String) stack.pop();
            TreeNode node = (TreeNode) stack.pop();
            // 如果当前节点为叶子节点
            if (node.left == null && node.right == null) {
                res.add(path);
            }
            // 右子树入栈
            if (node.right != null) {
                stack.push(node.right);
                stack.push(path + "->" + node.right.val);
            }
            // 左子树入栈
            if (node.left != null) {
                stack.push(node.left);
                stack.push(path + "->" + node.left.val);
            }
        }
        return res;
    }
}

2.递归
//总算是自己写出来一道题了,要死了

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> list = new LinkedList<>();
        String s2 = "";
        b(root, s2, list);
        return list;
    }
    void b(TreeNode root,String s2 ,List<String> list){
        StringBuilder s = new StringBuilder(s2);
        if (root == null) {
            return;
        } else {
            s.append(root.val);
            s.append("->");
            b(root.left,s.toString(),list); 
            b(root.right,s.toString(),list);
        }
        if (root.right == null && root.left == null) {
              s.delete(s.length() - 2, s.length());
            list.add(s.toString());
        }
    }
}

六十、100. 相同的树

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
      return   is(p, q);
    }
    boolean is(TreeNode p, TreeNode q){
        if (p == null&&q != null) {
            return false;
        }
        if (p != null&&q == null) {
            return false;
        }
        if (p == null&&q == null){
            return true;
        }
        if (p.val != q.val) {
            return false;
        }
        boolean b = is(p.left, q.left);
        boolean b1 = is(p.right, q.right);

        return b1 && b;
        
    }
    
}

六十一、404. 左叶子之和

1.前序遍历

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        int i=0;
        while (stack.size() != 0 || root != null) {
            if (root != null) {
                stack.add(root);
                root = root.left;
            }else{
                
                TreeNode pop = stack.pop();
                if (pop.right == null && pop.left == null) {
                    if (!stack.isEmpty()&&stack.peek().left == pop) {
                        i += pop.val;
                    }
                }
                root = pop.right;
            }
        }
        return i;
    }

}

2.递归

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftValue = sumOfLeftLeaves(root.left);
        int rightValue = sumOfLeftLeaves(root.right);

        int midValue = 0;
        if(root.left != null && root.left.left == null && root.left.right == null) {
            midValue = root.left.val;
        }
        int sum  = midValue + leftValue + rightValue;
        return sum;
    }
}

六十二、513. 找树左下角的值

1.思路找到最大深度的几个数然后找到最左
(使用层序遍历找到最大深度)从右向左最后一个就是
BFS

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int j=0;
        while (queue.peek() != null) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                if (root.right != null) {
                    queue.add(root.right);
                }
                if (root.left != null) {
                    queue.add(root.left);
                }
                TreeNode poll = queue.poll();
                if (queue.isEmpty()) {
                    return poll.val;
                }
                root = queue.peek();
            }
        }
        return 0;
    }
}

2.BFS,从左向右(使用递归)
先找到每个最大深度的值,然后进行判断是否下一个深度更深,是就更新数据,

六十三、112. 路径总和

1.BFS

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        Queue<TreeNode> stack = new LinkedList<>();
        Queue<Integer> stack1 = new LinkedList<>();
        stack.add(root);
        stack1.add(root.val);
        while (!stack.isEmpty()) {
            root = stack.poll();
            Integer peek = stack1.poll();
            if (root.left != null) {
                stack.add(root.left);
                stack1.add(peek + root.left.val);
            }
            if (root.right != null) {
                stack.add(root.right);
                stack1.add(peek + root.right.val);
            }
            if (root.left == null && root.right == null) {
                if (peek == targetSum) {
                    return true;
                }
            }
        }
        return false;
    }
}

2.BFS (递归)

class Solution {//学会递归
    public boolean hasPathSum(TreeNode root, int targetSum) {
        return sum(root, targetSum);
    }
    boolean sum(TreeNode root,int targetSum){
        if (root == null) {
            return false;
        }
        int i = targetSum - root.val;
        if (i == 0&&root.right==null&&root.left==null) {
            return true;
        }
        //这里保证能有true返回,所以是或
        return sum(root.left, i) || sum(root.right, i);
    }
}

总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值